贡献指南#
本文档重点介绍了cuDF中的最佳实践的高级概述。
目录结构和文件命名#
cuDF 通常提供与 pandas 相同的可导入模块和子包。
所有 Cython 代码都包含在 python/cudf/cudf/_lib 中。
代码风格#
cuDF 使用多种 linter 来确保代码库中的风格一致。
我们使用 pre-commit 来管理我们的 linter。
强烈建议开发者在进行任何开发之前设置 pre-commit。
仓库根目录下的 .pre-commit-config.yaml 文件是 linting 的主要依据。
具体来说,cuDF 使用以下工具:
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的使用通常由两部分组成:
一个
pxd文件,用于声明 C++ API,以便它们可以在 Cython 中使用,以及一个包含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 中 捕获警告 ,以便用户看不到这些警告。