Black 格式化含中文变量的 Notebook 魔法语句失败,按 UTF-8 字节截前缀
Black 用 AST 字节位置切字符前缀时会损坏含中文变量的 Notebook 魔法语句,应采用偏移修复并检查原单元格内容。
Notebook 单元格里的 变量 = %env FOO 在 IPython 中有明确含义,Black 格式化时却可能生成 变量 = get_b"..." 一类残缺文本,再报无法解析。把变量名换成 ASCII 后恢复正常,是定位魔法语句占位处理的线索,不表示中文变量名本身不合法。
Black 先把魔法语句转换为 get_ipython() 调用,再暂时替换成可格式化的占位符。这里从 Python AST 得到的 col_offset 按 UTF-8 字节计数,旧代码却直接用它切 Python 字符串。两个中文字占六个字节,字节位置 9 对应的字符位置只有 5,截取时就会多留下 get_ 等转换代码。
Black 26.10.0 包含修复:先对行按 UTF-8 编码,按字节位置截取前缀,再解码回字符串。使用 Notebook 支持所需的 Jupyter extra,并核对编辑器或 pre-commit 实际调用的版本。
python -m pip install "black[jupyter]==26.10.0"
python -m black --version
可在独立目录构造一个最小 Notebook,避免先改真实笔记本中的变量名:
import json
from pathlib import Path
notebook = {
"nbformat": 4, "nbformat_minor": 5,
"metadata": {"language_info": {"name": "python"}},
"cells": [{
"cell_type": "code", "id": "unicode-magic", "metadata": {},
"execution_count": None, "outputs": [],
"source": ["变量 = %env FOO\n"],
}],
}
Path("unicode-magic-check.ipynb").write_text(
json.dumps(notebook, ensure_ascii=False), encoding="utf-8"
)
python -m black unicode-magic-check.ipynb
python -m black --check unicode-magic-check.ipynb
格式化命令应成功,第二次检查应不再要求修改。打开文件查看单元格源代码,确认 %env FOO 仍是原魔法语句、变量名完整,没有残留 get_ipython 占位内容;再在项目认可的内核中验证该单元格行为。
普通 Python 的 AST 不能直接解析 %env,所以不能把魔法语句原文拿给 ast.parse(),将其异常当作 Black 的复现。Notebook 文件名、语言元数据及 Jupyter 依赖也需要正确,单独把文件改名为 .py 会走另一条路径。
这个修复处理非 ASCII 前缀与 AST 字节偏移的对齐。未知自定义魔法、未安装 IPython 或普通 Python 语法错误,仍需分别处理;更新不会使任意 Notebook 语法都自动得到支持。


