`string-or-bytes-too-long` (`PYI053`)
2026-09-07 18:12:34作者:苗圣禹Peter
[lint]
select = ["PYI053"]
Long name in __all__
Strings in __all__ correspond to exported names and should be exempt from the rule.
__all__ = [
"aaaaaaaaaabbbbbbbbbbccccccccccddddddddddeeeeeeeeeef",
]
把它拆解开来,每一部分都有明确语义:
| 文档元素 | mdtest 语义 |
| --- | --- |
| 一级标题 ``# `string-or-bytes-too-long` (`PYI053`)`` | 测试套件(测试用例集)的名称,同时也是被测规则的标识 |
| `toml` 围栏代码块 | **配置块**:被反序列化为 Ruff 的 `Options`,驱动本条测试的运行配置,等价于上面的 `[lint] select` |
| `## Long name in `__all__`` | 一个小节标题,也是一个独立**测试用例**的名字;测试名会由各级小节标题拼接而成 |
| `pyi` 围栏代码块 | 被检代码:会被写入内存中的 `mdtest_snippet.pyi` 文件后交给 linter 执行 |
| 无任何 `# error:` 断言注释 | 意味着**期望该片段不产生任何诊断** |
用例标题 “Long name in `__all__`” 直白地描述了本条测试的意图:`__all__` 中的字符串对应的是模块导出的名称,**应被本规则豁免**。代码块里放置的字符串足足有 51 个字符(10 个 `a` + 10 个 `b` + 10 个 `c` + 10 个 `d` + 10 个 `e` + 1 个 `f`),远超 50 字符阈值,却没有附带任何 `# error` 期望注释,因此该测试的断言就是:**即使超过 50 个字符,`__all__` 里的字符串也不得报出 `PYI053`**。
### mdtest 测试是怎么跑起来的
这些 `resources/mdtest/**/*.md` 文件由 [crates/ruff_mdtest/tests/mdtest.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_mdtest/tests/mdtest.rs?utm_source=gitcode_repo_files) 通过 `datatest_stable` 以 `root = "../ruff_linter/resources/mdtest", pattern = r"\.md$"` 的方式全部收集为集成测试。每个 Markdown 文件被解析成一个测试套件,内部每个含可检代码块的 `#`/`##` 小节则成为一个测试用例。
[parser.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/mdtest/src/parser.rs?utm_source=gitcode_repo_files) 负责把 Markdown 解析为结构化的 `MarkdownTestSuite`:
- 匿名的 `toml` 围栏块被视为该小节的配置([crates/mdtest/src/parser.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/mdtest/src/parser.rs?utm_source=gitcode_repo_files#L839-L841) 与 `process_config_block`),解析失败会直接报错;同一小节内不允许出现多个 TOML 配置块;
- 语言为 `pyi` 的围栏块会自动生成文件名 `mdtest_snippet.pyi`([crates/mdtest/src/parser.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/mdtest/src/parser.rs?utm_source=gitcode_repo_files#L351-L356)),`py`/`python` 对应 `mdtest_snippet.py`,`ipynb` 对应笔记本文件;也支持显式命名文件(如 `` `module.py`: ``)以及 `toml`、`text` 等;
- 同一小节内多个无显式命名的代码块会被**合并拼接**成同一个文件,用于模拟“片段间共享上下文”的场景;
- 代码块必须换行书写且前面要有至少一个空行,格式不规范时解析器会拒绝。
真正执行时,[crates/ruff_mdtest/src/lib.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_mdtest/src/lib.rs?utm_source=gitcode_repo_files) 会:
1. 在内存文件系统中把每个围栏块写入 `/src` 项目根下的对应文件(`run_test` 中的 `db.write_file`);
2. 用该小节 TOML 配置构造出真实的 Ruff 设置(`Configuration::from_options(...)`);
3. 通过 [ruff_linter 的测试入口](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_linter/src/test.rs?utm_source=gitcode_repo_files)(`test_contents`)对 `.pyi` 源码执行 lint,得到诊断;
4. 交由 [crates/mdtest/src/matcher.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/mdtest/src/matcher.rs?utm_source=gitcode_repo_files) 的 `match_file` 把每行诊断与该行的内联断言注释逐一比对。**没有写断言注释的行若产生任何诊断,就会被当作 “unexpected error” 导致测试失败**——这正是“`__all__` 用例如不豁免就会失败”的机制保证。
运行整个 mdtest 测试套件只需:
```bash
cargo test -p ruff_mdtest
```
由于小节标题就是测试名,也可以只跑与 `__all__` 豁免相关的这条用例(框架读取环境变量 `MDTEST_TEST_FILTER` 进行名字过滤,见 [crates/mdtest/src/lib.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/mdtest/src/lib.rs?utm_source=gitcode_repo_files#L20-L26)):
```bash
MDTEST_TEST_FILTER="__all__" cargo test -p ruff_mdtest
```
## 四、`__all__` 豁免的源码机制
回到规则实现 [string_or_bytes_too_long.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_linter/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs?utm_source=gitcode_repo_files#L55-L96),`PYI053` 的检查函数在报告诊断之前会依次做多次“放行”判断,其中与本文档测试用例直接相关的就是 `__all__` 检查:
```rust
if checker.in_dunder_all_assignment(parent) {
return;
}
```
`parent` 是当前字符串所在的语句,`in_dunder_all_assignment` 判断该语句是否为对 `__all__`(模块级魔术变量)的赋值。如果命中,函数直接返回、不产生诊断。规则文档注释也写明:**规则不适用于 `__all__` 中的超长条目,因为可假定那些导出名不在存根作者的可控范围内**(“The rule does not apply to long entries in `__all__`, which are assumed to be outside the stub author's control.”)。
也就是说,即使某个模块实际导出的名字很长(例如为了兼容旧版本而保留的超长别名),存根作者也无法更改它,此时要求用 `...` 替换会破坏存根的语义正确性,因此必须放行。mdtest 里的那段 51 字符示例,正是对这个分支行为的回归验证。
## 五、更多豁免与边界行为:源码级纵览
除了 `__all__`,规则实现里还藏着一整套边界处理,理解它们有助于你判断手头的 `.pyi` 会不会被 `PYI053` 误报。
### 仅作用于存根(`.pyi`)文件
`PYI053` 的规则入口不是独立的 AST 遍历,而是挂在“字符串类表达式”的公共分析点上。在 [crates/ruff_linter/src/checkers/ast/analyze/string_like.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_linter/src/checkers/ast/analyze/string_like.rs?utm_source=gitcode_repo_files#L21-L25) 中可以看到显式的文件类型门控:
```rust
if checker.source_type.is_stub() {
if checker.is_rule_enabled(Rule::StringOrBytesTooLong) {
flake8_pyi::rules::string_or_bytes_too_long(checker, string_like);
}
}
```
即只有当当前文件是 stub(`.pyi`)时才触发。该分析函数由 AST 检查器在访问每个表达式节点时调用,字符串字面量、字节字面量、f-string 与模板字符串(TString)都会走到这里(见 [crates/ruff_linter/src/checkers/ast/mod.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_linter/src/checkers/ast/mod.rs?utm_source=gitcode_repo_files#L2283-L2291))。规则在规则表中的注册位置见 [crates/ruff_linter/src/codes.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_linter/src/codes.rs?utm_source=gitcode_repo_files#L978)。
### docstring 豁免
如果超长字符串本身就是 docstring(当前语句是文档字符串语句),则跳过:
```rust
if is_docstring_stmt(parent) {
return;
}
```
存根中的文档说明文字不计入此规则。
### 类型定义上下文豁免
在类型定义与延迟求值的类型定义(如尚未展开的类型别名、`Annotated`/字符串化注解等被 deferred 处理的场景)内部,规则同样放行:
```rust
if semantic.in_type_definition() | semantic.in_deferred_type_definition() {
return;
}
```
### `warnings.deprecated` / `typing_extensions.deprecated` 消息豁免
用于标记弃用的超长说明消息是有价值的运行时/IDE 信息,不应被替换成 `...`。规则通过 `is_warnings_dot_deprecated` 判断当前字符串的父表达式是否为对 `warnings.deprecated(...)` 或 `typing_extensions.deprecated(...)` 的调用([crates/ruff_linter/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_linter/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs?utm_source=gitcode_repo_files#L120-L136)),命中则跳过。
### 不同字面量类型的长度口径
阈值 50 的判断依据字面量类型而不同([crates/ruff_linter/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_linter/src/rules/flake8_pyi/rules/string_or_bytes_too_long.rs?utm_source=gitcode_repo_files#L77-L89)):
| 字面量类型 | 长度统计方式 |
| --- | --- |
| 普通字符串 `str` | `value.chars().count()`:按 **Unicode 码点数量**统计,而不是按字节数或显示列宽 |
| 字节字面量 `bytes` | `value.len()`:按**字节数**统计 |
| f-string | 由 `count_f_string_chars` 统计:累加所有字面量分段的码点数,并把每个 `{插值表达式}` 按其在源码中的 range 长度计入 |
| 模板字符串 TString | 直接跳过(源码中留有 TODO,尚未决定插值部分的精确计数口径) |
f-string 的计数函数还处理了隐式拼接的多个 f-string 分段(`f_string.value.iter()`),并对字符串分段取 `chars().count()`、对插值段取 `expr.range().len()`,因此**带插值的 f-string 是“近似统计”**——插值本身的源码宽度(含 `{}` 与内部空白)也会被计入总长度。这一近似口径在源码注释中标注了待完善的 TODO。
## 六、自动修复:整段替换为 `...`
一旦确认违规,规则会报告一个**安全修复(safe fix)**:把整个超长字面量(含其引号/前缀/隐式拼接的完整 range)替换成 `...`。
```rust
let mut diagnostic = checker.report_diagnostic(StringOrBytesTooLong, string.range());
diagnostic.set_fix(Fix::safe_edit(Edit::range_replacement(
"...".to_string(),
string.range(),
)));
```
因此像 `def f2(x: str = "51 character ...") -> None: ...` 这样的代码执行 `ruff check --fix`(或 IDE 的快速修复)后,会被改写为:
```pyi
def f2(x: str = ...) -> None: ...
```
## 七、回归快照与测试夹具
规则的常规测试还通过快照测试覆盖了大量边界,测试用例注册位于 [crates/ruff_linter/src/rules/flake8_pyi/mod.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_linter/src/rules/flake8_pyi/mod.rs?utm_source=gitcode_repo_files#L85-L86),同时用 `.py` 与 `.pyi` 两种夹具验证“普通 Python 不受影响、存根才受检”。生成快照 [ruff_linter__rules__flake8_pyi__tests__string-or-bytes-too-long_PYI053.pyi.snap](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_linter/src/rules/flake8_pyi/snapshots/ruff_linter__rules__flake8_pyi__tests__string-or-bytes-too-long_PYI053.pyi.snap?utm_source=gitcode_repo_files) 直接印证了上文所有边界:
- 恰好 50 字符的 `str`/`bytes`/f-string 默认值均标注 `# OK`;
- 51 字符的默认值报出 `PYI053`,并给出 `help: Replace with ...` 与逐行 diff(`x: str = "..."` 改为 `x: str = ...`);
- 50 个 ASCII 字符后再加一个 `\U0001f600`(emoji,UTF-8 下占 4 字节)依然触发——证明字符串按码点而非字节计数;
- 字节字面量含 `\xff` 转义时按字节数累计,超过 50 即触发;
- 带 `{foo}` 插值、总长超限的 f-string 同样被报;
- 装饰器 `@not_warnings_dot_deprecated("超长消息")` 会被报——它刻意不是 `warnings.deprecated`/`typing_extensions.deprecated`,用于证明豁免只针对真正的弃用标记调用。
## 八、内联断言与 `snapshot` 块:mdtest 的两种写法
关联文档使用的是“零断言”写法(无诊断即通过)。除此之外,mdtest 还支持在 Python 代码行尾写内联断言注释,由 [crates/mdtest/src/assertion.rs](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/mdtest/src/assertion.rs?utm_source=gitcode_repo_files) 解析,格式包括:
- `# error` / `# error: [PYI053]`:期望本行产生(该规则的)一个诊断,可带 1-based 列号与消息片段;
- `# snapshot: [PYI053]`:期望本行产生该规则的诊断并纳入快照校验;
- `# revealed: <类型>`:用于类型检查(ty)场景的 `reveal_type` 断言。
同目录下的姊妹测试 [crates/ruff_linter/resources/mdtest/flake8-pyi/redundant-numeric-union.md](https://gitcode.com/GitHub_Trending/ru/ruff/blob/0044db22fa125d977c3ea909a70e8dcadc35b78b/crates/ruff_linter/resources/mdtest/flake8-pyi/redundant-numeric-union.md?utm_source=gitcode_repo_files) 就是“断言式”写法的范例:它在 Python 片段行尾标注 `# error: [PYI041]`,并用紧随其后的 `
登录后查看全文
热门项目推荐
相关项目推荐
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
最新内容推荐
Angular Caretaker 指南:掌握 ng-dev pr merge 的 PR 合并、TGP 全局预提交与跨仓库同步机制Impeccable `colorize` Command: Adding Strategic Color to Monochromatic UIs Without Breaking the Brand深入解析 CPython `sys` 模块:解释器状态、运行时控制与内置工具的完整参考FastAPI 部署实战:使用 `--workers` 让 Uvicorn 开启多 Worker 进程,榨干多核 CPUOpenCV Point Polygon Test 教程:用 cv::pointPolygonTest 判断点与轮廓的位置关系并计算带符号距离(附 C++/Java/Python 示例与源码剖析)Traefik HTTP 路由规则(Rules)与优先级(Priority)完全指南:匹配器用法、RuleSyntax 与排序机制Angular Query `infiniteQueryOptions`:在 Angular 中类型安全地定义、共享与复用无限查询选项Claude Sonnet 3.5 官方系统提示词(2024-11-22 快照)逐条拆解:claude_behavior 行为规范的结构、规则与工程启示Angular v20+ 现代开发与 AI 编码规范指南:Signals 状态管理、Standalone 组件与原生控制流的最佳实践用好 OpenMontage 的 gsap-utils:从 clamp 到 pipe 的 GSAP 工具函数权威速查
项目优选
收起
deepin linux kernel
C
33
18
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.13 K
2.75 K
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
暂无描述
Markdown
897
5.8 K
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
529
593
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
916
1.83 K
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.58 K
1.01 K
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.35 K
1.46 K
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.01 K
515
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
547
388