该代码是一个Python文档字符串生成工具,它能够自动为给定的Python代码生成指定风格(Google、NumPy或Sphinx)的文档字符串。它通过解析代码结构,使用AI模型生成符合规范的文档字符串,并支持将结果写回原文件或输出到控制台。
graph TD
A[开始] --> B{命令行调用?}
B -- 是 --> C[解析命令行参数]
B -- 否 --> D[直接调用WriteDocstring.run方法]
C --> E[读取指定文件内容]
D --> F[接收代码字符串和风格参数]
E --> G[调用WriteDocstring.run方法]
F --> G
G --> H[简化代码: 移除表达式和末尾if语句]
H --> I[构建系统提示词,包含风格示例]
I --> J[调用AI模型生成带文档字符串的代码]
J --> K[解析AI输出,提取代码部分]
K --> L[将生成的文档字符串合并到原始代码中]
L --> M{是否覆盖原文件?}
M -- 是 --> N[将结果写回原文件]
M -- 否 --> O[返回生成的代码字符串]
N --> P[结束]
O --> P
WriteDocstring (Action)
├── 类字段: desc, i_context
├── 类方法: run, write_docstring (静态方法)
└── 全局函数: _simplify_python_code用于指导大语言模型生成文档字符串的系统提示词模板,包含占位符以适配不同的文档风格。
类型:str
展示符合Google风格文档字符串格式的示例代码。
类型:str
展示符合NumPy风格文档字符串格式的示例代码。
类型:str
展示符合Sphinx风格文档字符串格式的示例代码。
类型:str
一个字典,将文档风格名称('google', 'numpy', 'sphinx')映射到对应的示例代码字符串。
类型:dict[str, str]
描述此Action类功能的字符串,用于元信息或日志记录。
类型:str
可选的上下文信息字符串,可能用于存储或传递与当前文档生成任务相关的额外信息。
类型:Optional[str]
该函数用于简化给定的Python代码字符串。它通过解析代码的抽象语法树(AST),移除所有表达式节点(如独立的字符串、数字等)以及最后一个if语句(如果存在),从而生成一个更简洁的代码版本,通常用于生成文档字符串时减少不必要的信息。
参数:
code:str,需要被简化的原始Python代码字符串。
返回值:str,简化后的Python代码字符串。
graph TD
A[开始] --> B[解析代码为AST]
B --> C[遍历AST主体]
C --> D{节点是Expr类型?}
D -- 是 --> E[从主体列表中移除该节点]
D -- 否 --> F[保留该节点]
E --> G[继续遍历]
F --> G
G --> H[遍历结束]
H --> I{最后一个节点是If类型?}
I -- 是 --> J[移除最后一个节点]
I -- 否 --> K[保留所有节点]
J --> L[将AST反解析为代码字符串]
K --> L
L --> M[返回简化后的代码]
M --> N[结束]
def _simplify_python_code(code: str) -> str:
"""Simplifies the given Python code by removing expressions and the last if statement.
Args:
code: A string of Python code.
Returns:
The simplified Python code.
"""
# 1. 使用ast.parse将输入的代码字符串解析为抽象语法树(AST)对象。
code_tree = ast.parse(code)
# 2. 过滤AST的body列表,移除所有类型为ast.Expr的节点。
# ast.Expr通常代表独立的表达式,如文档字符串、字面量等,这些在生成文档时通常不需要。
code_tree.body = [i for i in code_tree.body if not isinstance(i, ast.Expr)]
# 3. 检查过滤后的body列表的最后一个元素是否为ast.If类型。
# 如果是,则将其移除。这通常用于处理脚本末尾的`if __name__ == "__main__":`块。
if isinstance(code_tree.body[-1], ast.If):
code_tree.body.pop()
# 4. 使用ast.unparse将简化后的AST对象转换回Python代码字符串并返回。
return ast.unparse(code_tree)该方法用于为给定的Python代码生成指定风格的文档字符串(docstring)。它通过简化输入代码、调用AI模型生成文档字符串,然后将生成的文档字符串与原始代码合并,最终返回包含文档字符串的完整代码。
参数:
code:str,需要生成文档字符串的Python代码字符串。system_text:str,用于指导AI模型生成文档字符串的系统提示文本,默认为PYTHON_DOCSTRING_SYSTEM。style:Literal["google", "numpy", "sphinx"],指定生成的文档字符串风格,可选值为'google'、'numpy'或'sphinx',默认为'google'。
返回值:str,返回添加了文档字符串的Python代码字符串。
graph TD
A[开始] --> B[格式化system_text]
B --> C[简化Python代码]
C --> D[调用AI模型生成文档字符串]
D --> E[解析AI输出,提取代码]
E --> F[合并文档字符串到原始代码]
F --> G[返回最终代码]
G --> H[结束]
async def run(
self,
code: str,
system_text: str = PYTHON_DOCSTRING_SYSTEM,
style: Literal["google", "numpy", "sphinx"] = "google",
) -> str:
"""Writes docstrings for the given code and system text in the specified style.
Args:
code: A string of Python code.
system_text: A string of system text.
style: A string specifying the style of the docstring. Can be 'google', 'numpy', or 'sphinx'.
Returns:
The Python code with docstrings added.
"""
# 1. 格式化系统提示文本,插入指定的风格和示例
system_text = system_text.format(style=style, example=_python_docstring_style[style])
# 2. 简化输入的Python代码,移除表达式和最后的if语句
simplified_code = _simplify_python_code(code)
# 3. 调用AI模型,传入简化后的代码和系统提示,生成包含文档字符串的代码
documented_code = await self._aask(f"```python\n{simplified_code}\n```", [system_text])
# 4. 解析AI模型的输出,提取Python代码部分
documented_code = OutputParser.parse_python_code(documented_code)
# 5. 将生成的文档字符串合并到原始代码中,返回最终结果
return merge_docstring(code, documented_code)这是一个静态方法,用于为指定的Python文件生成并写入符合指定风格的文档字符串(docstring)。它读取文件内容,调用WriteDocstring类的run方法生成带文档字符串的代码,并根据overwrite参数决定是覆盖原文件还是仅返回生成的代码字符串。
参数:
filename:str | Path,需要生成文档字符串的Python文件的路径。overwrite:bool,默认为False。如果为True,则将生成的带文档字符串的代码写回原文件;如果为False,则仅返回生成的代码字符串。style:Literal["google", "numpy", "sphinx"],默认为"google"。指定生成文档字符串的风格,可选值为'google'、'numpy'或'sphinx'。
返回值:str,生成的、包含文档字符串的Python代码字符串。
graph TD
A[开始: write_docstring] --> B[读取文件内容];
B --> C[创建WriteDocstring实例];
C --> D[调用run方法生成带文档字符串的代码];
D --> E{overwrite?};
E -- 是 --> F[将代码写入原文件];
F --> G[返回生成的代码];
E -- 否 --> G;
G --> H[结束];
@staticmethod
async def write_docstring(
filename: str | Path, # 参数:需要处理的Python文件路径
overwrite: bool = False, # 参数:是否覆盖原文件
style: Literal["google", "numpy", "sphinx"] = "google" # 参数:文档字符串风格
) -> str: # 返回值:生成的带文档字符串的代码
# 异步读取指定文件的内容
data = await aread(str(filename))
# 创建WriteDocstring类实例,并调用其run方法生成带文档字符串的代码
code = await WriteDocstring().run(data, style=style)
# 根据overwrite参数决定是否将生成的代码写回原文件
if overwrite:
await awrite(filename, code)
# 返回生成的代码字符串
return code该类是 Action 的子类,负责为给定的 Python 代码生成指定风格的文档字符串(docstring)。它通过调用 LLM 来理解代码结构并生成符合 Google、NumPy 或 Sphinx 风格的文档字符串,然后将其合并回原始代码中。
该系统定义了三种主流的 Python 文档字符串风格(Google、NumPy、Sphinx)的模板和示例。它通过 PYTHON_DOCSTRING_SYSTEM 系统提示词和 _python_docstring_style 字典来配置和提供不同风格的示例,确保生成的文档字符串格式规范、统一。
该函数使用 Python 的 ast 模块解析代码,移除所有表达式节点(如独立的字符串,通常是已有的文档字符串)和文件末尾的 if __name__ == "__main__": 语句块。其目的是清理代码,只保留类、函数等核心结构定义,以减少输入给 LLM 的上下文长度和干扰信息。
该函数(虽然实现在导入的 metagpt.utils.pycst 模块中,但它是本功能的关键组件)负责将新生成的、包含文档字符串的代码与原始代码进行智能合并。它需要精确地识别出函数、类等代码块的位置,并用新的、带有文档字符串的版本替换旧版本,同时保持原始代码的其他部分(如实现逻辑、注释、格式)不变。
通过 fire 库实现,提供了 WriteDocstring.write_docstring 静态方法的命令行调用方式。用户可以通过命令行指定文件路径、是否覆盖原文件以及文档字符串风格,使得该工具可以方便地集成到开发流程或脚本中。
_simplify_python_code函数存在逻辑缺陷:该函数在移除ast.Expr节点后,直接假设列表最后一个元素是ast.If类型并进行移除。如果最后一个元素不是if语句(例如,是一个函数或类定义),此操作将引发IndexError异常。此外,如果code_tree.body在处理后变为空列表,code_tree.body[-1]的访问同样会引发IndexError。_simplify_python_code函数返回类型标注错误:函数签名中声明的返回类型为None,但函数实际返回的是ast.unparse(code_tree)的结果,这是一个str类型,导致类型声明与实际行为不符。PYTHON_DOCSTRING_SYSTEM提示词模板存在潜在风险:模板中要求“将函数体替换为 Ellipsis 对象(...)以减少输出”,这可能导致生成的代码片段 (documented_code) 中函数体信息丢失。虽然后续的merge_docstring函数旨在合并文档字符串和原始代码体,但如果合并逻辑不健壮或原始代码结构复杂,可能引发合并错误或信息丢失。run方法对system_text参数的默认值使用不当:system_text参数默认值为PYTHON_DOCSTRING_SYSTEM,但在方法内部会对其进行格式化 (system_text.format(...))。如果调用者传入一个自定义的、不包含{style}和{example}占位符的字符串,format方法将抛出KeyError异常。这限制了该参数的灵活使用。- 缺乏输入验证与错误处理:代码未对输入参数(如
code字符串是否为有效的 Python 语法,style参数是否为有效值)进行充分验证。ast.parse在解析无效代码时会抛出SyntaxError,但此异常未被捕获和处理。merge_docstring函数也可能因代码结构不匹配而失败。 - 潜在的代码合并冲突:
merge_docstring函数(虽然未在此文件中定义)的可靠性是整个功能的核心。如果它无法正确处理所有边缘情况(例如,嵌套函数、装饰器、带有复杂语句的函数体等),则生成的最终代码可能不正确。
- 修复
_simplify_python_code函数:- 修正返回类型注解为
-> str。 - 在尝试访问
code_tree.body[-1]之前,先检查列表是否为空 (if code_tree.body and isinstance(code_tree.body[-1], ast.If):)。
- 修正返回类型注解为
- 增强
run方法的健壮性:- 在调用
_simplify_python_code和ast.parse的地方添加try-except块,捕获SyntaxError等异常,并返回有意义的错误信息。 - 考虑移除
system_text参数的默认值,或改为在方法内部根据style参数动态构造系统提示词,以避免对调用者传入的system_text进行格式化时可能引发的异常。
- 在调用
- 改进提示词策略:重新评估
PYTHON_DOCSTRING_SYSTEM中要求替换函数体为...的指令。可以考虑改为要求模型“保持函数体原样”或“仅输出需要添加/修改文档字符串的部分”,以降低后续代码合并的复杂度和风险。 - 添加输入验证:在
run方法开始时,验证style参数是否在_python_docstring_style字典的键中。验证code参数是否非空。 - 提高
write_docstring静态方法的健壮性:在读取文件 (aread) 和写入文件 (awrite) 时,应考虑文件不存在、权限错误等IO异常,并进行适当处理。 - 增加日志记录:在关键步骤(如开始处理、调用大模型、合并代码、写入文件)添加日志输出,便于调试和监控工具的运行状态。
- 考虑性能优化:如果处理非常大的源文件,当前的
ast解析和合并操作可能会成为瓶颈。虽然对于典型用途可能足够,但可以评估是否有必要进行性能剖析和优化。 - 完善单元测试:为
_simplify_python_code函数和WriteDocstring类的run方法编写全面的单元测试,覆盖各种边界情况(空代码、无效语法、只有if语句的代码、没有if语句的代码等),以确保修复和优化后的代码行为正确。
- 核心目标:为输入的Python代码自动生成符合指定风格(Google、NumPy、Sphinx)的文档字符串(docstring)。
- 功能约束:
- 支持对模块、类、函数/方法级别的代码生成docstring。
- 当函数/方法已包含类型注解时,生成的docstring应避免重复包含类型信息。
- 生成的docstring必须与原始代码的语法结构正确合并,不能破坏原有代码逻辑。
- 提供命令行接口,支持指定输入文件、输出方式(覆盖或返回)及文档风格。
- 非功能约束:
- 可用性:提供清晰的命令行使用说明和错误提示。
- 可维护性:代码结构清晰,将核心逻辑(
WriteDocstring类)、工具函数(_simplify_python_code)和配置(风格示例)分离。 - 可扩展性:通过
_python_docstring_style字典管理不同风格模板,便于未来添加新的docstring风格。
- 输入验证:
WriteDocstring.run方法依赖调用方传入有效的code字符串和style参数。style参数通过Literal类型进行约束。WriteDocstring.write_docstring方法通过aread读取文件,若文件不存在或无法读取,将抛出底层IO异常。
- 处理过程错误:
_simplify_python_code函数使用ast.parse解析代码。如果输入的code不是有效的Python语法,将抛出SyntaxError。OutputParser.parse_python_code在解析大模型返回的代码时,如果返回内容不包含有效的代码块,可能返回空字符串或引发解析异常。merge_docstring函数在执行代码合并时,如果新旧代码的AST结构不匹配(例如,大模型返回的代码结构与简化后的代码结构差异过大),合并可能失败或产生错误结果。
- 异常传播:当前设计未显式捕获和处理上述大部分异常。异常将向上层调用者(如命令行用户或调用
run方法的其他组件)传播。在命令行场景下,fire库会捕获异常并打印错误信息。
- 核心数据流(
WriteDocstring.run方法):- 输入:原始代码字符串 (
code)、系统提示词模板 (system_text)、文档风格 (style)。 - 处理:
- 格式化提示词:将
style和对应示例填入system_text模板。 - 代码简化:调用
_simplify_python_code,移除表达式和末尾的if __name__ == "__main__"语句块,生成simplified_code。 - 大模型请求:将
simplified_code和格式化后的system_text发送给大模型 (_aask),请求生成带docstring的代码。 - 代码解析:使用
OutputParser.parse_python_code从大模型响应中提取代码块,得到documented_code。 - 代码合并:调用
merge_docstring,将documented_code中的docstring合并到原始code中,保留原始代码的函数体等实现细节。
- 格式化提示词:将
- 输出:合并后的、包含新docstring的完整代码字符串。
- 输入:原始代码字符串 (
- 状态机:本模块主要是一个无状态的工具类。
WriteDocstring类的run方法是纯函数式的,输出完全由输入决定。write_docstring静态方法封装了“读取文件->生成docstring->(可选)写回文件”的流程。
- 外部库依赖:
ast(Python标准库):用于解析和操作Python抽象语法树。fire:用于构建命令行接口。metagpt.actions.action.Action:父类,可能提供了_aask等方法与大模型交互的基础设施。metagpt.utils.common.OutputParser:提供从大模型文本响应中解析Python代码的功能。metagpt.utils.common.aread/awrite:异步文件读写工具函数。metagpt.utils.pycst.merge_docstring:使用libcst库进行精确的源代码级别合并,是保证合并正确性的关键组件。
- 接口契约:
WriteDocstring.run接口:期望接收有效的Python代码字符串。其成功执行依赖于父类Action提供的_aask方法能正常工作并返回预期格式的响应。merge_docstring函数:是本模块的核心外部契约。它接收原始代码和带新docstring的代码,返回合并后的代码。本模块的正确性严重依赖于该函数能正确处理各种代码结构。- 大模型服务:通过
_aask调用,是本模块功能的“智能”核心。其响应的质量和格式直接影响最终结果。