贡献指南#
我们衷心欢迎对项目的贡献,以使框架更加成熟并对大家更加有用。这些贡献可能以以下形式出现:
Bug 报告: 如果您发现任何 bug,请在 issue tracker 中报告。
功能请求: 请提出您希望在 discussions 中看到的新功能。
代码贡献: 请提交一个 pull request 。
错误修复
新特性
文档改进
教程和教程改进
我们更倾向于使用 GitHub discussions 来讨论想法、提问、交流和新功能的请求。
请仅使用 issue tracker 来跟踪具有明确范围和清晰交付物的可执行任务。这些任务可以是修复 bug、新功能或一般更新。
贡献代码#
注意
请参阅 Google Style Guide 以了解编码风格,然后再为代码库做贡献。在编码风格部分,我们概述了代码库中遵循的与风格指南的具体偏差。
我们使用 GitHub 进行代码托管。请按照以下步骤贡献代码:
在 issue tracker 中创建一个问题,讨论您希望进行的更改或添加内容。这有助于我们避免重复工作,并确保这些更改与项目的路线图保持一致。
Fork 仓库。
创建一个新分支用于您的更改。
进行更改并提交它们。
将您的更改推送到您的fork。
提交一个拉取请求到 main分支 。
确保对拉取请求模板执行所有检查。
在发送拉取请求之后,维护者将审查你的代码并提供反馈。
请确保您的代码格式良好、文档齐全,并且通过所有测试。
小技巧
保持拉取请求尽可能小是很重要的。这使得维护者更容易审查你的代码。如果你正在进行多个更改,请发送多个拉取请求。大型拉取请求很难审查,并且可能需要很长时间才能合并。
贡献文档#
为文档贡献的方式与为代码库贡献一样简单。所有文档的源文件都位于``IsaacLab/docs``目录中。文档使用`reStructuredText <https://docutils.sourceforge.io/rst.html>`__格式编写。
我们使用 Sphinx 和 Book Theme 来维护文档。
发送文档的拉取请求与发送代码库的拉取请求相同。请按照 Contributing Code 部分中提到的步骤进行操作。
小心
为了构建文档,我们推荐创建一个 virtual environment 来安装依赖项。这也可以是一个 conda environment 。
要构建文档,请在终端中运行以下命令,该命令将安装所需的 python 包并使用 docs/Makefile
构建文档:
./isaaclab.sh --docs # or "./isaaclab.sh -d"
文档生成在 docs/_build
目录中。要查看文档,请打开 html
目录中的 index.html
文件。这可以通过在终端中运行以下命令来完成:
xdg-open docs/_build/current/index.html
提示
xdg-open
命令用于在默认浏览器中打开 index.html
文件。如果您使用的是不同的操作系统,可以使用适当的命令在浏览器中打开该文件。
要进行干净的构建,请在终端中运行以下命令:
rm -rf docs/_build && ./isaaclab.sh --docs
贡献资产#
目前,我们将扩展的资源托管在 NVIDIA Nucleus Server 上。Nucleus 是一种基于云的存储服务,允许用户存储和共享大型文件。它与 NVIDIA Omniverse Platform 集成。
由于所有资产都托管在 Nucleus 上,我们不需要将它们包含在代码库中。然而,我们需要在文档中包含指向资产的链接。
包含的资产是 Isaac Sim Content 的一部分。要使用这些内容,您可以使用 Isaac Sim 中提供的资产浏览器。
请查看 Isaac Sim documentation 以获取有关如何下载资产的更多信息。
注意
我们目前正在研究一种更好的方式来贡献资产。一旦我们有了解决方案,我们将更新此部分。在此期间,请按照下面提到的步骤进行操作。
要托管您自己的资源,当前的解决方案是:
创建一个单独的存储库用于资产,并将其添加到那里
确保资产已获得使用和分发的许可
在仓库的 README 文件中包含资产的图片
发送一个包含仓库链接的拉取请求
我们将验证资产及其许可,并将资产包含到 Nucleus 服务器中进行托管。如果您有任何问题,请随时通过电子邮件或在存储库中开启问题来联系我们。
维护changelog和extension.toml#
每个扩展都在 docs
目录中的 CHANGELOG.rst
文件中维护变更日志,并在 config
目录中有一个 extension.toml
文件。
extension.toml
文件包含扩展的元数据。它用于描述扩展的名称、版本、描述以及其他元数据。
CHANGELOG.rst
是一个文件,包含了每个版本扩展的经过整理、按时间顺序排列的显著更改列表。
备注
extension.toml
文件中的版本号应根据 Semantic Versioning 更新,并应与 CHANGELOG.rst
文件中的版本号匹配。
变更日志文件是以 reStructuredText 格式编写的。此变更日志的目标是帮助用户和贡献者精确地看到每个版本(或版本之间)的显著变化。这对于每个扩展来说都是*必须*的。
为了更新变更日志,请遵循以下指南:
每个版本应有一个包含版本号和发布日期的章节。
版本号根据 Semantic Versioning 进行更新。发布日期是版本发布的日期。
每个版本根据所做更改的类型划分为子部分。
Added
: 新功能。Changed
: 现有功能的更改。Deprecated
: 即将删除的功能。Removed
: 当前移除的功能。Fixed
: 用于任何错误修复。
每个变化都在其对应的小节中以项目符号形式描述。
项目要点采用 过去时 编写。
这意味着这个变化被描述为已经发生过。
项目要简明扼要,直奔主题。不应冗长。
项目符号应当包括变更的原因,如果适用。
小技巧
有疑问时,请检查现有的 changelog 文件中的样式,并遵循相同的样式。
例如,以下是一个示例的更新日志:
Changelog
---------
0.1.0 (2021-02-01)
~~~~~~~~~~~~~~~~~~
Added
^^^^^
* Added a new feature that helps in a 10x speedup.
Changed
^^^^^^^
* Changed an existing feature. Earlier, we were using :meth:`torch.bmm` to perform the matrix multiplication.
However, this was slow for large matrices. We have now switched to using :meth:`torch.einsum` which is
significantly faster.
Deprecated
^^^^^^^^^^
* Deprecated an existing feature in favor of a new feature.
Removed
^^^^^^^
* Removed an existing feature. This was done to simplify the codebase and reduce the complexity.
Fixed
^^^^^
* Fixed crashing of the :meth:`my_function` when the input was too large.
We now use :meth:`torch.einsum` that is able to handle larger inputs.
编码风格#
我们遵循 Google Style Guides 作为代码库的规范。对于 Python 代码,遵循 PEP 指南。最重要的包括 PEP-8 用于代码注释和布局, PEP-484 和 PEP-585 用于类型提示。
对于文档,我们采用 Google Style Guide 来编写 docstring。我们使用 Sphinx 来生成文档。请确保您的代码有良好的文档说明并遵循指南。
循环导入#
循环导入发生在两个模块相互导入时,这是 Python 中常见的问题。您可以通过遵循在此 StackOverflow post 中概述的最佳实践来防止循环导入。
一般来说,避免循环导入是至关重要的,因为它们可能导致不可预测的行为。
然而,在我们的代码库中,我们在子包级别遇到了循环导入。这种情况是由于我们特定的代码结构所导致的。我们将类或函数及其相应的配置对象组织到不同的文件中。这种分离增强了代码的可读性和可维护性。然而,它可能导致循环导入,因为在许多配置对象中,我们使用属性 class_type
和 func
分别指定类或函数作为默认值。
为了处理循环导入问题,我们利用 typing.TYPE_CHECKING 变量。该特殊变量仅在类型检查时评估,从而使我们能够在配置对象中导入类或函数,而不会触发循环导入。
需要注意的是,这是我们代码库中唯一使用并且可以接受循环导入的实例。在所有其他情况下,我们遵循最佳实践,并建议您也这样做。
类型提示#
为了提高代码的可读性,我们对所有的函数和类使用了 type hints 。这有助于理解代码,并使得维护更加容易。遵循这一做法还可以帮助我们通过像 mypy 这样的静态类型检查工具尽早发现 bug。
仅在函数签名中使用类型提示
为了避免重复工作,我们在文档字符串中不为参数和返回值指定类型提示。
例如,以下是由于各种原因导致的不良示例:
def my_function(a, b):
"""Adds two numbers.
This function is a bad example. Reason: No type hints anywhere.
Args:
a: The first argument.
b: The second argument.
Returns:
The sum of the two arguments.
"""
return a + b
def my_function(a, b):
"""Adds two numbers.
This function is a bad example. Reason: Type hints in the docstring and not in the
function signature.
Args:
a (int): The first argument.
b (int): The second argument.
Returns:
int: The sum of the two arguments.
"""
return a + b
def my_function(a: int, b: int) -> int:
"""Adds two numbers.
This function is a bad example. Reason: Type hints in the docstring and in the function
signature. Redundancy.
Args:
a (int): The first argument.
b (int): The second argument.
Returns:
int: The sum of the two arguments.
"""
return a + b
以下是我们期望您编写文档字符串和类型提示的方式:
def my_function(a: int, b: int) -> int:
"""Adds two numbers.
This function is a good example. Reason: Type hints in the function signature and not in the
docstring.
Args:
a: The first argument.
b: The second argument.
Returns:
The sum of the two arguments.
"""
return a + b
不对 None 进行类型提示
我们在文档字符串中不指定 None
的返回类型。这是因为它不是必需的,并且可以从函数签名中推断出来。
例如,以下是一个不好的例子:
def my_function(x: int | None) -> None:
pass
相反,我们推荐以下内容:
def my_function(x: int | None):
pass
编写代码文档#
代码文档与代码本身同样重要。它有助于理解代码,并使维护变得更加容易。然而,更常见的情况是,文档往往是事后的思考,或者为了跟上开发进度而匆忙完成。
什么被认为是糟糕的文档?
如果其他人想使用这段代码,他们仅通过阅读文档是无法理解代码的。
这意味着文档不完整,或者没有以易于理解的方式编写。下次有人想使用代码时,他们将不得不花时间理解代码(在最佳情况下),或者丢弃代码并从头开始(在最坏情况下)。
某些设计细节没有文档化,只能从代码中看出。
通常会做出某些设计决策,以解决特定的使用场景。这些使用场景对想要使用代码的人来说并不明显。他们可能以一种不直观的方式修改代码,并无意中破坏代码。
文档在代码更新时没有更新。
这意味着文档没有随着代码的更新而保持最新。重要的是,在代码更新时更新文档。这有助于保持文档的最新状态,并与代码保持同步。
什么是被认为是好的文档?
我们建议将代码文档视为一个活文档,帮助读者理解代码的 what, why 和 how。我们经常看到的文档只解释了 what,而没有解释 how 或 why。这在长远来看并没有帮助。
我们建议始终从新用户的角度思考文档。他们应该能够直接查看文档并且对代码有一个清晰的理解。
有关如何编写优秀文档的信息,请查看 Dart’s effective documentation 和 technical writing 上的说明。我们在下面总结了关键要点:
告知(教育读者)并说服(说服读者)。 * 心中要有明确的目标,并确保你写的每一件事都只是为了这个目标。 * 在引入抽象概念之前,先使用示例和类比。
使用适合受众的语气。
组成简单的主动语态句子。
避免不必要的术语和重复。使用简单英语。
避免使用模糊的词语,如 ‘kind of’, ‘sort of’, ‘a bit’ 等。
在句子的开头陈述重要信息。
说出你真正想表达的意思。不要回避写下令人不舒服的真相。
单元测试#
我们使用 unittest 进行单元测试。好的测试不仅覆盖代码的基本功能,还要涵盖边界情况。它们应该能够捕捉回归,并确保代码按预期工作。请确保为您的更改添加测试。
工具#
我们使用以下工具来维护代码质量:
pre-commit: 在代码库上运行格式化工具和代码检查工具的列表。
black: 不妥协的代码格式化工具。
flake8: 一个 PyFlakes、pycodestyle 和 McCabe 复杂度检查器的封装器。
请检查 这里 以获取设置这些的说明。要在整个仓库中运行,请在终端中执行以下命令:
./isaaclab.sh --format # or "./isaaclab.sh -f"