嚴(yán)格類型檢查的完整落地流程)
PyTorch 中的 Pyrefly 類型覆蓋率遷移從 SKILL 文檔看文件級(jí)嚴(yán)格類型檢查的完整落地流程【免費(fèi)下載鏈接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration項(xiàng)目地址: https://gitcode.com/GitHub_Trending/py/pytorch本篇圍繞 PyTorch 倉庫中的.claude/skills/pyrefly-type-coverage/SKILL.md展開系統(tǒng)講解將一個(gè) Python 文件遷移到 Pyrefly 嚴(yán)格類型檢查所有函數(shù)、類、屬性強(qiáng)制帶注解的七步標(biāo)準(zhǔn)流程移除文件級(jí)抑制、配置pyrefly.toml子配置、運(yùn)行檢查并按規(guī)則分類處置錯(cuò)誤、按注解階梯補(bǔ)全注解、迭代修復(fù)、Lint 與測(cè)試驗(yàn)證。讀完后你能夠在 PyTorch 項(xiàng)目中獨(dú)立完成一次類型覆蓋率遷移并理解其背后的倉庫級(jí)證據(jù)根目錄pyrefly.toml的真實(shí)子配置寫法、torch/fx/_compatibility.py的后向兼容裝飾器實(shí)現(xiàn)以及test/test_fx.py中基于 golden 文件的簽名鎖定測(cè)試。Pyrefly 在 PyTorch 中的定位全局寬松、局部收緊PyTorch 倉庫根目錄維護(hù)了一份 pyrefly.toml文件頭部注釋說明其配置Based on mypy.ini即從原 mypy 體系平滑遷移而來。它的頂層策略是全局相對(duì)寬松python-version 3.12untyped-def-behavior check-and-infer-return-any對(duì)無注解函數(shù)體仍做檢查但返回值推斷為Any全局關(guān)閉了一批歷史包袱類報(bào)錯(cuò)implicitly-defined-attribute、bad-param-name-override、implicit-import、deprecated均為false理由是大量屬性在__init__中定義很多覆寫方法會(huì)重命名參數(shù)mypy 也不強(qiáng)制顯式 importproject-includes/project-excludes精細(xì)圈定檢查范圍torch、caffe2、tools及若干test/*.py排除 notebook、vendored 代碼等。在此基礎(chǔ)上倉庫通過大量[[sub-config]]塊對(duì)特定目錄或單文件逐步收緊。當(dāng)前pyrefly.toml中已存在多個(gè)真實(shí)示例例如torch/_dynamo/**、torch/_dispatch/**、torch/_functorch/**開啟了implicit-any true而 torch/fx/** 與torch/optim/optimizer.py等條目已經(jīng)完整開啟[[sub-config]] matches torch/fx/** [sub-config.errors] implicit-import false implicit-any true bad-param-name-override false unannotated-return true unannotated-parameter true unannotated-attribute true這正是 SKILL 文檔所描述的遷移目標(biāo)形態(tài)一個(gè)文件的類型覆蓋提升本質(zhì)上就是在pyrefly.toml中新增一個(gè)子配置然后讓該文件通過檢查。以下流程全部繼承自 SKILL.md。前置條件遷移開始前必須確認(rèn)目標(biāo)文件位于一個(gè)擁有pyrefly.toml的項(xiàng)目中PyTorch 即滿足pyrefly、lintrunner與項(xiàng)目的測(cè)試運(yùn)行器都在PATH上。若其中任何一個(gè)缺失應(yīng)停下來詢問是否需要激活 conda 環(huán)境而不是自行安裝或用其他工具替代這一約束來自倉庫的 CLAUDE.md 約定。Step 1移除文件級(jí)類型檢查抑制先刪除目標(biāo)文件頭部的所有文件級(jí)抑制注釋。Pyrefly 出于 mypy 兼容會(huì)識(shí)別# mypy: ignore-errors所以這一行也必須一并刪除。SKILL 文檔明確列出了四種需要清理的寫法# pyre-ignore-all-errors # pyre-ignore-all-errors[16,21,53,56] # lint-ignore-every PYRELINT # mypy: ignore-errorsStep 2在 pyrefly.toml 中新增子配置條目為目標(biāo)文件所在的目錄或文件追加一個(gè)子配置SKILL 文檔給出的模板是[[sub-config]] matches path/to/directory/** [sub-config.errors] implicit-import false implicit-any true bad-param-name-override false unannotated-return true unannotated-parameter true其中implicit-import false與bad-param-name-override false是刻意鏡像全局配置的全局本來就關(guān)了這兩項(xiàng)目的是防止報(bào)錯(cuò)語義漂移真正新增的嚴(yán)格項(xiàng)是implicit-any、unannotated-return、unannotated-parameter三項(xiàng)——這就是本流程的三個(gè)目標(biāo)類目。關(guān)鍵注意事項(xiàng)子配置中設(shè)置任何一個(gè) error 鍵都只相對(duì)于父配置覆蓋該鍵本身但開啟unannotated-return/unannotated-parameter/implicit-any會(huì)把此前被文件級(jí)抑制注釋掩蓋的舊錯(cuò)誤一并復(fù)活。如果此時(shí)看到無關(guān)錯(cuò)誤例如bad-param-name-override刷屏正確做法是在子配置里把該鍵按父配置的取值鏡像一份以壓住噪聲而不是去改文件里的代碼。Step 3運(yùn)行 pyrefly 并按報(bào)告位置分類處置錯(cuò)誤pyrefly check FILENAME目標(biāo)是解決所有unannotated-return、unannotated-parameter、implicit-any錯(cuò)誤——方式只有補(bǔ)注解這三個(gè)目標(biāo)類目永遠(yuǎn)可以解決絕不允許用# pyrefly: ignore壓掉唯一例外是下文后向兼容豁免。其余類目bad-argument-type、missing-attribute等屬于真實(shí)類型缺陷處置原則是看 pyrefly 把錯(cuò)誤報(bào)告在哪個(gè)文件報(bào)告在別的文件路徑 ≠ 目標(biāo)文件不動(dòng)它不擴(kuò)大改動(dòng)范圍。若該錯(cuò)誤恰好阻塞了目標(biāo)文件的檢查就在報(bào)告發(fā)生地用# pyrefly: ignore[category] # TODO壓制報(bào)告在目標(biāo)文件、但報(bào)錯(cuò)信息指向別處定義的符號(hào)例如因某個(gè)導(dǎo)入函數(shù)注解有誤而報(bào)bad-return在本地用同樣的 TODO 注釋壓制不要偽造一個(gè)cast()去掩蓋上游缺口報(bào)告在目標(biāo)文件且錯(cuò)誤根源就在本地直接修復(fù)。# pyrefly: ignore[...]只能作為最后手段且只能用于非目標(biāo)類目。Step 4補(bǔ)全注解——約定與注解階梯當(dāng)函數(shù)體看不出正確類型時(shí)要回到調(diào)用點(diǎn)去確認(rèn)。SKILL 文檔給出了 PyTorch 項(xiàng)目?jī)?nèi)一整套注解約定基礎(chǔ)語法與導(dǎo)入約定使用 PEP 604 / PEP 585 語法int | None、list[str]假設(shè) Python ≥ 3.10抽象類型優(yōu)先用collections.abc而非typingCallable、Sequence、Generator等泛型輔助類型在項(xiàng)目最低 Python 版本可用時(shí)從typing導(dǎo)入只有需要更新特性時(shí)才用typing_extensions如支持 3.11/3.12 時(shí)的Self、override或 PEP 696 的TypeVar/ParamSpec的default。不要無腦從typing_extensions導(dǎo)入Callable永遠(yuǎn)要參數(shù)化禁止裸Callable。優(yōu)先Callable[..., object]只有當(dāng)調(diào)用方真的消費(fèi)了動(dòng)態(tài)返回值時(shí)才用Callable[..., Any]——如果結(jié)果只是被透?jìng)魃踔吝@個(gè) callable 根本沒被調(diào)用object更嚴(yán)格且同樣正確新建的模塊級(jí)全局名一律加前導(dǎo)下劃線TypeVar/ParamSpec與字符串參數(shù)一致_T TypeVar(_T)、_P ParamSpec(_P)、_R TypeVar(_R)、TypeAlias、輔助常量、哨兵值皆如此。這是 torch 對(duì)非公開名的主流約定據(jù) SKILL 文檔統(tǒng)計(jì)代碼樹中_P出現(xiàn)次數(shù)約為P的 6 倍。例外被其他模塊導(dǎo)入的名字、列入__all__的名字、或作為運(yùn)行時(shí) token 的名字如注解字符串派發(fā)標(biāo)記保持無下劃線。只約束你新增的名字不要順手重命名既有全局變量——那屬于本次技能范圍之外的無關(guān)重構(gòu)。一個(gè)現(xiàn)成的倉庫內(nèi)印證是 torch/fx/_compatibility.py其中_T TypeVar(_T)、_BACK_COMPAT_OBJECTS: dict[Any, None] {}均為下劃線前綴的非公開名且compatibility()返回Callable[[_T], _T]恰好是透?jìng)黝愋陀肨ypeVar而非Any的范例。謂函數(shù)與類型收窄布爾謂函數(shù)——is_*/has_*命名、接收寬類型常見object、返回bool——通常應(yīng)標(biāo)注TypeGuard[X]或TypeIs[X]后者還能收窄否定分支。TypeGuard自 3.10 起在typing中直接從typing導(dǎo)入TypeIs直到 3.13 才進(jìn)入typing因此為保持 3.10 兼容應(yīng)從typing_extensions≥4.10導(dǎo)入接收klass: type[_T]的issubclass風(fēng)格輔助函數(shù)應(yīng)返回TypeGuard[type[_T]]優(yōu)先用顯式的isinstance(x, type)守衛(wèi)而不是在issubclass()外包try/except TypeError——前者更清晰也能讓檢查器收窄。TypeVar何時(shí)用、何時(shí)不該用當(dāng)返回值派生自參數(shù)時(shí)——透?jìng)?恒等函數(shù)、返回這些參數(shù)之一的輔助函數(shù)、裝飾器、按類型做鍵的注冊(cè)表——應(yīng)使用TypeVar若簽名需透?jìng)鞯氖?callable 參數(shù)則用ParamSpec/TypeVar組合成Callable[_P, _R]而不是放寬到object/Any。輸出類型 某個(gè)輸入類型正是TypeVar所編碼的語義objectin /objectout 會(huì)把信息丟掉。注意反向情形如果函數(shù)變換了值、輸出類型與輸入不同比如把數(shù)組轉(zhuǎn)成 int單個(gè)TypeVar就是錯(cuò)的——應(yīng)直接命名真實(shí)的領(lǐng)域類型。其他結(jié)構(gòu)性約定在__init__中賦值的類屬性應(yīng)在類級(jí)別補(bǔ)注解讓 pyrefly 能看到用if TYPE_CHECKING:打破 import 環(huán)——僅注解用的導(dǎo)入放進(jìn)守衛(wèi)并配合from __future__ import annotations或字符串前向引用保持運(yùn)行時(shí)惰性導(dǎo)入from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from torch.fx import GraphModule def transform(gm: GraphModule) - GraphModule: ...放寬而不是放棄四級(jí)注解階梯當(dāng)正確類型難以推斷時(shí)按下面階梯逐級(jí)下探而不是直接 ignore從調(diào)用點(diǎn)與返回路徑可觀察到的最具體具體類型聯(lián)合類型X | Y、Sequence[X]式抽象類型或?qū)φ嬲盒秃瘮?shù)恒等透?jìng)?、容器輔助使用帶約束的TypeVarobject—— 仍能通過類型檢查的最嚴(yán)格兜底迫使調(diào)用方先收窄再使用例如def serialize(value: object) - str:。它外觀上與Any相似但更嚴(yán)格——不加isinstance時(shí) pyrefly 會(huì)拒絕value.foo()Any—— 最后一級(jí)。永遠(yuǎn)優(yōu)先于對(duì)目標(biāo)類目的# pyrefly: ignore但僅在第 1–3 級(jí)都失敗后才可用且你能說清楚每一級(jí)為何不適用例如聯(lián)合類型超過 8 種觀察不到公共上界調(diào)用方確實(shí)從不收窄。配套的兩條紀(jì)律特別警惕返回值位置的object/Any——函數(shù)通常比調(diào)用方更清楚自己產(chǎn)出了什么。寬返回只在真正的邊界處正確原樣返回輸入或值由 handler/調(diào)用方?jīng)Q定若函數(shù)體構(gòu)造了已知形狀就命名它領(lǐng)域別名或聯(lián)合優(yōu)于object判定某參數(shù)必須是Any之前至少讀三個(gè)調(diào)用點(diǎn)——不要憑第一眼看起來動(dòng)態(tài)就下結(jié)論。# pyrefly: ignore[...]的窄范圍用法非目標(biāo)類目保留給 pyrefly確實(shí)錯(cuò)了的具體局部錯(cuò)誤——?jiǎng)討B(tài)元編程、第三方 stub 缺口# pyrefly: ignore[attr-defined] result getattr(obj, dynamic_name)()若行內(nèi) ignore 注釋會(huì)讓該行超出行寬限制把它放在被標(biāo)記行的上一行pyrefly 支持上一行的 ignore而不是為了保留行內(nèi)注釋去加# fmt: skip——唯一的例外是后向兼容豁免那里注釋必須寫在def行上。后向兼容豁免唯一允許壓制目標(biāo)類目的場(chǎng)景關(guān)鍵規(guī)則被compatibility(is_backward_compatibleTrue)裝飾的函數(shù)簽名不得改動(dòng)。后向兼容測(cè)試test_function_back_compat會(huì)把inspect.signature的字符串化結(jié)果與 golden 文件比對(duì)——哪怕只加- None這樣的注解字符串都會(huì)變化測(cè)試即失敗。此時(shí)應(yīng)改用 pyrefly ignore 注釋compatibility(is_backward_compatibleTrue) def my_function( # pyrefly: ignore[unannotated-return] self, arg1, # cant add type here either ): ...# pyrefly: ignore注釋必須位于def行pyrefly 報(bào)錯(cuò)的位置而不是收尾的)上。這套機(jī)制在倉庫中有完整閉環(huán)。裝飾器定義在 torch/fx/_compatibility.pycompatibility(is_backward_compatibleTrue)會(huì)給函數(shù) docstring 追加Backwards-compatibility for this API is guaranteed說明并把對(duì)象注冊(cè)進(jìn)_BACK_COMPAT_OBJECTS。消費(fèi)端在 test/test_fx.py 的test_function_back_compat中它遍歷_BACK_COMPAT_OBJECTS用_fn_to_stable_annotation_str手工序列化簽名注釋說明這是因?yàn)閕nspect.Signature的序列化在不同 Python 版本間不穩(wěn)定且要避免把模塊路徑、函數(shù)內(nèi)存地址寫進(jìn) golden 文件與 golden 文件fx_backcompat_function_signatures比對(duì)不一致時(shí)錯(cuò)誤信息會(huì)明確提示如屬有意變更請(qǐng)與 FX 團(tuán)隊(duì)確認(rèn)棄用流程后--accept。ParamSpec保簽名包裝器裝飾器、functools.wraps風(fēng)格的輔助函數(shù)應(yīng)使用Callable[P, R]讓被包裝函數(shù)的簽名流向調(diào)用方——Callable[..., Any]會(huì)丟失這一信息只有當(dāng)包裝器真的接受任意 callable時(shí)才跳過 ParamSpec。包裝器在前/后追加參數(shù)時(shí)與Concatenate[X, P]搭配使用from collections.abc import Callable from typing import ParamSpec, TypeVar _P ParamSpec(_P) _R TypeVar(_R) def log_calls(fn: Callable[_P, _R]) - Callable[_P, _R]: def wrapper(*args: _P.args, **kwargs: _P.kwargs) - _R: return fn(*args, **kwargs) return wrapperStep 5迭代直至干凈重跑pyrefly check。新注解往往會(huì)暴露bad-return——即函數(shù)實(shí)際返回了不兼容類型逐一修復(fù)循環(huán)到零錯(cuò)誤。還有一個(gè)容易遺漏的收尾動(dòng)作收緊共享輔助函數(shù)加TypeGuard或精確返回類型后其調(diào)用方中既有的# pyrefly: ignore可能已經(jīng)失效。要回頭檢查并刪除這些僵尸抑制及其配套的解釋性注釋——不留死代碼。Step 6Lint交付前必做注解常常會(huì)改變 import 順序與行寬因此在交接前必須跑lintrunner -a files...lintrunner無法自動(dòng)修復(fù)的項(xiàng)要手工處理干凈。Step 7測(cè)試與優(yōu)先級(jí)規(guī)則失敗時(shí)的優(yōu)先級(jí)測(cè)試通過 pyrefly 干凈 注解嚴(yán)格度。如果新加的注解弄壞了測(cè)試先按階梯把注解降一級(jí)如具體類型 →object或撤銷破壞下游isinstance檢查的Any放寬再考慮回退整個(gè)文件。后向兼容檢查。僅當(dāng)目標(biāo)文件命中下述 grep 時(shí)才需要跑——compatibility(is_backward_compatibleTrue)裝飾器才是 golden 文件比對(duì)的真正前置條件import 了torch.fx這一更寬的啟發(fā)式會(huì)誤中torch/里約一半的文件不可作為依據(jù)grep -l compatibility(is_backward_compatibleTrue) target python -m pytest test/test_fx.py::TestFXAPIBackwardCompatibility -x -v修改模塊的單元測(cè)試。下結(jié)論沒有覆蓋之前兩個(gè)方向都要搜# torch/foo/bar.py 通常由 test/test_foo.py 或 test/test_bar.py 覆蓋 ls test/ | grep -i module-name # 或者按 import 關(guān)系找 grep -rl from torch.foo.bar import\|import torch.foo.bar test/兩者都為空時(shí)要明確告知用戶不要靜默跳過。類型變更可能引入真實(shí)的運(yùn)行時(shí)回歸例如.append被調(diào)用時(shí)Optional[X]vsX、Sequencevslist的差異。收尾注意事項(xiàng)類體中的前向引用即使沒有from __future__ import annotations某些位置仍需字符串引號(hào)class MyClass: def __new__(cls) - MyClass: ...提交紀(jì)律除非用戶明確要求不提交per repo CLAUDE.md。文件檢查干凈后停下來把 diff 呈現(xiàn)給用戶評(píng)審。小結(jié)這篇技能文檔把給一個(gè)文件上嚴(yán)格類型檢查壓縮成了一條可復(fù)現(xiàn)的流水線清抑制 → 加子配置 → 按錯(cuò)誤報(bào)告位置分類處置 → 沿注解階梯補(bǔ)全object優(yōu)于AnyTypeVar優(yōu)于放寬→ 迭代清理僵尸 ignore → lintrunner → 測(cè)試驗(yàn)證并用測(cè)試通過 檢查干凈 注解嚴(yán)格的優(yōu)先級(jí)保證遷移不引入行為回歸。它與 pyrefly.toml 中逐目錄收緊的子配置策略、torch/fx/_compatibility.py 的簽名鎖定機(jī)制、test/test_fx.py 的TestFXAPIBackwardCompatibility共同構(gòu)成了 PyTorch 類型覆蓋率逐步提升的完整工程閉環(huán)?!久赓M(fèi)下載鏈接】pytorchTensors and Dynamic neural networks in Python with strong GPU acceleration項(xiàng)目地址: https://gitcode.com/GitHub_Trending/py/pytorch創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考