贡献代码库#
代码规范#
编写优秀的代码不仅仅在于你写了什么。还在于你 *如何* 编写它。在 持续集成 测试期间,将运行多个工具来检查你的代码是否存在风格错误。任何警告都会导致测试失败。因此,良好的风格是向 pandas 提交代码的要求。
pandas 中有几种工具可以帮助贡献者在向项目贡献代码之前验证他们的更改。
./ci/code_checks.sh:一个脚本,用于验证 doctests、docstring 中的格式以及导入的模块。可以通过使用参数docstrings、code和doctests来独立运行检查(例如./ci/code_checks.sh doctests);pre-commit,我们将在下一节中详细介绍。
此外,由于有很多人使用我们的库,因此我们不能对代码进行可能导致大量用户代码中断的突然更改,也就是说,我们需要它尽可能地 *向后兼容*,以避免大规模中断。
提交前的检查#
此外,持续集成 将使用 pre-commit hooks 运行代码格式检查,例如 ruff、isort 和 clang-format 等。这些检查的任何警告都会导致 持续集成 失败;因此,在提交代码之前自行运行检查很有帮助。这可以通过安装 pre-commit(如果您遵循了 设置开发环境 中的说明,则应该已经安装了)然后运行来实现。
pre-commit install
从 pandas 仓库的根目录运行。现在,每次提交更改时都会运行所有样式检查,而无需手动运行每个检查。此外,使用 pre-commit 还可以让您更轻松地跟上代码检查的最新变化。
请注意,如果需要,您可以使用 git commit --no-verify 跳过这些检查。
如果您不想在工作流程中使用 pre-commit,仍然可以使用它来运行其检查,使用以下命令之一。
pre-commit run --files <files you have modified>
pre-commit run --from-ref=upstream/main --to-ref=HEAD --all-files
无需事先运行 pre-commit install。
最后,我们还有一些耗时较长的提交前检查,它们不会在每次提交时运行,但在持续集成期间会运行。您可以通过以下方式手动触发它们。
pre-commit run --hook-stage manual --all-files
注意
您可能需要定期运行 pre-commit gc,以清理不再使用的仓库。
注意
如果您有 virtualenv 的冲突安装,则可能会收到错误 - 请参阅 此处。
另外,由于 virtualenv 中的一个 bug,如果您使用 conda,可能会遇到问题。要解决此问题,您可以将 virtualenv 降级到版本 20.0.33。
注意
如果您最近从上游分支合并了 main,那么 pre-commit 使用的一些依赖项可能已发生更改。请确保 更新您的开发环境。
可选依赖项#
可选依赖项(例如 matplotlib)应使用私有助手 pandas.compat._optional.import_optional_dependency 进行导入。这可确保在未满足依赖项时显示一致的错误消息。
所有使用可选依赖项的方法都应包含一个测试,断言当找不到可选依赖项时会引发 ImportError。如果库存在,则应跳过此测试。
所有可选依赖项都应在 可选依赖项 中进行文档化,并且最低必需版本应在 pandas.compat._optional.VERSIONS 字典中设置。
向后兼容性#
请尽量保持向后兼容性。pandas 有大量用户和大量现有代码,所以如果可能的话,不要破坏它。如果您认为破坏是必要的,请在 pull request 中清楚说明原因。此外,在更改方法签名时要小心,并在需要时添加弃用警告。另外,将弃用的 sphinx 指令添加到已弃用的函数或方法中。
如果存在与正在弃用的函数参数相同的函数,您可以使用 pandas.util._decorators.deprecate。
from pandas.util._decorators import deprecate
deprecate('old_func', 'new_func', '1.1.0')
否则,您需要手动执行此操作。
import warnings
from pandas.util._exceptions import find_stack_level
def old_func():
"""Summary of the function.
.. deprecated:: 1.1.0
Use new_func instead.
"""
warnings.warn(
'Use new_func instead.',
FutureWarning,
stacklevel=find_stack_level(),
)
new_func()
def new_func():
pass
您还需要:
编写一个新的测试,断言在使用已弃用的参数调用时会发出警告。
更新 pandas 所有现有的测试和代码以使用新参数。
更多信息请参阅 测试警告。
类型提示#
pandas 强烈鼓励使用 PEP 484 风格的类型提示。新开发应包含类型提示,并且也接受用于注释现有代码的 pull request!
风格指南#
类型导入应遵循 from typing import ... 约定。您的代码可能会被自动重写,以使用一些现代构造(例如,使用内置的 list 而不是 typing.List),由 提交前的检查 完成。
在某些情况下,代码库中的类可能定义了会覆盖内置类型的类变量。这会引起一个问题,如 Mypy 1775 中所述。这里防御性的解决方案是创建一个内置类型的无歧义别名,并在不带注释的情况下使用它。例如,如果您遇到以下定义:
class SomeClass1:
str = None
正确的注释方式如下:
str_type = str
class SomeClass2:
str: str_type = None
在某些情况下,您可能会想在知道得比分析器更清楚时,使用 typing 模块中的 cast。这尤其发生在您使用自定义推断函数时。例如:
from typing import cast
from pandas.core.dtypes.common import is_number
def cannot_infer_bad(obj: Union[str, int, float]):
if is_number(obj):
...
else: # Reasonably only str objects would reach this but...
obj = cast(str, obj) # Mypy complains without this!
return obj.upper()
这里的限制是,虽然人类可以合理地理解 is_number 会捕获 int 和 float 类型,但 mypy 目前无法做出相同的推断(参见 mypy #5206)。虽然上述方法有效,但 *强烈不鼓励* 使用 cast。在可能的情况下,重构代码以适应静态分析是更可取的。
def cannot_infer_good(obj: Union[str, int, float]):
if isinstance(obj, str):
return obj.upper()
else:
...
对于自定义类型和推断,这并不总是可能的,因此会做出例外,但在走这条路之前,应尽一切努力避免使用 cast。
pandas 特有类型#
pandas 特有的常用类型将出现在 pandas._typing 中,并且您应该在适用时使用它们。此模块是私有的,仅用于 pandas 开发。供用户使用的类型应在 pandas.api.typing.aliases 中公开,并最好添加到 pandas-stubs 项目中。
例如,pandas 中有许多函数接受 dtype 参数。这可以表示为字符串(如 "object")、numpy.dtype(如 np.int64)甚至 pandas ExtensionDtype(如 pd.CategoricalDtype)。为了避免用户必须不断地注释所有这些选项,可以直接从 pandas._typing 模块导入并重用它。
from pandas._typing import Dtype
def as_type(dtype: Dtype) -> ...:
...
此模块最终将包含用于重复使用的概念的类型,如“类路径”(path-like)、“类数组”(array-like)、“数值”(numeric)等,并且还可以包含用于常见参数的别名,如 axis。此模块的开发是活跃的,因此请务必参考源代码以获取最新的可用类型列表。
验证类型提示#
pandas 使用 mypy 和 pyright 来静态分析代码库和类型提示。进行任何更改后,您可以通过运行以下命令来确保您的类型提示一致:
pre-commit run --hook-stage manual --all-files mypy
pre-commit run --hook-stage manual --all-files pyright
pre-commit run --hook-stage manual --all-files pyright_reportGeneralTypeIssues
# the following might fail if the installed pandas version does not correspond to your local git version
pre-commit run --hook-stage manual --all-files stubtest
在您的 python 环境中。
警告
请注意,上述命令将使用当前 python 环境。如果您的 python 包比 pandas CI 安装的旧/新,上述命令可能会失败。这通常发生在
mypy或numpy版本不匹配时。请参阅 如何设置 python 环境 或选择一个 最近成功的流程,选择“Docstring validation, typing, and other manual pre-commit hooks”作业,然后单击“Set up Conda”和“Environment info”以查看 pandas CI 安装的版本。
使用 pandas 测试类型提示#
警告
pandas 尚未成为一个 py.typed 库(PEP 561)!在本地将 pandas 声明为 py.typed 库的主要目的是测试和改进 pandas 内置的类型注解。
在 pandas 成为 py.typed 库之前,可以通过在 pandas 安装文件夹中创建一个名为“py.typed”的空文件来轻松尝试 pandas 附带的类型注解。
python -c "import pandas; import pathlib; (pathlib.Path(pandas.__path__[0]) / 'py.typed').touch()"
py.typed 文件的存在向类型检查器表明 pandas 已经是 py.typed 库。这使得类型检查器能够识别 pandas 附带的类型注解。
通过持续集成测试#
一旦提交了 pull request,pandas 测试套件将自动在 GitHub Actions 持续集成服务上运行。但是,如果您希望在提交 pull request 之前在分支上运行测试套件,那么需要将持续集成服务连接到您的 GitHub 仓库。GitHub Actions 的说明在此处:GitHub Actions。
当所有构建都显示“绿色”时,pull request 将被考虑合并。如果任何测试失败,您将看到一个红色的“X”,您可以点击它来查看单个失败的测试。这是一个绿色构建的示例。
测试驱动开发#
pandas 非常重视测试,并强烈鼓励贡献者采用 测试驱动开发(TDD)。这种开发过程“依赖于一个非常短的开发周期:首先,开发人员编写一个(最初失败的)自动化测试用例,该用例定义了期望的改进或新功能,然后生成最少量的代码来通过该测试。” 因此,在实际编写任何代码之前,您应该编写您的测试。通常,测试可以从原始 GitHub issue 中获取。但是,始终值得考虑额外的用例并编写相应的测试。
我们使用 代码覆盖率 来帮助理解被测试覆盖的代码量。我们建议努力确保您在 Pandas 中添加或更改的代码被测试覆盖。请参阅我们的 Codecov 代码覆盖率仪表板 以获取更多信息。
添加测试是代码推送到 pandas 后最常见的请求之一。因此,值得养成提前编写测试的习惯,这样就不会有问题。
编写测试#
所有测试都应放在特定包的 tests 子目录中。此文件夹包含许多当前测试示例,我们建议从中获取灵感。
作为一个通用技巧,您可以使用集成开发环境(IDE)中的搜索功能,或在终端中使用 git grep 命令来查找方法被调用的测试文件。如果您不确定将测试放在哪里最合适,请进行最佳猜测,但请注意,审查者可能会要求您将测试移动到其他位置。
要使用 git grep,您可以在终端中运行以下命令:
git grep "function_name("
这将搜索您仓库中的所有文件以查找文本 function_name(。这是一种快速定位代码库中函数并确定添加测试的最佳位置的有用方法。
理想情况下,应该只有一个、并且只有一个明确的测试位置。在我们达到这个理想之前,以下是一些关于测试应位于何处的经验法则。
您的测试是否仅依赖于
pd._libs.tslibs中的代码?此测试可能属于以下之一:tests.tslibs
注意
在
tests.tslibs中,任何文件都不应导入pd._libs.tslibs之外的任何 pandas 模块。tests.scalar
tests.tseries.offsets
您的测试是否仅依赖于
pd._libs中的代码?此测试可能属于以下之一:tests.libs
tests.groupby.test_libgroupby
您的测试是否针对算术或比较方法?此测试可能属于以下之一:
tests.arithmetic
注意
这些用于可以共享的测试,以使用
box_with_arrayfixture 来测试 DataFrame/Series/Index/ExtensionArray 的行为。tests.frame.test_arithmetic
tests.series.test_arithmetic
您的测试是否针对归约方法(min、max、sum、prod 等)?此测试可能属于以下之一:
tests.reductions
注意
这些用于可以共享的测试,以测试 DataFrame/Series/Index/ExtensionArray 的行为。
tests.frame.test_reductions
tests.series.test_reductions
tests.test_nanops
您的测试是否针对索引方法?这是确定测试位置最困难的情况,因为有许多此类测试,而且其中许多测试会测试多个方法(例如,同时测试
Series.__getitem__和Series.loc.__getitem__)。测试是否专门测试 Index 方法(例如
Index.get_loc、Index.get_indexer)?此测试可能属于以下之一:tests.indexes.test_indexing
tests.indexes.fooindex.test_indexing
在这些文件中,应该有一个特定于方法的测试类,例如
TestGetLoc。在大多数情况下,这些测试中都不应需要
Series或DataFrame对象。测试是否针对 Series 或 DataFrame 索引方法 *除了*
__getitem__或__setitem__,例如xs、where、take、mask、lookup或insert?此测试可能属于以下之一:tests.frame.indexing.test_methodname
tests.series.indexing.test_methodname
测试是否针对
loc、iloc、at或iat?此测试可能属于以下之一:tests.indexing.test_loc
tests.indexing.test_iloc
tests.indexing.test_at
tests.indexing.test_iat
在相应的文件中,测试类对应于索引类型(例如
TestLocBooleanMask)或主要用例(例如TestLocSetitemWithExpansion)。有关测试多个索引方法的测试,请参阅 D 部分的注释。
测试是否针对
Series.__getitem__、Series.__setitem__、DataFrame.__getitem__或DataFrame.__setitem__?此测试可能属于以下之一:tests.series.test_getitem
tests.series.test_setitem
tests.frame.test_getitem
tests.frame.test_setitem
如果许多情况下的测试可能测试多个相似的方法,例如:
import pandas as pd import pandas._testing as tm def test_getitem_listlike_of_ints(): ser = pd.Series(range(5)) result = ser[[3, 4]] expected = pd.Series([2, 3]) tm.assert_series_equal(result, expected) result = ser.loc[[3, 4]] tm.assert_series_equal(result, expected)
在这种情况下,测试位置应基于正在测试的 *底层* 方法。或者,如果是针对 bug 修复的测试,则是实际 bug 的位置。因此,在此示例中,我们知道
Series.__getitem__调用Series.loc.__getitem__,因此这 *实际上* 是针对loc.__getitem__的测试。因此,此测试属于tests.indexing.test_loc。
您的测试是否针对 DataFrame 或 Series 方法?
该方法是绘图方法吗?此测试可能属于以下之一:
tests.plotting
该方法是 IO 方法吗?此测试可能属于以下之一:
tests.io
注意
这包括
to_string,但不包括__repr__,它在tests.frame.test_repr和tests.series.test_repr中进行测试。其他类通常有一个test_formats文件。
否则,此测试可能属于以下之一:
tests.series.methods.test_mymethod
tests.frame.methods.test_mymethod
注意
如果一个测试可以通过
frame_or_seriesfixture 在 DataFrame/Series 之间共享,按照惯例,它会放在tests.frame文件中。
您的测试是否针对 Index 方法,且不依赖于 Series/DataFrame?此测试可能属于以下之一:
tests.indexes
您的测试是否针对 pandas 提供的任何 ExtensionArrays(
Categorical、DatetimeArray、TimedeltaArray、PeriodArray、IntervalArray、NumpyExtensionArray、FloatArray、BoolArray、StringArray)?此测试可能属于以下之一:tests.arrays
您的测试是否针对 *所有* ExtensionArray 子类(“EA 接口”)?此测试可能属于以下之一:
tests.extension
使用 pytest#
测试结构#
pandas 现有的测试结构*主要*是基于类的,这意味着您通常会发现测试被封装在类中。
class TestReallyCoolFeature:
def test_cool_feature_aspect(self):
pass
我们更喜欢使用 pytest 框架的*函数式*风格,它提供了一个更丰富的测试框架,有助于测试和开发。因此,我们不会编写测试类,而是编写函数式测试,如下所示:
def test_really_cool_feature():
pass
推荐的 pytest 惯用法#
函数式测试的名称为
def test_*,并且*仅*接受是 fixture 或参数的参数。使用裸
assert来测试标量和真值测试。分别使用
tm.assert_series_equal(result, expected)和tm.assert_frame_equal(result, expected)来比较Series和DataFrame的结果。当测试多个用例时,使用 @pytest.mark.parameterize。
当测试用例预计会失败时,使用 pytest.mark.xfail。
当测试用例*永远*不会通过时,使用 pytest.mark.skip。
当测试用例需要特定标记时,使用 pytest.param。
如果多个测试可以共享一个设置对象,则使用 @pytest.fixture。
警告
请勿使用 pytest.xfail(这与 pytest.mark.xfail 不同),因为它会立即停止测试并且不会检查测试是否会失败。如果您需要这种行为,请改用 pytest.skip。
如果一个测试已知会失败,但失败的方式不是为了被捕获,则使用 pytest.mark.xfail。这通常用于表现出 bug 行为或未实现功能的测试。如果失败的测试具有不稳定的行为,请使用参数 strict=False。这将使 pytest 在测试碰巧通过时不会失败。*非常不推荐*使用 strict=False,请仅作为最后的手段使用。
请优先使用装饰器 @pytest.mark.xfail 和参数 pytest.param,而不是在测试中使用它们,以便在 pytest 的收集阶段正确标记测试。对于涉及多个参数、fixture 或这些组合的 xfail 测试,只能在测试阶段进行 xfail。为此,请使用 request fixture。
def test_xfail(request):
mark = pytest.mark.xfail(raises=TypeError, reason="Indicate why here")
request.applymarker(mark)
xfail 不应用于涉及无效用户参数的失败测试。对于这些测试,我们需要使用 pytest.raises 来验证是否引发了正确的异常类型和错误消息。
测试警告#
使用 tm.assert_produces_warning 作为上下文管理器来检查代码块是否引发了警告,并使用 match 参数指定警告消息。
with tm.assert_produces_warning(DeprecationWarning, match="the warning message"):
pd.deprecated_function()
如果某个警告在代码块中不应发生,请将 False 传递给上下文管理器。
with tm.assert_produces_warning(False):
pd.no_warning_function()
如果您有一个会发出警告的测试,但您实际上并不测试警告本身(例如,因为它将在未来被删除,或者因为我们正在匹配第三方库的行为),请使用 pytest.mark.filterwarnings 来忽略错误。
@pytest.mark.filterwarnings("ignore:msg:category")
def test_thing(self):
pass
测试异常#
使用 pytest.raises 作为上下文管理器,并指定具体的异常子类(即,*切勿* 使用 Exception)以及 match 中的异常消息。
with pytest.raises(ValueError, match="an error"):
raise ValueError("an error")
涉及文件的测试#
temp_file pytest fixture 会创建一个临时的文件 Pathlib 对象用于测试。
def test_something(temp_file):
pd.DataFrame([1]).to_csv(str(temp_file))
有关文件保留策略,请参阅 pytest 的文档。
涉及网络连接的测试#
单元测试不应通过 Internet 访问公共数据集,因为网络连接不稳定且不拥有所连接的服务器。要模拟此交互,请使用 pytest-localserver 插件 中的 httpserver fixture,并使用合成数据。
@pytest.mark.network
@pytest.mark.single_cpu
def test_network(httpserver):
httpserver.serve_content(content="content")
result = pd.read_html(httpserver.url)
示例#
这里是一个自包含的测试集示例,位于文件 pandas/tests/test_cool_feature.py 中,演示了我们喜欢使用的多个特性。请记住,在添加新的测试时,请将 GitHub Issue 的编号作为注释添加。
import pytest
import numpy as np
import pandas as pd
@pytest.mark.parametrize('dtype', ['int8', 'int16', 'int32', 'int64'])
def test_dtypes(dtype):
assert str(np.dtype(dtype)) == dtype
@pytest.mark.parametrize(
'dtype', ['float32', pytest.param('int16', marks=pytest.mark.skip),
pytest.param('int32', marks=pytest.mark.xfail(
reason='to show how it works'))])
def test_mark(dtype):
assert str(np.dtype(dtype)) == 'float32'
@pytest.fixture
def series():
return pd.Series([1, 2, 3])
@pytest.fixture(params=['int8', 'int16', 'int32', 'int64'])
def dtype(request):
return request.param
def test_series(series, dtype):
# GH <issue_number>
result = series.astype(dtype)
assert result.dtype == dtype
expected = pd.Series([1, 2, 3], dtype=dtype)
tm.assert_series_equal(result, expected)
运行此命令会得到:
((pandas) bash-3.2$ pytest test_cool_feature.py -v
=========================== test session starts ===========================
platform darwin -- Python 3.6.2, pytest-3.6.0, py-1.4.31, pluggy-0.4.0
collected 11 items
tester.py::test_dtypes[int8] PASSED
tester.py::test_dtypes[int16] PASSED
tester.py::test_dtypes[int32] PASSED
tester.py::test_dtypes[int64] PASSED
tester.py::test_mark[float32] PASSED
tester.py::test_mark[int16] SKIPPED
tester.py::test_mark[int32] xfail
tester.py::test_series[int8] PASSED
tester.py::test_series[int16] PASSED
tester.py::test_series[int32] PASSED
tester.py::test_series[int64] PASSED
现在可以通过测试名称访问我们 参数化 的测试,例如,我们可以使用 -k int8 来运行它们,以 *仅* 选出与 int8 匹配的那些测试。
((pandas) bash-3.2$ pytest test_cool_feature.py -v -k int8
=========================== test session starts ===========================
platform darwin -- Python 3.6.2, pytest-3.6.0, py-1.4.31, pluggy-0.4.0
collected 11 items
test_cool_feature.py::test_dtypes[int8] PASSED
test_cool_feature.py::test_series[int8] PASSED
使用 hypothesis#
Hypothesis 是一个基于属性的测试库。与其显式地参数化测试,不如描述 *所有* 有效的输入,让 Hypothesis 尝试找到一个失败的输入。更好的是,无论它尝试多少随机示例,Hypothesis 始终报告一个最小的反例给您的断言——通常是一个您从未想过要测试的示例。
有关更多介绍,请参阅 Hypothesis 入门指南,然后 参考 Hypothesis 文档了解详情。
import json
from hypothesis import given, strategies as st
any_json_value = st.deferred(lambda: st.one_of(
st.none(), st.booleans(), st.floats(allow_nan=False), st.text(),
st.lists(any_json_value), st.dictionaries(st.text(), any_json_value)
))
@given(value=any_json_value)
def test_json_roundtrip(value):
result = json.loads(json.dumps(value))
assert value == result
此测试展示了 Hypothesis 的几个有用功能,同时演示了一个好的用例:检查在大量或复杂的输入域中应成立的属性。
为了使 pandas 的测试套件运行得更快,当输入或逻辑简单时,优先使用参数化测试,而将 Hypothesis 测试保留用于逻辑复杂的情况,或者当选项组合过多或相互作用微妙以至于无法测试(或考虑)所有情况时。
运行测试套件#
然后,您可以通过键入以下命令直接在 Git 克隆的副本中运行测试(无需安装 pandas):
pytest pandas
注意
如果少数测试未通过,可能不是您的 pandas 安装有问题。某些测试(例如,一些 SQLAlchemy 的测试)需要额外的设置,有些测试可能会因为非固定版本的库发布了新版本而开始失败,还有一些测试在并行运行时可能会不稳定。只要您能从本地构建的版本中导入 pandas,那么您的安装很可能没问题,您就可以开始贡献了!
通常,在运行整个套件之前,先只运行您更改周围的测试子集是值得的。
最简单的方法是使用
pytest pandas/path/to/test.py -k regex_matching_test_name
或使用以下构造之一
pytest pandas/tests/[test-module].py
pytest pandas/tests/[test-module].py::[TestClass]
pytest pandas/tests/[test-module].py::[TestClass]::[test_method]
使用 pytest-xdist(已包含在我们的“pandas-dev”环境中),可以在多核机器上加速本地测试。然后,在运行 pytest 时可以使用 -n 标志指定并行运行的核数,或者使用 auto 来利用您机器上的所有可用核。
# Utilize 4 cores
pytest -n 4 pandas
# Utilizes all available cores
pytest -n auto pandas
如果您想进一步加快速度,此命令的高级用法如下所示:
pytest pandas -n 4 -m "not slow and not network and not db and not single_cpu" -r sxX
除了多线程性能的提升外,这还可以通过使用 -m 标记标志来跳过某些测试,从而提高测试速度。
slow:任何耗时长的测试(以秒而非毫秒计)
network:需要网络连接的测试
db:需要数据库(mysql 或 postgres)的测试
single_cpu:仅应在单个 CPU 上运行的测试
如果以下选项与您相关,您可能需要启用它们:
arm_slow:在 arm64 架构上耗时长的任何测试
这些标记在 此 toml 文件 中定义,位于 [tool.pytest.ini_options] 下的 markers 列表中,以防您想检查是否有新创建的、令您感兴趣的标记。
使用 -r 报告标志将显示一个简短的摘要信息(请参阅 pytest 文档)。这里我们显示了
s:跳过的测试
x:xfailed(预期失败)的测试
X:xpassed(预期通过)的测试
摘要是可选的,如果您不需要额外信息,可以删除它。使用并行化选项可以显著减少在提交拉取请求之前本地运行测试所需的时间。
如果您需要有关结果的帮助(过去曾发生过这种情况),请在运行命令并打开 bug 报告之前设置一个种子,这样我们就可以重现它。以下是在 Windows 上设置种子的示例:
set PYTHONHASHSEED=314159265
pytest pandas -n 4 -m "not slow and not network and not db and not single_cpu" -r sxX
在 Unix 上使用:
export PYTHONHASHSEED=314159265
pytest pandas -n 4 -m "not slow and not network and not db and not single_cpu" -r sxX
更多信息,请参阅 pytest 文档。
此外,还可以运行:
pd.test()
并使用导入的 pandas 来运行类似的测试。
运行性能测试套件#
性能很重要,值得考虑您的代码是否引入了性能回归。pandas 正在迁移到 asv benchmark,以便轻松监控关键 pandas 操作的性能。所有这些 benchmark 都位于 pandas/asv_bench 目录下,测试结果可以在 这里 找到。
要使用 asv 的所有功能,您需要 conda 或 virtualenv。有关更多详细信息,请查看 asv 安装网页。
安装 asv:
pip install git+https://github.com/airspeed-velocity/asv
如果您需要运行 benchmark,请将目录更改为 asv_bench/ 并运行:
asv continuous -f 1.1 upstream/main HEAD
您可以将 HEAD 替换为您正在处理的分支的名称,并报告变化超过 10% 的 benchmark。该命令默认使用 conda 来创建 benchmark 环境。如果您想改用 virtualenv,请写入:
asv continuous -f 1.1 -E virtualenv upstream/main HEAD
应将 -E virtualenv 选项添加到所有运行 benchmark 的 asv 命令中。默认值在 asv.conf.json 中定义。
运行完整的 benchmark 套件可能需要一整天的时间,具体取决于您的硬件和资源利用情况。然而,通常只需将部分结果粘贴到拉取请求中,以表明提交的更改不会导致意外的性能回归就足够了。您可以使用 -b 标志运行特定的 benchmark,该标志接受一个正则表达式。例如,这将只运行 pandas/asv_bench/benchmarks/groupby.py 文件中的 benchmark:
asv continuous -f 1.1 upstream/main HEAD -b ^groupby
如果您只想运行文件中的特定一组 benchmark,可以使用 . 作为分隔符。例如:
asv continuous -f 1.1 upstream/main HEAD -b groupby.GroupByMethods
将只运行 groupby.py 中定义的 GroupByMethods benchmark。
您还可以使用当前 Python 环境中已安装的 pandas 版本来运行 benchmark 套件。如果您没有 virtualenv 或 conda,或者正在使用上面讨论的 setup.py develop 方法,这可能很有用;对于就地构建,您需要设置 PYTHONPATH,例如 PYTHONPATH="$PWD/.." asv [remaining arguments]。您可以通过以下方式使用现有的 Python 环境运行 benchmark:
asv run -e -E existing
或者,使用特定的 Python 解释器:
asv run -e -E existing:python3.6
这将显示 benchmark 的 stderr,并使用您本地的、来自您 $PATH 的 python。
有关如何编写 benchmark 和使用 asv 的信息,请参阅 asv 文档。
记录您的代码#
更改应反映在位于 doc/source/whatsnew/vx.y.z.rst 的发布说明中。此文件包含每个版本的持续更新日志。将条目添加到此文件中以记录您的修复、增强功能或(不可避免的)破坏性更改。添加条目时,请确保包含 GitHub issue 编号(使用 :issue:`1234`,其中 1234 是 issue/pull request 编号)。您的条目应使用完整的句子和正确的语法编写。
提及 API 的部分时,请使用适当的 Sphinx 指令,如 :func:、:meth: 或 :class:。并非所有公共 API 函数和方法都有文档页面;理想情况下,只有在链接可以解析时才添加链接。您通常可以通过检查先前版本的发布说明来找到类似的示例。
如果您的代码是 bug 修复,请将您的条目添加到相关的 bug 修复部分。避免添加到 Other 部分;只有在极少数情况下条目才应放在那里。在尽可能简洁的情况下,对 bug 的描述应包括用户如何遇到该 bug 以及 bug 的指示,例如“产生不正确的结果”或“错误地引发”。可能还需要指明新的行为。
如果您的代码是一项增强功能,那么很可能需要将使用示例添加到现有文档中。这可以通过遵循关于 文档 的部分来完成。此外,为了让用户知道该功能何时添加,使用了 versionadded 指令。其 Sphinx 语法如下:
.. versionadded:: 2.1.0
这将在您放置 sphinx 指令的任何地方显示文本 *新版本 2.1.0*。当添加新函数或方法(示例)或新关键字参数(示例)时,也应将其放在 docstring 中。