風(fēng)格流水線解析)
Black 代碼格式化器完全指南從安裝配置到源碼級(jí)風(fēng)格流水線解析【免費(fèi)下載鏈接】blackThe uncompromising Python code formatter項(xiàng)目地址: https://gitcode.com/GitHub_Trending/bl/blackBlackThe Uncompromising Code Formatter不妥協(xié)的 Python 代碼格式化器是 PSF 出品的 Python 自動(dòng)格式化工具。本文以倉(cāng)庫(kù)根目錄的 README.md 為主線覆蓋其設(shè)計(jì)哲學(xué)、安裝與用法、風(fēng)格規(guī)則與務(wù)實(shí)例外、pyproject.toml配置方式并結(jié)合 src/black/init.py、src/black/mode.py 等源碼深入講解一次格式化背后的實(shí)際執(zhí)行流水線。讀完后你可以直接在生產(chǎn)項(xiàng)目中落地 Black并理解它的每個(gè)默認(rèn)值從何而來(lái)。一、Black 是什么一場(chǎng)關(guān)于格式控制權(quán)的交易R(shí)EADME 的開(kāi)篇就點(diǎn)明了 Black 的核心主張Any color you like.喜歡什么顏色都行。Black是不妥協(xié)的 Python 代碼格式化器。使用它意味著你放棄對(duì)手工格式細(xì)節(jié)minutiae的控制權(quán)作為交換Black 給你速度、確定性以及免于被pycodestyle反復(fù)嘮叨格式問(wèn)題的自由從而把時(shí)間和精力留給更重要的事情。它的三個(gè)關(guān)鍵特性直接寫在 README 中跨項(xiàng)目一致性被 Black 格式化過(guò)的代碼在任何項(xiàng)目里讀起來(lái)都是一樣的。格式在一段時(shí)間后變得透明你可以專注于代碼內(nèi)容本身更小的 diffBlack 以產(chǎn)生盡可能小的 diff 為目標(biāo)從而讓代碼審查code review更快確定性同樣的輸入永遠(yuǎn)得到同樣的輸出風(fēng)格爭(zhēng)議在團(tuán)隊(duì)內(nèi)被終結(jié)。源碼印證88 列與只認(rèn) .py的默認(rèn)值Black 的默認(rèn)行為在源碼中集中定義于 src/black/const.pyDEFAULT_LINE_LENGTH 88 DEFAULT_EXCLUDES r/(\.direnv|\.eggs|\.git|\.hg|\.ipynb_checkpoints|\.mypy_cache|\.nox|\.pytest_cache|\.ruff_cache|\.tox|\.svn|\.venv|\.vscode|__pypackages__|_build|buck-out|build|dist|venv)/ DEFAULT_INCLUDES r(\.pyi?|\.ipynb)$這解釋了兩件事為什么 Black 默認(rèn)行寬是 88 而不是 PEP 8 的 7988 是為了兼容 80 列寬編輯器下的 88 字符緩沖區(qū)以及為什么直接對(duì)目錄運(yùn)行black .時(shí).git、.venv、build、虛擬環(huán)境等目錄會(huì)被自動(dòng)跳過(guò)、只有.py/.pyi/.ipynb文件會(huì)被處理——這些正是 README 強(qiáng)調(diào)的sensible defaults合理默認(rèn)值的源碼出處。二、安裝與基本用法2.1 安裝Black 運(yùn)行需要Python 3.10見(jiàn) pyproject.toml 中的requires-python 3.10。安裝方式如下pip install black如果需要格式化 Jupyter Notebook需安裝 jupyter 擴(kuò)展依賴對(duì)應(yīng) pyproject.toml 中[project.optional-dependencies]的jupyter [ipython7.8.0, tokenize-rt3.2.0]pip install black[jupyter]此外如果不想安裝 Python 環(huán)境也可以從最新的 GitHub release 下載 PyInstaller 打包的獨(dú)立可執(zhí)行文件README 中給出倉(cāng)庫(kù)的 pyproject.toml 中[tool.cibuildwheel]段落即為這些跨平臺(tái)二進(jìn)制文件的構(gòu)建配置覆蓋 CPython 3.10 的 Linux/Windows/macOS 64 位平臺(tái)。2.2 三種調(diào)用方式方式一直接運(yùn)行腳本最快black {source_file_or_directory}方式二作為 Python 包運(yùn)行腳本不可用時(shí)python -m black {source_file_or_directory}方式三格式化代碼字符串而不觸碰文件$ black --code print ( hello, world ) print(hello, world)命令行參數(shù)入口由 pyproject.toml 中的black black:patched_main[project.scripts]注冊(cè)主入口patched_main與main均定義在 src/black/init.py。2.3 Black 是守規(guī)矩的 Unix 工具配合倉(cāng)庫(kù)文檔 docs/usage_and_configuration/the_basics.mdREADME 中直接跑就得到合理結(jié)果的承諾背后有一套明確約定找不到任何可格式化源碼時(shí)什么都不做文件名用-表示從標(biāo)準(zhǔn)輸入讀、寫標(biāo)準(zhǔn)輸出所有面向用戶的信息只輸出到stderr退出碼為 0除非發(fā)生內(nèi)部錯(cuò)誤或某個(gè) CLI 選項(xiàng)要求非零退出這對(duì) CI 集成很重要--check模式會(huì)因存在未格式化代碼而返回非零。2.4 安全網(wǎng)AST 校驗(yàn)與--fastREADME 特別提到一個(gè)安全機(jī)制作為會(huì)拖慢處理的安全措施Black會(huì)檢查重新格式化后的代碼仍然能產(chǎn)生與原始代碼在語(yǔ)義上等效的 AST詳見(jiàn)文檔中 Pragmatism 一節(jié)的 AST Before and After Formatting 部分。如果你對(duì)自己的代碼有信心、想換取速度可以使用black --fast {source}從源碼結(jié)構(gòu)看這個(gè)校驗(yàn)發(fā)生在format_file_contents/format_file_in_placesrc/black/init.py 起中格式化前對(duì)源碼做一次ast.dump格式化后再做一次并比較不一致即報(bào)錯(cuò)并保留原文件——這也是倉(cāng)庫(kù) CHANGES.md 中大量fix unparseable output / failed Blacks own AST safety check條目存在的原因Black 把輸出必須可解析且語(yǔ)義等價(jià)當(dāng)作硬性驗(yàn)收標(biāo)準(zhǔn)。三、Black 代碼風(fēng)格受限的配置 有限度量的務(wù)實(shí)3.1 風(fēng)格總則README 對(duì)風(fēng)格的表述可以概括為四條PEP 8 兼容Black 是 PEP 8 兼容的、有主見(jiàn)的opinionated格式化器整文件就地重寫_Black_ reformats entire files in place配置項(xiàng)刻意受限風(fēng)格配置選項(xiàng)被刻意限制、極少新增不參考原有格式它基本不考慮你之前的排版少數(shù)例外見(jiàn)務(wù)實(shí)一節(jié)最典型的是 magic trailing comma。這套一行一個(gè)表達(dá)式、超出行寬就沿括號(hào)逐層展開(kāi)的具體排版規(guī)則在倉(cāng)庫(kù)文檔 docs/the_black_code_style/current_style.md 中有完整示例例如短表達(dá)式會(huì)被合并回一行# in: j [1, 2, 3] # out: j [1, 2, 3]而超長(zhǎng)的函數(shù)簽名會(huì)被逐參數(shù)展開(kāi)閉括號(hào)回退縮進(jìn)且補(bǔ)上尾隨逗號(hào)def very_important_function( template: str, *variables, file: os.PathLike, engine: str, header: bool True, debug: bool False, ): ...3.2 穩(wěn)定性策略與務(wù)實(shí)PragmatismREADME 明確風(fēng)格變更受Stability Policy約束——Black 已趨于穩(wěn)定不應(yīng)預(yù)期未來(lái)出現(xiàn)大規(guī)模格式變化風(fēng)格變更主要是對(duì) bug 報(bào)告的響應(yīng)和新 Python 語(yǔ)法的適配。它同時(shí)警告提交 issue 之前請(qǐng)先閱讀 Current style 與 Future style 兩份文檔看似 bug 的行為可能是有意設(shè)計(jì)。Pragmatism務(wù)實(shí)一節(jié)說(shuō)明Black 早期版本在某些方面是絕對(duì)主義的追隨其最初作者的風(fēng)格偏好這在用戶很少時(shí)讓實(shí)現(xiàn)更簡(jiǎn)單作為成熟工具Black 現(xiàn)在會(huì)對(duì)其一般規(guī)則做有限的例外處理。這些例外在文檔中有專門章節(jié)The Black code style: Pragmatism閱讀它同樣應(yīng)在提 issue 之前進(jìn)行。3.3 源碼印證一次格式化到底發(fā)生了什么理解整文件重寫 確定性最直觀的方式是看核心流水線。入口函數(shù)format_str定義在 src/black/init.py其文檔字符串本身就給出了標(biāo)準(zhǔn)用法import black print(black.format_str(def f(arg:str)-None:..., modeblack.Mode())) # 輸出: # def f(arg: str ) - None: # ...format_str內(nèi)部委托給_format_str_oncesrc/black/init.py完整調(diào)用鏈為decode_bytes用tokenize.detect_encoding檢測(cè)文件頭聲明的編碼識(shí)別 LF/CRLF/CR 換行并在輸出時(shí)還原避免 Windows 換行被無(wú)謂改寫lib2to3_parse用倉(cāng)庫(kù)內(nèi)置的 src/blib2to3lib2to3 的分叉構(gòu)建語(yǔ)法樹(shù)這是 Black 不依賴 CPython 解析器版本、從而能解析未來(lái)語(yǔ)法的關(guān)鍵目標(biāo)版本檢測(cè)若用戶未通過(guò)-t指定則調(diào)用detect_target_versions依據(jù)from __future__導(dǎo)入與 src/black/init.py 中g(shù)et_features_used識(shí)別到的語(yǔ)言特性f-string、下劃線數(shù)字字面量、海象運(yùn)算符、match 語(yǔ)句、except*、可變參數(shù)泛型、懶導(dǎo)入等見(jiàn)Feature枚舉與VERSION_TO_FEATURES映射定義在 src/black/mode.py推斷語(yǔ)法兼容的版本集LineGenerator遍歷語(yǔ)法樹(shù)生成候選邏輯行src/black/linegen.pyEmptyLineTracker維護(hù)函數(shù)/類之間的空行規(guī)則transform_line按Mode中的line_length對(duì)每行執(zhí)行括號(hào)爆炸bracket splitting等變換強(qiáng)制第二遍format_str中有一段注釋直白的Admittedly ugly邏輯——如果第一遍產(chǎn)生了變化就用第一遍的輸出再格式化一次。原因是可選尾隨逗號(hào)在第二遍會(huì)變成強(qiáng)制尾隨逗號(hào)進(jìn)而與可選括號(hào)產(chǎn)生交互必須跑兩遍才能收斂。這段源碼是理解Black 輸出是確定的固定點(diǎn)的最直接證據(jù)。Mode數(shù)據(jù)類src/black/mode.py是全部風(fēng)格參數(shù)的載體dataclass class Mode: line_length: int DEFAULT_LINE_LENGTH # 默認(rèn) 88 string_normalization: bool True # 默認(rèn)統(tǒng)一雙引號(hào) ... preview: bool False # 預(yù)覽風(fēng)格開(kāi)關(guān)這解釋了 README風(fēng)格配置選項(xiàng)刻意受限的由來(lái)——用戶可調(diào)的旋鈕主要就是line_length、string_normalization對(duì)應(yīng) CLI 的-l、-S與少量開(kāi)關(guān)而非一份任意風(fēng)格表。四、配置pyproject.toml是最主要的面板4.1 README 的原文結(jié)論Black可以從pyproject.toml讀取命令行選項(xiàng)的項(xiàng)目級(jí)默認(rèn)值這在為項(xiàng)目指定自定義的--include和--exclude/--force-exclude/--extend-exclude模式時(shí)特別有用詳見(jiàn) The basics: Configuration via a file 與 Usage and Configuration。README 還給出了官方 Pro-tip如果你在想我到底需不需要配置什么——答案是不需要。Black 的全部?jī)r(jià)值就在于合理默認(rèn)值應(yīng)用這些默認(rèn)值你的代碼就能與眾多其他 Black 項(xiàng)目保持一致。4.2 一份可復(fù)制的真實(shí)配置Black 項(xiàng)目自用配置本倉(cāng)庫(kù)的 pyproject.toml 就是一份被 Black 官方注釋過(guò)的配置范例可以直接作為模板# NOTE: you have to use single-quoted strings in TOML for regular # expressions. Its the equivalent of r-strings in Python. # Multiline strings are treated as verbose regular expressions by Black. # Use [ ] to denote a significant space character. [tool.black] line-length 88 target-version [py310] include \.pyi?$ extend-exclude /( # The following are specific to Black, you probably dont want those. tests/data/ | profiling/ ) # We use the unstable style for formatting Black itself. If you # want bug-free formatting, you should keep this off. unstable true幾個(gè)要點(diǎn)TOML 正則有講究正則必須用單引號(hào)字符串等價(jià) Python 的 raw string多行字符串按verbose 正則解析[ ]表示顯著空格——這正是extend-exclude里那段/( ... | ... )能寫多行的原因include/extend-exclude分別對(duì)應(yīng) CLI 的同名選項(xiàng)用于覆蓋 src/black/const.py 中的DEFAULT_INCLUDES/DEFAULT_EXCLUDESforce-exclude則連顯式傳入的路徑也跳過(guò)適合排除第三方生成的目錄target-version-t選項(xiàng)的文件版如target-version [py311, py312, py313]。它決定 Black 用什么語(yǔ)法解析代碼、以及風(fēng)格細(xì)節(jié)——例如只有當(dāng)所有目標(biāo)版本 ≥ py35 時(shí)Black 才會(huì)在f(a, *args)的*args后加尾隨逗號(hào)docs/usage_and_configuration/the_basics.md 中給出了 py34/py35 的對(duì)比示例unstable true僅 Black 項(xiàng)目自身使用不穩(wěn)定的預(yù)覽風(fēng)格普通項(xiàng)目若追求無(wú) bug 的穩(wěn)定格式應(yīng)保持該標(biāo)志關(guān)閉。對(duì)應(yīng)源碼中Mode的 preview 語(yǔ)義unstable 模式啟用全部預(yù)覽特性見(jiàn) src/black/mode.py 中__contains__的實(shí)現(xiàn)注釋。4.3 其他常用開(kāi)關(guān)速查與 README 承諾的有限旋鈕一致以下選項(xiàng)在 docs/usage_and_configuration/the_basics.md 中有完整說(shuō)明均可同時(shí)以 CLI 或pyproject.toml形式配置選項(xiàng)作用-h, --help顯示全部命令行選項(xiàng)-c, --code格式化傳入的代碼字符串-l, --line-length行寬默認(rèn) 88-t, --target-version目標(biāo) Python 版本可多次給出--pyi/--ipynb強(qiáng)制按 stub / Notebook 處理輸入管道輸入場(chǎng)景-x, --skip-source-first-line跳過(guò)源碼第一行-S, --skip-string-normalization保留字符串原樣默認(rèn)統(tǒng)一為雙引號(hào)并規(guī)范化前綴-C, --skip-magic-trailing-comma忽略魔法尾隨逗號(hào)默認(rèn)會(huì)把你已有的尾隨逗號(hào)當(dāng)作請(qǐng)保持逐行展開(kāi)的信號(hào)--preview啟用下一大版本可能并入主功能、但可能有破壞性的風(fēng)格變更--fast關(guān)閉 AST 前后比對(duì)安全校驗(yàn)換取速度--line-ranges只格式化指定行范圍配合lines參數(shù)走 src/black/init.py 的sanitized_lines/adjusted_lines路徑五、跳過(guò)格式化的三種注釋指令雖然 README 正文未展開(kāi)但作為以指定文檔為主體、文檔生態(tài)為輔佐的一環(huán)docs/usage_and_configuration/the_basics.md 定義了與放棄格式控制權(quán)直接對(duì)沖的逃生艙屬于 README 承諾的基本用法范疇# fmt: skip跳過(guò)該行可與其他 pragma 混排# fmt: skip # pylint # noqa或分號(hào)列表形式# fmt: off/# fmt: on關(guān)閉/開(kāi)啟區(qū)間格式化兩者必須處于同一縮進(jìn)層級(jí)、同一代碼塊內(nèi)兼容 YAPF 的# yapf: disable/enable塊注釋。六、社區(qū)采用與口碑README 的 Used by 一節(jié)列出了信任 Black 的知名開(kāi)源項(xiàng)目pytest、tox、Pyramid、Django、Django Channels、Hypothesis、attrs、SQLAlchemy、Poetry、PyPA 系列應(yīng)用Warehouse、Bandersnatch、Pipenv、virtualenv、pandas、Pillow、Twisted、LocalStack、Datadog Agent 全部集成、Home Assistant、Zulip、Kedro、OpenOA、FLORIS、ORBIT、WOMBAT 等以及使用它的組織Dropbox、KeepTruckin、Lyft、Mozilla、Quora、Duolingo、QuantumBlack、Tesla、Archer Aviation。README 同時(shí)收錄了幾位知名開(kāi)發(fā)者的評(píng)價(jià)Testimonials其中 SQLAlchemy 作者 Mike Bayer 稱其為整個(gè)編程生涯中帶來(lái)的生產(chǎn)力提升最大的單一工具重構(gòu)時(shí)的擊鍵量降到原來(lái)的約 1%attrs 作者、Twisted 核心開(kāi)發(fā)者 Hynek Schlawack 寫道一個(gè)不爛的自動(dòng)格式化器就是我全部的圣誕愿望requests 作者 Kenneth Reitz 則說(shuō)它大幅改善了我們代碼的格式化。測(cè)試與 CI 基礎(chǔ)設(shè)施README 還提到 Black 擁有全面的測(cè)試套件、高效的并行測(cè)試以及自研的并行 CI 運(yùn)行器倉(cāng)庫(kù)中 tests/test_black.py、tests/data/cases/ 下數(shù)百個(gè)輸入/輸出成對(duì)的用例文件如 tests/data/cases/comments.py、tests/data/cases/torture.py就是穩(wěn)定性承諾的具體載體——每個(gè)歷史 bug 修復(fù)都沉淀為一個(gè)回歸用例。七、展示你的風(fēng)格README 徽章在自己的項(xiàng)目 README 中聲明使用了 Black是 README 給出的官方做法。Markdown 形式[](https://github.com/psf/black)RST 形式用于 README.rst.. image:: https://img.shields.io/badge/code%20style-black-000000.svg :target: https://github.com/psf/black八、許可、貢獻(xiàn)與周邊文檔LicenseMIT見(jiàn) LICENSEChange log更新日志較長(zhǎng)獨(dú)立存放于 CHANGES.md當(dāng)前共 2000 余行按 Stable style / Preview style 等分類記錄每次風(fēng)格與行為變更Authors作者列表同樣獨(dú)立存放見(jiàn) AUTHORS.mdContributing貢獻(xiàn)入門見(jiàn) docs/contributing/the_basics.md貢獻(xiàn)流程見(jiàn) docs/contributing/index.mdCode of Conduct遵循 Python 社區(qū)行為準(zhǔn)則README 結(jié)尾還按項(xiàng)目幽默傳統(tǒng)補(bǔ)了一句如果實(shí)在需要打某人請(qǐng)邊跳舞邊用魚(yú)打。九、總結(jié)為什么這套設(shè)計(jì)值得借鑒從 README.md 到源碼Black 的工程決策可以濃縮為三點(diǎn)默認(rèn)值即產(chǎn)品88 列行寬src/black/const.py、自動(dòng)排除虛擬環(huán)境與構(gòu)建目錄、按語(yǔ)法特性自動(dòng)探測(cè)目標(biāo)版本src/black/mode.py 的VERSION_TO_FEATURES讓零配置成為真實(shí)可用的狀態(tài)而非營(yíng)銷話術(shù)確定性與安全性雙保險(xiǎn)兩遍格式化收斂src/black/init.py保證固定點(diǎn)輸出AST 前后比對(duì)保證輸出可解析、語(yǔ)義等價(jià)--fast留給愿意自己承擔(dān)風(fēng)險(xiǎn)的場(chǎng)景變更治理穩(wěn)定性策略 按 Stable/Preview 雙通道發(fā)布風(fēng)格變更CHANGES.md配合# fmt: skip/off逃生艙把工具替你格式化與必要時(shí)你說(shuō)了算的邊界劃得清清楚楚?!久赓M(fèi)下載鏈接】blackThe uncompromising Python code formatter項(xiàng)目地址: https://gitcode.com/GitHub_Trending/bl/black創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考