Python 编码规范
这里整理 XRobot、LibXR_CppCodeGenerator、xr-syntax 等 Python 仓库的代码写法,与编码规范中的 C++ 写法对应。反例取自仓库中实际出现过的代码。
Python 版本
- 支持 Python 3.10 及以上版本,
pyproject.toml中写requires-python = ">=3.10",CI 覆盖 3.10 到当前最新的正式版本。 - 版本下限跟随仍在支持期内、最早的 Ubuntu LTS 自带的 Python 版本,只在大版本发布时调整。
- 可以使用 3.10 的语法,例如
X | None、list[str]和match。
命名
- 包和模块文件使用
snake_case。
反例:
xrobot/GenerateMain.py
libxr/GeneratorCodeSTM32.py
正例:
xr_syntax/cpp/lexer.py
xrobot/generate_main.py
- 类使用
PascalCase;函数、方法、变量使用snake_case;常量使用全大写。
class ConfigError(ValueError):
...
MANIFEST_VERSION = 2
def load_config(path: Path) -> dict:
...
- 只在模块内部使用的名称以
_开头。
def _header_relative(path: Path, header: Path) -> str:
...
模块与导入
- 包内使用绝对导入。
from xrobot.config import ConfigError, load_config
- 导入按标准库、第三方库、本项目分组,组内按字母排序,由
ruff检查。
import re
from pathlib import Path
import yaml
from xrobot.config import ConfigError
- 命令行入口只在执行某个命令时导入它用到的模块,以缩短启动时间;其他模块在文件开头导入。
def cmd_gen(args: argparse.Namespace) -> None:
from xrobot.generate_main import generate
...
版式
- 版式交给
ruff format处理:双引号,行宽 100,与 xr-syntax 现有配置一致。 - 不为了统一版式手工改动无关代码;格式化工具的改动单独提交。
声明与定义
- 公开的函数和方法写类型标注。
反例:
def find_root(start='.'):
start = Path(start).resolve()
正例:
def find_root(start: str | Path = ".") -> Path:
"""从 start 向上查找含有 Modules/modules.yaml 的目录。
Find the nearest directory at or above start that contains Modules/modules.yaml.
"""
start = Path(start).resolve()
- 字符串格式化使用 f-string。
反例:
return '%d %s%s' % (number, noun, '' if number == 1 else 's')
正例:
return f"{number} {noun}{'' if number == 1 else 's'}"
- 路径使用
pathlib.Path;读写文本时写明encoding="utf-8"。
text = path.read_text(encoding="utf-8")
- 调用外部命令时以列表传参,不经过 shell。
反例:
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
正例:
result = subprocess.run(["git", "-C", str(path), "rev-parse", "HEAD"],
capture_output=True, text=True, encoding="utf-8")
注释
- 每个模块、类、函数都有 docstring:第一行中文,第二行英文,与 LibXR 头文件中的
@brief 中文 / English对应。
def _has_chinese(text: str | None) -> bool:
"""判断文本是否包含中文。
Return whether text contains Chinese characters.
"""
return bool(text and _CHINESE.search(text))
- 参数、返回值或异常不能从名字和类型看出时,按
Args:、Returns:、Raises:分段说明,对应 Doxygen 的@param、@return;每一项同样中英双语。
def selected_config(self) -> Path:
"""当前头文件对应的配置;没有头文件时为 User/xrobot.yaml。
The configuration the current header was generated for; User/xrobot.yaml without a header.
Raises:
ProjectError: 头文件记录的配置已不存在。
The configuration named by the header no longer exists.
"""
- 行内注释说明语义和边界条件,不记录试错过程,不写口语化句子;与 docstring 一样,中文一行、英文一行。
# 编辑器仍需要其他配置来重新选择。
# Editors still need the other configurations to select one.
selected = project.header_selection()
- docstring 是否齐全且包含中文由各仓库检查:XRobot 与 LibXR_CppCodeGenerator 为
tests/test_docstrings.py,xr-syntax 为 CI 中运行的tools/check_bilingual_docs.py。
错误处理
- 每个工具定义自己的异常类型,继承
ValueError;错误信息以<文件>:<行>或<配置>: <位置>开头,并说明需要做什么。
class ConfigError(ValueError):
"""带有 <配置>: <位置> 前缀的配置错误。
A configuration error that already carries its <config>: <path> prefix.
"""
raise ConfigError(f"{source}:{line}: YAML anchors and aliases are not allowed; reference "
"instances by id and share values through constexprs")
- 命令行入口捕获这些异常,只输出一行错误信息,退出码为 1。
- 只捕获预期的异常类型,不用宽泛的
Exception吞掉错误。捕获后先回滚再重新抛出的情况除外。
反例:
def _shell_join(args: Sequence[str]) -> str:
try:
import shlex
return " ".join(shlex.quote(arg) for arg in args)
except Exception:
return " ".join(args)
正例(回滚后重新抛出):
try:
...
except Exception:
for identity in reversed(applied):
... # 恢复已检出的模块 / restore the Modules already checked out
raise
# noqa只写在需要例外的那一行上,并注明规则编号,不在文件或配置中整体关闭规则(E501除外,见 ruff)。
from xrobot.config import ConfigError # noqa: F401
测试代码
- XRobot 与 LibXR_CppCodeGenerator 使用
unittest,xr-syntax 使用pytest;各仓库沿用现有框架。 - 需要编译器的测试通过环境变量
CXX选择编译器,找不到编译器时跳过。 - 测试代码的命名和版式与主代码一致。
- 测试放在哪里、测什么、怎样确认每个测试都有作用,见测试规范。
ruff
pyproject.toml中配置ruff:
[tool.ruff]
line-length = 100
target-version = "py310"
[tool.ruff.lint]
select = ["E", "F", "I", "UP", "B", "SIM"]
ignore = ["E501"]
-
行宽由
ruff format控制;格式化后仍超出的长字符串不另行报错,因此忽略E501。 -
CI 固定
ruff==0.16.9,只检查源码、测试和脚本目录(XRobot 与 xr-syntax 为src tests tools,LibXR_CppCodeGenerator 为src tests scripts),例如 XRobot:
ruff format --check src tests tools
ruff check src tests tools
- xr-syntax 另外启用
TID规则并设置ban-relative-imports = "all",CI 还运行mypy src(strict = true)。