
blackd 服务器协议与使用指南让 Black 代码格式化通过 HTTP 常驻服务提供【免费下载链接】blackThe uncompromising Python code formatter项目地址: https://gitcode.com/GitHub_Trending/bl/blackblackd 是 Black 项目内置的一个轻量级 HTTP 服务器它以简单的 POST 协议对外暴露 Black 的格式化能力。它的核心价值在于免去“每格式化一个文件就冷启动一次新的 Black 进程”的开销非常适合编辑器插件、CI 工具、批量格式化脚本等高频调用场景。读完本文你将掌握 blackd 的安装、启动、命令行参数、完整 HTTP 协议请求头、状态码、响应头以及官方 Python 客户端的用法并能结合源码理解其并发与安全模型。blackd 是什么为什么要让 Black 常驻服务Black 本体是一个命令行格式化工具每次执行都会经历 Python 解释器启动、模块加载、语法解析与格式化的完整过程。当调用频率很高例如保存文件时触发、批量处理大量文件时进程启动开销不可忽略。blackd 的设计动机正是文档中明确指出的避免每次想要 blacken 一个文件时都付出启动一个新 Black 进程的成本见 black_as_a_server.md。blackd 是一个小型的 HTTP 服务器把 Black 的功能包装成发一段代码进去、收一段格式化后的代码出来的简单协议。它基于aiohttp构建由 src/blackd/init.py 实现核心逻辑真正的可执行入口只是简单的一行调用见 src/blackd/main.py。安全边界仅限本机使用官方文档对 blackd 给出了明确的红线警告blackd不应作为公网可访问的服务器运行因为没有任何防止滥用abuse的安全措施。它只用于本地使用。因此在任何部署场景下都应当遵守这一限制将其绑定在回环地址默认localhost上而不是暴露到公网。安装通过 [d] extra 获取额外依赖blackd 默认不随 Black 一起打包因为它引入了额外的依赖aiohttp。安装方式与普通 Black 不同需要使用带 extra 的安装命令pip install black[d]在源码层面pyproject.toml中对应声明如下见 pyproject.tomld [aiohttp3.10]也就是说这个 extra 至少要求aiohttp3.10。如果未安装该依赖直接导入 blackdsrc/blackd/init.py 会抛出明确的ImportError并提示请使用pip install black[d]重新安装以获取 aiohttp_cors。测试代码同样要求这一前置条件见 tests/test_blackd.py运行tests/test_blackd.py前也必须安装dextra。blackd 的命令行入口由项目脚本表注册见 pyproject.toml[project.scripts] black black:patched_main blackd blackd:patched_main [d]启动 blackd命令行选项一览直接运行blackd即可在默认端口启动服务并默认只绑定本地接口blackd启动成功后会打印一行信息包含服务器版本号以及监听的 host 和 port此后它会像大多数 Web 服务器一样在标准输出打印访问日志同时也会输出因非法格式化请求产生的异常堆栈。由于 blackd 面向的服务化场景非常聚焦它提供的命令行选项比 Black 本身还要少。官方文档要求读者自行运行blackd --help查看结合源码实现见 src/blackd/init.py其支持的选项可以归纳如下选项类型 / 默认值说明--bind-host字符串默认localhost服务器绑定的地址。默认仅本地可访问符合仅限本地使用的安全定位--bind-port整数默认45484监听端口--cors-allow-origin可多次传递允许通过 CORS 访问 blackd 的来源Origin用于浏览器端客户端场景--max-body-size整数click.IntRange(min1)默认 5 MiB5 * 1024 * 1024请求体最大字节数-h/--help—帮助blackd使用了help_option_names显式同时支持-h与--help--version—通过click.version_option输出 Black 版本号从源码常量可看到默认上限的具体定义DEFAULT_MAX_BODY_SIZE 5 * 1024 * 1024见 src/blackd/init.py与文档中请求体默认限制为 5 MiB一致DEFAULT_WORKERS os.cpu_count() or 1见同文件 L60则决定了进程池的并发规模。验证 blackdcurl 与官方 Python 客户端用 curl 快速验证官方文档给出了最直接的验证方式——先启动服务器再用 curl 发起 POST 请求blackd --bind-port 9090 # 或让 blackd 自动选择端口 curl -s -XPOST localhost:9090 -d print(valid)请求体是要格式化的 Python 源码文本。若输入需要格式化curl 会直接打印出格式化后的代码例如单引号被规范化为双引号并补上换行若输入本就符合规范则返回 204 且响应体为空。用官方 Python 客户端BlackDClientblackd 随包提供了异步客户端blackd.client.BlackDClient。官方文档示例使用asyncio与之配合import asyncio from blackd.client import BlackDClient async def main(): client BlackDClient(urlhttp://127.0.0.1:9090) unformatted_code def hello(): print(Hello, World!) formatted_code await client.format_code(unformatted_code) print(formatted_code) if __name__ __main__: asyncio.run(main())该客户端实现于 src/blackd/client.py。从构造签名可以看出它把上面介绍的请求头都封装成了构造参数见同文件 L10-L21对应关系为BlackDClient 参数生成的请求头对应 Black CLI 选项line_lengthX-Line-Length--line-lengthskip_source_first_lineX-Skip-Source-First-Line--skip-source-first-lineskip_string_normalizationX-Skip-String-Normalization--skip-string-normalizationskip_magic_trailing_commaX-Skip-Magic-Trailing-Comma--skip-magic-trailing-commapreviewX-Preview--previewfastX-Fast-Or-Safe: fast--fastpython_variantX-Python-Variant--pyi为pyi时或--target-versiondiffX-Diff--diffheaders合并进请求头的附加字典—客户端还做了状态码语义封装见 src/blackd/client.py204 表示输入本就合规、原样返回200 返回格式化结果400 抛black.InvalidInput500 抛RuntimeError其余状态码抛Unexpected response status code。浏览器端访问与 CORS默认情况下来自浏览器的跨域cross-origin请求会被拒绝。如果确实需要从浏览器客户端访问 blackd需要通过--cors-allow-origin传入一个或多个允许的来源Origin。其底层 CORS 中间件实现于 src/blackd/middlewares.py只有当请求携带Origin头时才进行校验若 Origin 不在白名单内直接返回403CORS origin is not allowed对合法的预检preflightOPTIONS请求会返回Access-Control-Allow-Origin、Access-Control-Expose-Headers暴露X-Black-Version、Access-Control-Allow-Headers与Access-Control-Allow-MethodsOPTIONS, POST。在 tests/test_blackd.py 中可以看到未白名单的 Origin 预检请求被 403 拒绝的测试用例。协议细节请求、请求头与响应码请求格式blackd 只接受发送到/路径的POST请求协议要点如下请求体为待格式化的 Python 源码文本。编码由请求头Content-Type中的charset字段决定若未指定blackd 假定为UTF-8源码中默认取utf8见 src/blackd/init.py。请求体默认上限 5 MiB可通过--max-body-size调整该限制在构建aiohttp.web.Application时通过client_max_size传入见 src/blackd/init.py。X-Protocol-Version协议的版本约定绝大多数请求头都对应 Black 的某个命令行参数唯一例外是X-Protocol-Version。若该头存在其值必须是1否则请求会被以HTTP 501Not Implemented拒绝。源码中的校验逻辑见 src/blackd/init.py会返回文本This server only supports protocol version 1对应的测试见 tests/test_blackd.py。格式化控制请求头与 Black CLI 选项一一对应下表完整列出官方文档中所有控制格式化行为的请求头及其语义请求头对应 CLI 标志取值语义X-Line-Length--line-length行宽默认black.DEFAULT_LINE_LENGTH见 src/blackd/init.py非法整数值触发 400X-Skip-Source-First-Line--skip-source-first-line存在且值非空字符串时忽略源码第一行用于保留 shebang 等首行头X-Skip-String-Normalization--skip-string-normalization存在且值非空字符串时不做字符串归一化如引号统一X-Skip-Magic-Trailing-Comma--skip-magic-trailing-comma存在且值非空字符串时不以尾随逗号作为触发拆行的理由X-Preview--preview存在且值非空字符串时启用实验性、可能破坏既有风格的新变化X-Unstable--unstable存在且值非空字符串时启用已知可能存在缺陷的实验性风格变化X-Enable-Unstable-Feature--enable-unstable-feature值为逗号分隔的功能名列表如X-Enable-Unstable-Feature: feature1, feature2每个名称会被按black.Preview枚举解析非法名称触发 400 并指明问题片段X-Fast-Or-Safe--fast值设为fast时等价于 Black 的--fast跳过安全校验以提速X-Python-Variant--pyi/--target-version值为pyi时按 stub 文件.pyi处理否则须为 Python 版本或其逗号组合可带py前缀。例如兼容 Python 3.5 与 3.6 时设py3.5,py3.6X-Diff--diff存在时输出格式化前后差异关键细节X-Skip-*与X-Preview/X-Unstable系列是存在且非空即生效的开关源码中用bool(headers.get(..., False))解析见 src/blackd/init.py所以客户端传yes、true等任意非空值均可官方客户端即用yes见 src/blackd/client.py。而X-Line-Length、X-Python-Variant、X-Enable-Unstable-Feature则是需要按语法解析的取值头。X-Python-Variant的取值相当灵活既可写3.6、py3.6也可写py36、36甚至3.6.4只取主次版本并支持逗号组合如3.6,py3.72.x 一律不受支持。解析逻辑见parse_python_variant_headersrc/blackd/init.py以上合法/非法形态在 tests/test_blackd.py 中均有覆盖。如果任意请求头取到非法值blackd 会返回HTTP 400且响应体消息中会提到出问题的请求头名称。响应状态码除请求头非法产生的 400 外blackd 可能产生如下响应码状态码含义HTTP 204输入已经格式良好响应体为空对应black.NothingChanged异常分支见 src/blackd/init.pyHTTP 200输入需要格式化响应体为格式化后的 Python 代码Content-Type相应设置HTTP 400输入包含语法错误错误详情在响应体中对应black.InvalidInput与black.SourceASTParseError见同文件 L207-L210HTTP 500格式化过程中的其他任意错误响应体包含错误的文本表示HTTP 501X-Protocol-Version不是1HTTP 403CORS 中间件拒绝未白名单的 Origin见 src/blackd/middlewares.py响应头所有响应都会附带一个X-Black-Version头内容是 Black 的版本号见 src/blackd/init.py。该头也通过 CORS 的Access-Control-Expose-Headers暴露给浏览器端见 src/blackd/init.py并已有专门的测试断言其存在见 tests/test_blackd.py。从源码看 blackd 的工作原理结合源码可以还原 blackd 请求处理的完整链路这有助于使用者预估它的行为与性能特征应用装配make_app()构建aiohttp.web.Application注册唯一的POST /路由路由处理函数handle通过functools.partial绑定共享的进程池执行器与信号量见 src/blackd/init.py。并发模型executor()是一个ProcessPoolExecutor工作进程数默认取os.cpu_count()同时以asyncio.BoundedSemaphore(DEFAULT_WORKERS)限制并发格式化任务数防止任务无限堆积见同文件 L121-L123、L141-L148。事件循环会优先尝试通过maybe_use_uvloop使用 uvloop见同文件 L106进一步提高 IO 性能。请求解析handle()校验协议版本 → 调用parse_mode()把请求头解析成一个black.FileMode见 src/blackd/init.py→ 读取请求体并按 charset 解码。首行剥离与还原当X-Skip-Source-First-Line生效时源码第一行含换行会被暂存并从请求体中摘除格式化完成后再原样拼回响应见同文件 L178-L197因此首行如 shebang 或编码声明不会被改动。核心格式化format_code()通过信号量限制并发后用loop.run_in_executor把格式化任务投递给进程池实际执行的是black.format_file_contents(req_str, fastfast, modemode)——这正是 Black 本体的格式化核心函数。若启用了X-Diff还会在同一执行器中调用black.diff计算差异并以In\t{时间戳}与Out\t{时间戳}作为 diff 的文件名标注见同文件 L218-L243。值得特别注意的是blackd 不会读取pyproject.toml配置。这一限制在 the_basics.md 中明确写明因为所有格式化选项都经由请求头传递。因此如果想为某个请求定制行宽、目标版本等参数必须通过请求头显式指定而不是依赖项目中的[tool.black]配置。从测试看协议的语义边界test_blackd.py 是理解 blackd 行为最直接的可运行规范几个代表性用例可以帮你校准预期test_blackd_request_needs_formattingprint(hello world)无换行会返回 200且响应精确为print(hello world)\n——单引号归一化为双引号并补上换行见 tests/test_blackd.py。test_blackd_request_no_change已合规的输入返回 204 且响应体为空见同文件 L49-L52。test_blackd_request_syntax_error无法解析的源码返回 400错误文本以cannot parse开头见同文件 L54-L61。test_blackd_skip_first_source_line带首行头的输入默认解析失败400开启X-Skip-Source-First-Line后返回 200 且首行原样保留在结果开头见同文件 L167-L178。test_blackd_invalid_line_lengthX-Line-Length: NaN会触发 400见同文件 L159-L165对应parse_mode中int()抛ValueError后转HeaderError的分支。test_blackd_diff开启X-Diff后返回统一 diff 文本文件名由时间戳构成见同文件 L105-L120。典型使用建议将 blackd 用于编辑器插件或后台格式化服务时可遵循以下实践服务端用blackd --bind-host 127.0.0.1 --bind-port 45484或让系统自动选端口启动仅绑定回环地址切勿暴露到公网。客户端优先复用官方BlackDClient它已内置对 204/200/400/500 等状态码的语义处理能直接抛出black.InvalidInput等更友好的异常。需要浏览器端访问时为--cors-allow-origin显式列出可信来源默认的跨域拒绝策略不应被关闭。记住配置全靠请求头的规则任何格式化偏好行宽、目标版本、是否跳过字符串归一化、是否启用 preview/unstable 功能等都要在每次请求中以X-*头传递。对于超大输入接近 5 MiB 上限通过--max-body-size按需调高同时留意它与后端基于 CPU 核数的进程池并发规模之间的配合。blackd 以常驻一个进程、用 HTTP 交换格式化任务的方式让 Black 的格式化能力得以嵌入各种高频调用场景。理解它的协议与安全边界是在项目中稳定落地 blackd 的前提。【免费下载链接】blackThe uncompromising Python code formatter项目地址: https://gitcode.com/GitHub_Trending/bl/black创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考