首页
/ Ansible 代码风格规范全解:从 Python 版本支持到 sanity 测试自动校验

Ansible 代码风格规范全解:从 Python 版本支持到 sanity 测试自动校验

2026-09-04 21:53:50作者:滕妙奇

本文以 Ansible 官方仓库中的编码风格文档 context/coding-style.md 为核心,逐条解读 Ansible 对新代码与既有代码修改(含单元测试)的全部风格要求:为什么控制器代码要求 Python 3.13+ 而模块可以低至 3.9、类型注解与 PEP 695 语法的使用边界、f-string 与 !r 引号限定符的具体用法,以及如何用 ansible-test sanity 一键完成格式化与合规检查。读完本文,你可以按 Ansible 社区的标准写出能通过全套 sanity 检查的提交。

Python 版本支持:三层不同的最低版本

Ansible 的代码库被拆分成运行环境不同的三层,每层的最低 Python 版本要求由仓库中的真实配置决定,而不是惯例:

代码层 最低 Python 版本 定义位置
控制器代码(controller) 3.13 pyproject.toml 中的 requires-python
模块 / module_utils(运行在目标机上) 3.9 lib/ansible/module_utils/basic.py 中的 _PY_MIN
测试用版本范围 ansible-test 定义 test/lib/ansible_test/

pyproject.toml 中可以确认控制器代码的门槛:

[project]
requires-python = ">=3.13"

而在目标端执行的模块代码,其版本检查逻辑位于 lib/ansible/module_utils/basic.py

_PY_MIN = (3, 9)

if sys.version_info < _PY_MIN:
    msg = f"Ansible requires Python {'.'.join(map(str, _PY_MIN))} or newer on the target. "

模块支持比控制器代码更宽的 Python 版本范围,因为模块运行在远端主机上,其 Python 环境不受控制器安装方式的约束。由此引出一条核心写作原则:优先使用较新的 Python 特性,但前提是当前所写代码层的最低支持版本已具备该特性,且不与其它受支持版本冲突。例如,可以在控制器代码中放心使用 3.12+ 的语法糖,但不能把它写进 lib/ansible/modules/module_utils/ 的代码中——后者的下限是 3.9。

依赖选择:标准库优先,复用项目内代码

依赖方面的规则只有两条,但直接影响模块体积与目标机兼容性:

  • 优先使用 Python 标准库,而非外部第三方依赖;
  • 优先复用 Ansible 项目内部已有的代码。

这与 Ansible 的架构一致:模块代码会被打包进 Ansiballz 载荷发送到远端执行,每引入一个外部依赖都会增大载荷并增加目标机的运行时风险。因此,凡是项目内(尤其是 lib/ansible/module_utils/)已有可用实现,应直接复用而不是重新引入外部包。

Markdown 与 ASCII 字符规范

Markdown 文件

仓库中的 Markdown 文件统一采用 GitHub Flavored Markdown,并由 pymarkdown sanity 测试自动校验。两条具体书写规则:

  • 无序列表项使用短横线(-),不要使用星号(*);
  • 列表项末尾要加句号。

ASCII 字符

no-smart-quotes sanity 测试强制校验:

  • 使用 ASCII 引号('"),不要使用 Unicode 智能引号(' ' " ");
  • 使用 ASCII 短横线(---)代替 em dash()。

这一点在代码、注释、文档中一视同仁。编辑器若开启了"自动替换为智能引号"功能,在贡献 Ansible 前应当关闭。

行宽与行尾空白

  • 行长上限为 160 个字符
  • 不要在行尾留下任何尾随空白。

160 的行长上限同时被后文的 black 格式化检查复用,因此手写代码时按此宽度折行即可与自动格式化工具保持一致。

Docstring 规范:解释行为,不罗列参数

Ansible 对 docstring 的要求是"少而准":

  • 解释被注释代码做什么,但不要为参数创建结构化条目;
  • 不要在 docstring 中记录参数类型——类型信息交给类型注解(type hints)表达;
  • 一切被视为公开 API(public API)的代码必须有 docstring;
  • 内部代码也应当有 docstring,对单元测试同样如此,且往往很有意义。

这意味着类似以下"参数手册式"的写法不符合规范:

def setup(src: str, dest: str) -> bool:
    """Setup the target.

    :param src: source path (str)
    :param dest: destination path (str)
    :returns: whether setup succeeded
    """

应当改为一句自然语言说明行为,把参数与类型信息交给签名本身。

源码文本中的换行:一行一句

在 docstring、注释以及 changelog fragment 等文本中,尽量保持一行只写一个句子

这一条与仓库的 changelog 实践直接相关:Ansible 用 YAML fragment 收集变更说明(见 changelogs/README.mdchangelogs/fragments/ 目录)。一行一句让 diff 和 code review 更精确——某个句子被修改时,改动只影响一行,避免长段落造成的无谓冲突。

类型注解:from __future__ import annotations 与 PEP 695

统一使用原生注解

boilerplate sanity 测试校验,所有 Python 文件应使用:

from __future__ import annotations

配合它使用原生类型注解,并为函数/方法的参数与返回值标注类型,唯一例外是注解本身过于复杂的情况(例如 TypedDict)。

需要理解 mypy sanity 测试的边界:它对已标注的函数/方法执行类型检查。也就是说,注解是"标注即承诺"——一旦写了注解,就要能通过 mypy 检查;没写注解的函数则不在检查范围内。这个设计鼓励渐进式加注解,而不是强制全量注解。

PEP 695 类型参数语法

优先使用 PEP 695 的类型参数语法,而不是单独声明 TypeVarParamSpec

# 推荐(PEP 695)
def first(itemsT -> T:
    return values[0]

# 不推荐:单独声明 TypeVar
def first(values: list[T]) -> T:
    return values[0]

关键例外:这条规则不适用于 module_utils/ 下的代码。因为模块侧代码必须支持旧版 Python(下限 3.9,见 lib/ansible/module_utils/basic.py 中的 _PY_MIN),而这些旧版本没有 PEP 695 语法。所以在 module_utils/ 中仍需使用传统的 TypeVar 声明方式。

格式字符串与字符串引号

使用 f-string

一律使用 f-string,不要使用 % 格式化或 str.format。唯一的例外是日志语句:由于日志框架采用延迟求值(level 不匹配时不执行格式化),日志中应保留 % 风格以避免无谓的字符串构造开销:

# 一般代码
msg = f"Unable to process {name!r}: {err}"

# 日志:延迟格式化,不用 f-string
logger.debug("Retrying connection to %s in %d seconds", host, delay)

使用 !r 引号限定符

当需要给字符串里的值加上引号时,使用 !r 格式限定符(等价于 repr),而不是手动拼接引号:

# 正确
f"A string with a {quoted!r} value."

# 不推荐:手动加引号
f"A string with a '{quoted}' value."

!r 会正确处理值内包含引号、特殊字符等边界情况,而手动拼接容易生成非法或误导性的字符串。

代码格式化:black 只约束 _internal

black sanity 测试针对所有 _internal 包运行(如 lib/ansible/_internal/ 目录),使用默认配置,仅做两处调整:

  • 行长上限提高到 160;
  • 禁用引号转换(no quote normalization),即 black 不会把你的单引号改成双引号。

格式化修改应交给工具自动完成,而不是手动调整:

ansible-test sanity --test black --fix

这条命令会扫描受影响的 _internal 包并自动应用所需的全部格式变更。这也解释了风格文档为什么把行长设为 160——手写代码与自动格式化共用同一个宽度基准。

模块中的 import 顺序:E402 被忽略

在 pep8 检查配置中,E402(模块级 import 不在文件顶部)规则被整体忽略,可见 test/lib/ansible_test/_util/controller/sanity/pep8/current-ignore.txt。这不是疏漏,而是有意为之,因为 Ansible 模块的文档字符串必须先于代码出现:

lib/ansible/modules/ 下的模块中,所有 import 必须位于 DOCUMENTATIONEXAMPLESRETURN 三个定义之后

#!/usr/bin/python
# -*- coding: utf-8 -*-

from __future__ import annotations

DOCUMENTATION = '''
...
'''

EXAMPLES = '''
...
'''

RETURN = '''
...
'''

from ansible.module_utils.basic import AnsibleModule

这样的顺序让文档元数据随模块源码一同可被提取,也保证 ansible-doc 等工具能直接读取字符串常量而无需先执行 import。

小结:用 sanity 测试验证你的风格

上述每条规范在文档中都对应了自动校验手段,这是 Ansible 风格体系的落地方式——规则不靠人工审查,而靠 ansible-test 的 sanity 测试强制:

规范条目 校验测试
Markdown 语法(GFM) pymarkdown
ASCII 引号 no-smart-quotes
from __future__ import annotations 样板 boilerplate
类型注解一致性 mypy(仅检查已标注函数)
_internal 包格式 black
导入位置(E402) pep8 配置中显式忽略

sanity 测试的基础设施位于 test/lib/ansible_test/ 目录,其 CLI 入口在 pyproject.toml 中声明为 ansible-test。提交前对改动运行一次 ansible-test sanity,即可在本地提前发现上述所有风格问题。

除本文覆盖的风格规范外,仓库 context/ 目录下的姊妹文档可进一步深入:代码组织结构参见 context/code-structure.md,编写测试的完整要求参见 context/writing-tests.md

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
987
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384