为代码库做出贡献#

代码标准#

编写好的代码不仅仅是关于你写了什么。它还关乎你 如何 写它。在 持续集成 测试期间,将运行几个工具来检查你的代码是否存在风格错误。生成任何警告都会导致测试失败。因此,良好的风格是向 pandas 提交代码的要求。

在 pandas 中有一些工具可以帮助贡献者在贡献项目之前验证他们的更改

  • ./ci/code_checks.sh: 一个验证文档测试、文档字符串格式和导入模块的脚本。可以通过使用参数 docstringscodedoctests 独立运行检查(例如 ./ci/code_checks.sh doctests);

  • pre-commit,我们将在下一节详细讨论。

此外,由于许多人使用我们的库,重要的是我们不要对代码进行突然的更改,这些更改可能会导致大量用户代码崩溃,也就是说,我们需要它尽可能地 向后兼容 以避免大规模崩溃。

预提交#

此外,持续集成 将运行代码格式检查,如 ruffisortclang-format 等,使用 pre-commit hooks。这些检查中的任何警告都将导致 持续集成 失败;因此,在提交代码之前运行检查是非常有帮助的。这可以通过安装 ``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 中的一个错误,如果你使用 conda,可能会遇到问题。要解决这个问题,你可以将 virtualenv 降级到版本 20.0.33

备注

如果你最近从上游分支合并了主分支,pre-commit 使用的一些依赖项可能已经更改。请确保 更新你的开发环境

可选依赖项#

可选依赖项(例如 matplotlib)应使用私有辅助函数 pandas.compat._optional.import_optional_dependency 导入。这确保了在未满足依赖项时提供一致的错误消息。

所有使用可选依赖的方法都应该包含一个测试,断言当找不到可选依赖时会引发 ImportError 。如果库存在,则应跳过此测试。

所有可选依赖项应在 可选依赖项 中记录,并且最小所需版本应在 pandas.compat._optional.VERSIONS 字典中设置。

向后兼容性#

请尽量保持向后兼容性。pandas 有很多用户和大量现有代码,所以如果可能的话,不要破坏它。如果你认为必须破坏兼容性,请在拉取请求中清楚地说明原因。此外,在更改方法签名时要小心,并在需要的地方添加弃用警告。此外,将弃用 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

你还需要

  1. 编写一个新测试,断言在调用带有已弃用参数时会发出警告

  2. 更新所有现有的 pandas 测试和代码以使用新参数

更多信息请参见 测试一个警告

类型提示#

pandas 强烈鼓励使用 PEP 484 风格的类型提示。新开发应包含类型提示,并且对现有代码进行注解的拉取请求也是被接受的!

风格指南#

类型导入应遵循 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 会捕捉 intfloat 类型,但 mypy 还不能做出同样的推断(参见 mypy #5206)。虽然上述方法有效,但使用 cast 是**强烈不鼓励的**。在适用的情况下,重构代码以满足静态分析是更可取的

def cannot_infer_good(obj: Union[str, int, float]):

    if isinstance(obj, str):
        return obj.upper()
    else:
        ...

使用自定义类型和推断时,这并不总是可能的,因此会做出例外,但在走这条路之前,应尽一切努力避免 cast

pandas-specific 类型#

常用的特定于pandas的类型将出现在 pandas._typing 中,你应该在适用的地方使用这些类型。该模块目前是私有的,但最终应该向希望对pandas实施类型检查的第三方库公开。

例如,pandas 中的许多函数接受 dtype 参数。这可以表示为字符串,如 "object",一个 numpy.dtypenp.int64,甚至是 pandas 的 ExtensionDtypepd.CategoricalDtype。与其让用户不断注释所有这些选项,这可以简单地从 pandas._typing 模块中导入和重用。

from pandas._typing import Dtype

def as_type(dtype: Dtype) -> ...:
    ...

这个模块最终将包含用于重复使用概念的类型,如“类路径”、“类数组”、“数值”等…,还可以包含常见参数的别名,如 axis。这个模块的开发是活跃的,所以请务必参考源代码以获取最新的可用类型列表。

验证类型提示#

pandas 使用 mypypyright 来静态分析代码库和类型提示。在进行任何更改后,您可以通过运行来确保类型提示的一致性

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环境 或选择一个 最近成功的workflow,选择“Docstring验证、类型检查和其他手动pre-commit钩子”任务,然后点击“设置Conda”和“环境信息”以查看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 一起提供的类型注解。

使用持续集成进行测试#

pandas 测试套件将在您提交拉取请求后自动在 GitHub Actions 持续集成服务上运行。然而,如果您希望在提交拉取请求之前在分支上运行测试套件,那么持续集成服务需要连接到您的 GitHub 仓库。这里有 GitHub Actions 的说明。

当你的构建全部为‘绿色’时,才会考虑合并拉取请求。如果有任何测试失败,你会得到一个红色的‘X’,你可以点击查看单个失败的测试。这是一个绿色构建的例子。

../_images/ci.png

测试驱动开发#

pandas 非常重视测试,并强烈鼓励贡献者采用 测试驱动开发 (TDD)。这种开发过程“依赖于一个非常短的开发周期的重复:首先,开发人员编写一个(最初会失败的)自动化测试用例,定义期望的改进或新功能,然后生成通过该测试所需的最少代码量。”因此,在实际编写任何代码之前,你应该先编写测试。通常,测试可以从原始的 GitHub 问题中提取。然而,总是值得考虑额外的用例并编写相应的测试。

添加测试是在代码推送到 pandas 之后最常见的请求之一。因此,养成提前编写测试的习惯是值得的,这样这永远不会成为一个问题。

编写测试#

所有测试应放入特定包的 tests 子目录中。此文件夹包含许多当前的测试示例,我们建议参考这些示例以获取灵感。

作为一个一般建议,你可以在你的集成开发环境(IDE)中使用搜索功能,或者在终端中使用 git grep 命令来查找调用该方法的测试文件。如果你不确定最佳的测试放置位置,请做出最佳猜测,但请注意,审阅者可能会要求你将测试移动到不同的位置。

要使用 git grep,你可以在终端中运行以下命令:

git grep "function_name("

这将搜索您仓库中的所有文件以查找文本 function_name(。这是一种快速定位代码库中的函数并确定为其添加测试的最佳位置的有用方法。

理想情况下,应该有一个且只有一个明显的地方存放测试。在我们达到这个理想状态之前,这些是一些关于测试应放置位置的经验法则。

  1. 你的测试是否仅依赖于 pd._libs.tslibs 中的代码?这个测试可能属于以下之一:

    • tests.tslibs

      备注

      tests.tslibs 中的文件不应从 pd._libs.tslibs 之外的任何 pandas 模块导入

    • tests.scalar

    • tests.tseries.offsets

  2. 你的测试是否仅依赖于 pd._libs 中的代码?这个测试可能属于以下之一:

    • tests.libs

    • tests.groupby.test_libgroupby

  3. 你的测试是针对算术还是比较方法的?这个测试可能属于以下之一:

    • tests.arithmetic

      备注

      这些用于可以共享的测试,以测试使用 box_with_array 夹具的 DataFrame/Series/Index/ExtensionArray 的行为。

    • tests.frame.test_arithmetic

    • tests.series.test_arithmetic

  4. 你的测试是针对一种归约方法(最小值、最大值、总和、乘积等)吗?这个测试可能属于以下之一:

    • tests.reductions

      备注

      这些用于可以共享的测试,以测试 DataFrame/Series/Index/ExtensionArray 的行为。

    • tests.frame.test_reductions

    • tests.series.test_reductions

    • tests.test_nanops

  5. 你的测试是针对索引方法的吗?这是决定测试属于哪里的最困难的情况,因为有这么多这样的测试,而且其中许多测试不止一种方法(例如,Series.__getitem__Series.loc.__getitem__

    1. 这个测试是否专门测试了索引方法(例如 Index.get_locIndex.get_indexer )?这个测试可能属于以下之一:

      • tests.indexes.test_indexing

      • tests.indexes.fooindex.test_indexing

      在这些文件中应该有一个特定于方法的测试类,例如 TestGetLoc

      在大多数情况下,这些测试中不需要 SeriesDataFrame 对象。

    2. 这个测试是针对 Series 或 DataFrame 索引方法的 其他 方法,而不是 __getitem____setitem__,例如 xswheretakemasklookupinsert 吗?这个测试可能属于以下之一:

      • tests.frame.indexing.test_methodname

      • tests.series.indexing.test_methodname

    3. 这个测试是针对 locilocatiat 中的任何一个吗?这个测试可能属于以下之一:

      • tests.indexing.test_loc

      • tests.indexing.test_iloc

      • tests.indexing.test_at

      • tests.indexing.test_iat

      在适当的文件中,测试类对应于索引器的类型(例如 TestLocBooleanMask)或主要用例(例如 TestLocSetitemWithExpansion)。

      请参阅 D) 节中的注释,关于测试多种索引方法的测试。

    4. 这是针对 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)
      

    在这种情况下,测试位置应基于被测试的 底层 方法。或者在修复错误的测试情况下,应基于实际错误的位置。因此在这个例子中,我们知道 Series.__getitem__ 调用 Series.loc.__getitem__,所以这实际上是 loc.__getitem__ 的测试。因此这个测试属于 tests.indexing.test_loc

  6. 你的测试是针对 DataFrame 还是 Series 方法?

    1. 这个方法是一个绘图方法吗?这个测试可能属于以下之一:

      • tests.plotting

    2. 这个方法是一个IO方法吗?这个测试可能属于以下之一:

      • tests.io

        备注

        这包括 to_string 但不包括 __repr__,后者在 tests.frame.test_reprtests.series.test_repr 中进行测试。其他类通常有一个 test_formats 文件。

    3. 否则,此测试可能属于以下之一:

      • tests.series.methods.test_mymethod

      • tests.frame.methods.test_mymethod

        备注

        如果一个测试可以在 DataFrame/Series 之间共享使用 frame_or_series 夹具,按照惯例它放在 tests.frame 文件中。

  7. 你的测试是为 Index 方法进行的,不依赖于 Series/DataFrame 吗?这个测试可能属于以下之一:

    • tests.indexes

  1. 你的测试是针对 pandas 提供的某个 ExtensionArrays (CategoricalDatetimeArrayTimedeltaArrayPeriodArrayIntervalArrayNumpyExtensionArrayFloatArrayBoolArrayStringArray)吗?这个测试可能属于以下之一:

    • tests.arrays

  2. 你的测试是针对 所有 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_* 的功能测试 接受作为固定装置或参数的参数。

  • 对于测试标量和真值测试,使用一个简单的 assert

  • 使用 tm.assert_series_equal(result, expected)tm.assert_frame_equal(result, expected) 分别比较 SeriesDataFrame 结果。

  • 在测试多个案例时使用 @pytest.mark.parameterize

  • 当一个测试用例预期会失败时,使用 pytest.mark.xfail

  • 当一个测试用例预期永远不会通过时,使用 pytest.mark.skip

  • 当测试用例需要特定的标记时,使用 pytest.param

  • 如果多个测试可以共享一个设置对象,请使用 @pytest.fixture

警告

不要使用 pytest.xfail (这与 pytest.mark.xfail 不同),因为它会立即停止测试,并且不会检查测试是否会失败。如果这是你想要的行为,请改用 pytest.skip

如果一个测试已知会失败,但失败的方​​式不需要被捕获,请使用 pytest.mark.xfail。对于表现出错误行为或未实现功能的测试,通常会使用这种方法。如果失败的测试行为不稳定,请使用参数 strict=False。这将使 pytest 在测试偶然通过时不失败。使用 strict=False 是非常不推荐的,请仅在万不得已时使用。

建议在测试中使用装饰器 @pytest.mark.xfail 和参数 pytest.param,以便在 pytest 的收集阶段适当地标记测试。对于涉及多个参数、夹具或这些组合的测试,只能在测试阶段进行 xfail。为此,请使用 request 夹具:

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 的文档 以了解文件保留策略。

涉及网络连接的测试#

单元测试不应访问互联网上的公共数据集,因为网络连接不稳定且无法控制所连接的服务器。要模拟这种交互,请使用 pytest-localserver 插件 中的 httpserver 固定装置和合成数据。

@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: 任何耗时较长的测试(考虑秒而不是毫秒)

  • 网络:需要网络连接的测试

  • db: 需要数据库(mysql 或 postgres)的测试

  • single_cpu:应该仅在单个CPU上运行的测试

如果这对您相关,您可能希望启用以下选项:

  • arm_slow: 在 arm64 架构上运行时间较长的任何测试

这些标记在 这个 toml 文件 中定义,位于 [tool.pytest.ini_options] 下的一个名为 markers 的列表中,以防你想检查是否有新创建的对你感兴趣的标记。

-r 报告标志将显示一个简短的摘要信息(参见 pytest 文档)。这里我们显示了以下数量:

  • s: 跳过的测试

  • x: xfailed 测试

  • X: xpassed 测试

摘要是可以选择性的,如果你不需要额外的信息,可以将其移除。使用并行化选项可以显著减少在提交拉取请求之前本地运行测试所需的时间。

如果您在使用结果时需要帮助,这在过去发生过,请在运行命令和提交错误报告之前设置一个种子,这样我们就可以重现它。以下是在 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 benchmarks 以方便监控关键 pandas 操作的性能。这些基准测试都可以在 pandas/asv_bench 目录中找到,测试结果可以在这里找到 here

要使用 asv 的所有功能,你需要 condavirtualenv。更多详细信息请查看 asv 安装网页

要安装 asv:

pip install git+https://github.com/airspeed-velocity/asv

如果你需要运行基准测试,请将你的目录更改为 asv_bench/ 并运行:

asv continuous -f 1.1 upstream/main HEAD

你可以将 HEAD 替换为你正在工作的分支名称,并报告变化超过10%的基准测试。该命令默认使用 conda 来创建基准测试环境。如果你想使用 virtualenv 代替,请写:

asv continuous -f 1.1 -E virtualenv upstream/main HEAD

-E virtualenv 选项应添加到所有运行基准测试的 asv 命令中。默认值在 asv.conf.json 中定义。

运行完整的基准测试套件可能是一个全天过程,具体取决于您的硬件及其资源利用率。然而,通常只需将结果的一个子集粘贴到拉取请求中,以显示提交的更改不会导致意外的性能退化。您可以使用 -b 标志运行特定的基准测试,该标志接受一个正则表达式。例如,这将仅运行来自 pandas/asv_bench/benchmarks/groupby.py 文件的基准测试:

asv continuous -f 1.1 upstream/main HEAD -b ^groupby

如果你想只运行文件中的特定一组基准测试,你可以使用 . 作为分隔符。例如:

asv continuous -f 1.1 upstream/main HEAD -b groupby.GroupByMethods

将仅运行在 groupby.py 中定义的 GroupByMethods 基准测试。

你也可以使用当前Python环境中已安装的``pandas``版本运行基准测试套件。如果你没有virtualenv或conda,或者正在使用上述讨论的``setup.py develop``方法,这会非常有用;对于就地构建,你需要设置``PYTHONPATH``,例如``PYTHONPATH=”$PWD/..” asv [剩余参数]``。你可以通过以下方式使用现有的Python环境运行基准测试:

asv run -e -E existing

或者,使用特定的 Python 解释器,:

asv run -e -E existing:python3.6

这将显示来自基准测试的 stderr,并使用来自 $PATH 的本地 python

关于如何编写基准测试以及如何使用 asv 的信息可以在 asv 文档 中找到。

记录你的代码#

更改应在位于 doc/source/whatsnew/vx.y.z.rst 的发布说明中反映。该文件包含每个版本的持续更新日志。向该文件添加一个条目以记录您的修复、增强或(不可避免的)破坏性更改。添加条目时,请确保包括 GitHub 问题编号(使用 :issue:`1234` ,其中 1234 是问题/拉取请求编号)。您的条目应使用完整的句子并使用正确的语法书写。

在提及API的部分时,根据情况使用Sphinx的 :func:, :meth:, 或 :class: 指令。并非所有公共API函数和方法都有文档页面;理想情况下,只有在链接解析时才会添加。通常可以通过检查前一个版本的发布说明来找到类似的示例。

如果你的代码是一个修复bug的提交,请将你的条目添加到相关的修复bug部分。避免添加到 其他 部分;只有在极少数情况下,条目才应该放在那里。尽可能简洁地描述bug,应该包括用户可能如何遇到它以及bug本身的指示,例如“产生错误结果”或“错误地引发”。可能还需要指出新的行为。

如果你的代码是一个增强功能,很可能需要在现有文档中添加使用示例。这可以按照关于 文档 的部分进行。此外,为了让用户知道这个功能是什么时候添加的,使用 versionadded 指令。Sphinx 的语法如下:

.. versionadded:: 2.1.0

这将在你放置 sphinx 指令的任何地方显示文本 New in version 2.1.0。在添加新函数或方法 (示例) 或新关键字参数 (示例) 时,也应该将其放入文档字符串中。