贡献指南#

本文档重点介绍了cuDF中的最佳实践的高级概述。

目录结构和文件命名#

cuDF 通常提供与 pandas 相同的可导入模块和子包。 所有 Cython 代码都包含在 python/cudf/cudf/_lib 中。

代码风格#

cuDF 使用多种 linter 来确保代码库中的风格一致。 我们使用 pre-commit 来管理我们的 linter。 强烈建议开发者在进行任何开发之前设置 pre-commit。 仓库根目录下的 .pre-commit-config.yaml 文件是 linting 的主要依据。 具体来说,cuDF 使用以下工具:

  • ruff 检查代码格式是否符合一般规范。

  • mypy 执行静态类型检查。 结合 类型提示mypy 可以帮助捕捉各种难以发现的错误。

  • codespell 查找拼写错误。

Linter配置数据存储在多个文件中。 我们通常使用pyproject.toml而不是setup.cfg,并避免使用项目特定的文件(例如pyproject.toml > python/cudf/pyproject.toml)。 然而,工具和仓库中不同包之间的差异导致了以下注意事项:

  • isort 必须为每个项目进行配置,以设置哪个项目是“第一方”项目。

因此,我们目前同时维护根目录和项目级别的 pyproject.toml 文件。

有关如何使用预提交钩子的更多信息,请参阅整体贡献指南中的代码格式化部分。

弃用和移除代码#

cuDF遵循在移除代码之前先弃用一个版本的策略。 例如,如果我们在22.08发布周期决定移除一个API, 它将在22.08版本中被标记为弃用,并在22.10版本中被移除。 请注意,如果在注释中明确提到(如# Do not remove until..), 在满足注释中的条件之前,不要通过移除受影响的代码来强制执行弃用。 当API被弃用时,应移除cuDF中所有对弃用API的内部使用。 这可以防止用户在使用其他(未弃用的)API时遇到意外的弃用警告。 API的文档也应更新以反映其弃用状态。 当需要移除一个弃用的API时,确保移除所有相关的测试和文档。

弃用消息应:

  • 发出一个 FutureWarning;

  • 由一行组成,没有换行符;

  • 指示替换的API,如果存在的话 (弃用消息是向用户展示更好方法的机会);

  • 不指定移除发生的版本(这给了我们更多的灵活性)。

例如:

warnings.warn(
    "`Series.foo` is deprecated and will be removed in a future version of cudf. "
    "Use `Series.new_foo` instead.",
    FutureWarning
)

警告

弃用应该使用FutureWarning 而不是DeprecationWarning来发出信号! DeprecationWarning默认情况下是隐藏的,除非在__main__模块中运行的代码。

弃用也应该在相应的公共API文档字符串中使用deprecated警告来指定:

.. deprecated:: 23.08
    `foo` is deprecated and will be removed in a future version of cudf.

pandas 兼容性#

保持与pandas API的兼容性是cuDF的主要目标。 开发者在向cuDF添加新功能时,应始终参考pandas的API。 在引入与pandas类似的新cuDF API时,我们应尽可能与pandas保持一致。 由于我们尝试保持与各种边缘情况(如空值处理)的兼容性, 新的pandas版本有时需要做出破坏与旧版本兼容性的更改。 因此,我们的兼容性目标是最新的pandas版本。

然而,偶尔有充分的理由偏离pandas的行为。最常见的原因围绕性能。一些API无法在不产生过高运行时成本的情况下完全匹配pandas的行为。其他可能需要使用额外的内存,这在GPU工作流程中总是非常宝贵的。如果您正在开发一个功能,并认为完美的pandas兼容性不可行或不可取,您应该与团队的其他成员协商,评估如何继续。

当这种偏离pandas行为的情况是必要的时,应该进行记录。有关如何做到这一点的更多信息,请参阅我们关于pandas比较的文档

Python 与 Cython 对比#

cuDF 大量使用了 Cython。 Cython 是一个强大的工具,但它不如纯 Python 用户友好。 它也较难调试或分析。 因此,开发者在可能的情况下应优先选择 Python 代码而非 Cython。

Cython在cuDF中的主要用途是将libcudf C++ API暴露给Python。 这种Cython的使用通常由两部分组成:

  1. 一个 pxd 文件,用于声明 C++ API,以便它们可以在 Cython 中使用,以及

  2. 一个包含Cython函数的pyx文件,这些函数包装了那些C++ API,以便可以从Python调用它们。

后者的包装器通常应尽可能保持简洁,以最小化Cython的使用。 更多信息请参阅我们的Cython层设计文档

在某些罕见的情况下,我们实际上可能会从编写纯Cython代码中受益,以加速特定的代码路径。 然而,鉴于cuDF中的大多数数值计算实际上发生在libcudf中, 这样的用例相当罕见。 任何为此目的编写纯Cython代码的尝试都应通过基准测试来证明其合理性。

异常处理#

为了与保持与pandas的兼容性, cuDF与pandas共享的任何API在给定相同输入时应抛出与相应pandas API相同的所有异常。 然而,不需要匹配相应pandas API的异常消息。

在编写错误信息时,应包含足够的信息以帮助用户定位错误的来源,例如包括预期的参数类型与提供的实际参数。

对于尚未支持的参数,抛出NotImplementedError。不需要提及该参数将来何时会被支持。

处理 libcudf 异常#

标准C++原生支持各种异常类型, Cython将其映射到这些Python异常类型。 除了内置异常外,libcudf还会引发一些额外的异常类型。 cuDF扩展了Cython的默认映射以处理这些异常类型。 当添加新的libcudf异常类型时,应在cuDF的异常处理程序中添加适当的except子句。 如果没有内置的Python异常看起来合适,则应创建一个新的Python异常。

引发警告#

在适当的情况下,cuDF 应该抛出与 pandas 相同的警告。 例如,cuDF 中的 API 弃用应尽可能紧密地跟踪 pandas。 然而,并非所有 pandas 的警告都适用于 cuDF。 常见的例外情况包括 与实现相关的性能警告 这些警告不适用于 cuDF 的内部实现。 是否匹配 pandas 的决定必须根据具体情况做出,并留给开发者和 PR 审查者的最佳判断。

捕获来自依赖项的警告#

cuDF 开发者应避免使用来自包依赖的已弃用 API。 然而,在某些情况下,至少在短期内,可能无法避免使用这些 API。 如果出现这种情况,开发者应在 cuDF 中 捕获警告 ,以便用户看不到这些警告。