风格指南¶
本文档概述了 Vyper 开发团队遵循的代码风格、项目结构和实践。
注意
当前代码库的一部分不符合本风格指南。我们正在进行大规模重构,本指南旨在概述重构期间和之后的结构和最佳实践。重构的代码和新增的功能**必须**符合本指南。对现有功能的错误修复和修改**可以**采用与相关代码相同的风格。
项目组织¶
Vyper 中的每个子目录**应该**是一个独立的包,代表编译器的单个阶段或其他逻辑组件。
旨在从包外部模块调用的功能**必须**在基本
__init__.py
中公开。所有其他功能仅供内部使用。**应该**能够删除任何包并用另一个暴露相同 API 的包替换它,而不会破坏其他包中的功能。
代码风格¶
所有代码**必须**符合 PEP 8 风格指南,以下例外情况除外
最大行长为 100
我们使用 black 处理代码格式,并将行长选项设置为 80。这确保了整个项目的风格一致,并且通过避免强加主观意见节省了时间。
命名约定¶
名称**必须**符合 PEP 8 命名约定
**模块**使用简短的全部小写名称。如果提高可读性,可以在模块名称中使用下划线。
**类名**使用 CapWords 约定。
**异常**遵循与其他类相同的约定。
**函数**名称使用小写,如果提高可读性,则用下划线分隔单词。
**方法**名称和**实例**变量遵循与函数相同的约定。
**常量**使用全大写字母,用下划线分隔单词。
布尔值¶
布尔值**应该**以
is_
为前缀。布尔值**不能**表示负面属性(例如
is_not_set
)。这会导致双重否定评估,这对读者来说并不直观。返回单个布尔值的函数**应该**使用
@property
装饰器。
方法¶
命名函数或方法时**应该**使用以下约定。一致的命名在整个代码库中提供逻辑一致性,使未来的读者更容易理解方法的作用(以及不作用)。
get_
:用于简单的数据检索,没有任何副作用。
fetch_
:用于可能产生某种副作用的检索。
build_
:用于从其他数据创建新的对象。
set_
:用于在对象中添加新值或修改现有值。
add_
:用于向对象添加新属性或其他值。如果值已存在,则引发异常。
replace_
:用于变异对象。成功时应返回None
,或者如果出现问题则引发异常。
compare_
:用于比较值。返回True
或False
,不会引发异常。
validate_
:返回None
,或者如果出现问题则引发异常。
from_
:用于根据给定输入数据实例化对象的类方法。
对于其他功能,请选择能够清晰地传达意图的名称,而不是过于冗长。重点关注方法的作用,而不是方法的实现方式。
导入¶
导入排序由 isort 处理。我们遵循以下附加规则
标准库导入¶
标准库**应该**以绝对方式导入,并且不使用别名。导入库有助于可读性,因为其他用户可能熟悉该库。
# Good import os os.stat('.') # Bad from os import stat stat('.')
内部导入¶
内部导入是同一 Vyper 包中的两个模块之间的导入。
内部导入**可以使用**
import
或from ..
语法。导入的值**应该**是模块,而不是对象。导入模块而不是对象可以避免循环依赖问题。内部导入**可以使用**别名,如果这有助于可读性。
内部导入**必须**使用绝对路径。相对导入在移动模块时会导致问题。
# Good import vyper.ast.nodes as nodes from vyper.ast import nodes # Bad, `get_node` is a function from vyper.ast.nodes import get_node # Bad, do not use relative import paths from . import nodes
跨包导入¶
跨包导入是指一个 Vyper 包与另一个 Vyper 包之间的导入。
跨包导入**不能**请求目标包的根命名空间之外的任何内容。
跨包导入**可以使用**别名,如果这有助于可读性。
跨包导入**可以使用**
from [module] import [package]
语法。# Good from vyper.ast import fold from vyper import ast as vy_ast # Bad, do not import beyond the root namespace from vyper.ast.annotation import annotate_python_ast
异常¶
我们使用 自定义异常类 来指示编译期间发生了什么错误。
所有引发的异常**必须**使用适当描述错误的异常类。如果没有合适的类,或者当使用单个异常类来表示过于广泛的错误范围时,请考虑创建新的类。
不应故意引发内置 Python 异常。未处理的内置异常表示代码库中的错误。
对于不是由用户引起的错误,请使用
CompilerPanic
。
测试¶
我们使用 pytest 框架进行测试,以及 eth-tester 用于我们本地的开发链。
最佳实践¶
pytest
功能**不应**使用from ...
风格语法导入,尤其是pytest.raises
。导入库本身有助于可读性。测试**不应**相互依赖。我们使用
xdist
并行执行测试。你**不能**依赖于测试的执行顺序,或者两个测试在同一个进程中执行。测试用例**应该**以简约的方式设计。每个测试应该验证单个行为。好的测试是断言很少的测试,并且可以立即清楚地知道正在测试什么。
测试**不能**涉及模拟。
目录结构¶
在可能的情况下,测试套件**应该**复制主 Vyper 包的结构。例如,针对 vyper/context/types/
的测试用例应该存在于 tests/context/types/
中。
文件名¶
测试文件**必须**使用以下命名约定
test_[module].py
:当一个模块的所有测试都包含在一个文件中时。
test_[module]_[functionality].py
:当一个模块的测试分散在多个文件中时。
Fixture¶
Fixture**应该**存储在
conftest.py
中,而不是测试文件本身。
conftest.py
文件**不能**存在于初始tests/
目录之外的两个以上子目录中。Fixture 的功能**必须**完全记录,可以通过文档字符串或注释来实现。
文档¶
维护 Vyper 语言的全面和最新的文档非常重要。
文档**必须**准确反映 Github 上主分支的当前状态。
新功能**不能**在没有相应的文档更新的情况下添加。
写作风格¶
我们使用祈使句,现在时态来描述 API:“return”而不是“returns”。检验是否正确的一种方法是完成以下句子
“如果我们调用这个 API,它将:……”
对于叙事风格的文档,我们更倾向于使用第一人称“我们”形式,而不是第二人称“你”形式。
此外,我们在编写文档时**建议**遵循以下最佳实践
一致使用术语。
避免使用含糊的代词。
消除不必要的词语。
在文档开头建立要点。
每段只关注一个主题。
每个句子只关注一个想法。
当顺序很重要时使用编号列表,当顺序无关紧要时使用项目符号列表。
适当地引入列表和表格。
Google 的 技术写作课程 是一项宝贵的资源。我们在进行任何重大文档工作之前,建议您先学习这些课程。
API 指令¶
所有 API 文档**必须**使用标准 Python 指令。
在可能的情况下,对语法的引用**应该**使用适当的 Python 角色。
外部引用**可以**使用 intersphinx 角色。
内部文档¶
内部文档对于帮助其他贡献者理解 Vyper 代码库的布局至关重要。
我们通过以下方式处理内部文档
Vyper 包的每个一级子目录中**必须**包含一个
README.md
。readme 说明子目录的用途、组织和控制流程。所有公开的类和方法**必须**包含详细的文档字符串。
内部方法**应该**包含文档字符串,或者至少包含注释。
任何可能被认为是“巧妙的”或“神奇的”代码**必须**包含注释,解释正在发生的事情。
文档字符串**应该**根据 NumPy 文档字符串样式 进行格式化。
提交消息¶
贡献者在将提交合并到 Vyper 代码库时**应该**遵守以下标准和最佳实践。
维护者**可以**请求重新绑定,或者选择将不符合这些标准的拉取请求进行扁平化合并。
常规提交¶
提交消息**应该**遵循 常规提交 标准。常规提交消息的结构如下
<type>[optional scope]: <description>
[optional body]
[optional footer]
提交包含以下元素,以便向您的库的使用者传达意图
fix:类型为
fix
的提交修补了代码库中的错误(这与语义版本控制中的PATCH
相对应)。feat:类型为
feat
的提交向代码库引入了新功能(这与语义版本控制中的MINOR
相对应)。BREAKING CHANGE:在可选主体或页脚部分开头包含文本
BREAKING CHANGE:
的提交引入了破坏性 API 更改(与语义版本控制中的MAJOR
相对应)。BREAKING CHANGE 可以是任何类型的提交的一部分。
建议使用除 fix:
和 feat:
之外的其他提交类型。例如:docs:
,style:
,refactor:
,test:
,chore:
或 improvement:
。这些标签不受规范的强制,并且在语义版本控制中没有隐式影响。
最佳实践¶
我们**建议**遵循以下提交消息的最佳实践(摘自 如何编写提交消息)
将主题行限制在 50 个字符以内。
在主题行中使用祈使句,现在时态。
将主题行首字母大写。
不要在主题行末尾使用句号。
用空行分隔主题行和正文。
将正文换行至 72 个字符。
使用正文解释什么和为什么,而不是如何。
以下是一个符合上述实践的示例提交消息
Summarize changes in around 50 characters or less
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of the commit and the rest of the text as the body. The
blank line separating the summary from the body is critical (unless
you omit the body entirely); various tools like `log`, `shortlog`
and `rebase` can get confused if you run the two together.
Explain the problem that this commit is solving. Focus on why you
are making this change as opposed to how (the code explains that).
Are there side effects or other unintuitive consequences of this
change? Here's the place to explain them.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, preceded
by a single space, with blank lines in between, but conventions
vary here
If you use an issue tracker, put references to them at the bottom,
like this:
Resolves: #XXX
See also: #XXY, #XXXZ