コードベースへの貢献#
目次
コード標準#
優れたコードを作成することは、単に何を書くかということだけではありません。どのように書くかということも重要です。 継続的インテグレーション テスト中、いくつかのツールが実行され、コードのスタイル上のエラーがチェックされます。警告が生成されると、テストは失敗します。したがって、優れたスタイルは pandas にコードを提出するための要件です。
pandas には、コントリビューターがプロジェクトに貢献する前に変更を検証するのに役立ついくつかのツールがあります。
./ci/code_checks.sh
: スクリプトは、ドキュメントテスト、ドキュメント文字列のフォーマット、およびインポートされたモジュールを検証します。docstrings
、code
、およびdoctests
のパラメータを使用して、チェックを個別に実行できます(例:./ci/code_checks.sh doctests
)。pre-commit
。これについては次のセクションで詳しく説明します。
さらに、多くの人が私たちのライブラリを使用しているため、多くのユーザーコードを壊す可能性のあるコードへの突然の変更を行わないことが重要です。つまり、大規模な破損を避けるために、できる限り *後方互換性* を維持する必要があります。
Pre-commit#
さらに、継続的インテグレーション では、pre-commit フックを使用して、black
、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 チェックもいくつかあります。次のコマンドで手動でトリガーできます。
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
次のことも必要になります。
非推奨の引数で呼び出すと警告が発行されることをアサートする新しいテストを作成します。
pandas の既存のすべてのテストとコードを更新して、新しい引数を使用します。
詳細については、警告のテスト を参照してください。
型ヒント#
pandas は、PEP 484 スタイルの型ヒントの使用を強く推奨します。新しい開発には型ヒントを含める必要があり、既存のコードに注釈を付けるプルリクエストも受け付けています。
スタイルガイドライン#
型のインポートは、from typing import ...
の規則に従う必要があります。コードは、pre-commit チェック によって、最新の構成(例: typing.List
の代わりに組み込みの 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 の非常に多くの関数が dtype
引数を受け入れます。これは "object"
のような文字列、np.int64
のような numpy.dtype
、または pd.CategoricalDtype
のような pandas の ExtensionDtype
として表現できます。ユーザーにこれらのすべてのオプションを常にアノテーションする必要性を負担させるのではなく、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
のバージョンが一致しない場合に頻繁に発生します。pandas CI がインストールするバージョンを確認するには、python 環境のセットアップ方法 を参照するか、最近成功したワークフロー を選択し、「Docstring validation, typing, and other manual pre-commit hooks」ジョブを選択して、「Set up Conda」と「Environment info」をクリックしてください。
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」が表示され、それをクリックすると個々の失敗したテストを確認できます。これは、成功したビルドの例です。

テスト駆動開発#
pandas はテストを重視しており、貢献者が テスト駆動開発 (TDD) を採用することを強く推奨しています。この開発プロセスは、「非常に短い開発サイクルの繰り返しに依存します。最初に、開発者は (最初は失敗する) 自動テストケースを作成して、望ましい改善または新しい機能を定義し、次にそのテストに合格するための最小限のコードを作成します。」したがって、実際にコードを記述する前に、テストを記述する必要があります。多くの場合、テストは元の GitHub issue から取得できます。ただし、追加のユースケースを検討し、対応するテストを作成する価値は常にあります。
テストの追加は、コードが pandas にプッシュされた後によくある要求の 1 つです。したがって、これが問題にならないように、事前にテストを記述する習慣を身につける価値があります。
テストの記述#
すべてのテストは、特定のパッケージの 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
テストが pd._libs のコードのみに依存している場合、このテストは次のいずれかに属する可能性が高いです。
tests.libs
tests.groupby.test_libgroupby
テストが算術演算または比較メソッドのテストである場合、このテストは次のいずれかに属する可能性が高いです。
tests.arithmetic
注
これらは、
box_with_array
フィクスチャを使用して 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
オブジェクトも必要ありません。テストが
__getitem__
または__setitem__
以外 の Series または DataFrame インデックスメソッド (例: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)
このような場合、テストの場所は、テスト対象の基盤となるメソッドに基づいて決定する必要があります。あるいは、バグ修正のテストの場合は、実際のバグの場所に基づいて決定します。したがって、この例では、
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_series
フィクスチャを使用して DataFrame/Series 間で共有できる場合、慣例によりtests.frame
ファイルに配置されます。
テスト対象が、Series/DataFrame に依存しない Index メソッドですか?このテストは、以下のいずれかに該当する可能性が高いです。
tests.indexes
テスト対象が、pandas が提供する ExtensionArray(
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_*
という名前で、フィクスチャまたはパラメータのいずれかである引数のみを受け取ります。スカラーと真理値テストには、ベアな
assert
を使用します。Series
とDataFrame
の結果を比較するには、tm.assert_series_equal(result, expected)
とtm.assert_frame_equal(result, expected)
をそれぞれ使用します。複数のケースをテストする場合は、@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 の収集フェーズ中に適切にマークされるように、テスト内での使用よりも、デコレータ @pytest.mark.xfail
と引数 pytest.param
を優先します。複数のパラメータ、フィクスチャ、またはこれらの組み合わせを含むテストを xfail するには、テストフェーズ中に 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
をコンテキストマネージャーとして使用します。
with tm.assert_produces_warning(DeprecationWarning):
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")
ファイルを含むテスト#
tm.ensure_clean
コンテキストマネージャーは、テスト用の一時ファイルを生成されたファイル名(または指定したファイル名)で作成し、コンテキストブロックが終了すると自動的に削除します。
with tm.ensure_clean('my_file_path') as path:
# do something with the path
ネットワーク接続を含むテスト#
ユニットテストは、ネットワーク接続の不安定さや、接続先のサーバーの所有権がないため、インターネット経由でパブリックデータセットにアクセスすべきではありません。この相互作用をモックするには、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
parametrize
したテストには、テスト名でアクセスできるようになりました。たとえば、-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 テストは、ロジックが複雑な場合や、オプションの組み合わせが多すぎる場合、またはテスト(または考える!)するのに微妙な相互作用が多すぎる場合に予約されます。
テストスイートの実行#
pandasをインストールしなくても、Gitクローン内で直接テストを実行できます。以下のコマンドを入力してください。
pytest pandas
注
いくつかのテストがパスしない場合でも、pandasのインストールに問題があるとは限りません。一部のテスト(例えば、SQLAlchemy関連のテスト)は追加の設定が必要です。また、ピン留めされていないライブラリが新しいバージョンをリリースしたために失敗する可能性や、並列実行時に不安定になるテストもあります。ローカルでビルドしたバージョンからpandasをインポートできる限り、インストールは問題ないと考えられ、貢献を始めることができます。
多くの場合、テストスイート全体を実行する前に、変更箇所周辺のテストのサブセットのみを実行する価値があります(ヒント:pandas-coverageアプリを使用すると、変更したコード行をヒットするテストを特定し、それらのテストのみを実行できます)。
これを行う最も簡単な方法は、以下のコマンドを使うことです。
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]
‘pandas-dev’環境に含まれているpytest-xdistを使用すると、マルチコアマシンでのローカルテストを高速化できます。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またはPostgreSQL)が必要なテスト
single_cpu:シングルCPUでのみ実行する必要があるテスト
関連性がある場合は、以下のオプションを有効にすることができます。
arm_slow:arm64アーキテクチャで実行に時間がかかるテスト
これらのマーカーは、このtomlファイルの[tool.pytest.ini_options]
の下にあるmarkers
というリストで定義されています。興味のある新しいマーカーが作成されていないか確認したい場合は、このファイルを参照してください。
-r
レポートフラグは、簡単なサマリー情報を表示します(詳細はpytestドキュメントを参照)。ここでは、以下の数を表示します。
s:スキップされたテスト
x:xfailしたテスト
X:xpassしたテスト
サマリーはオプションであり、追加の情報が必要ない場合は削除できます。並列化オプションを使用すると、プルリクエストを送信する前にローカルでテストを実行する時間を大幅に短縮できます。
過去に発生したように、結果についてサポートが必要な場合は、コマンドを実行する前にシードを設定してバグレポートを送信してください。そうすることで、再現することができます。以下は、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は、重要なpandas操作のパフォーマンスを簡単に監視できるように、asvベンチマークへの移行を進めています。これらのベンチマークはすべてpandas/asv_bench
ディレクトリにあり、テスト結果はこちらで確認できます。
asvのすべての機能を使用するには、conda
またはvirtualenv
のいずれかが必要です。詳細については、asvのインストールに関するWebページを確認してください。
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番号を必ず含めてください(:issue:`1234`
を使用します。1234
はissue/プルリクエスト番号です)。エントリは完全な文章と正しい文法を使用して記述する必要があります。
APIの一部に言及する場合は、必要に応じてSphinxの:func:
、:meth:
、または:class:
ディレクティブを使用してください。すべてのパブリックAPI関数とメソッドにドキュメントページがあるわけではありません。理想的には、リンクが解決する場合にのみリンクを追加する必要があります。通常、以前のバージョンのいずれかのリリースノートを確認することで、同様の例を見つけることができます。
コードがバグ修正である場合は、関連するバグ修正セクションにエントリを追加してください。Other
セクションへの追加は避けてください。まれにしかエントリはそこに入れるべきではありません。できるだけ簡潔に、バグの説明には、ユーザーがどのように遭遇する可能性があるか、およびバグ自体の兆候(例えば、「誤った結果を生成する」または「誤って例外を発生させる」)を含める必要があります。新しい動作を示す必要もあるかもしれません。
コードが機能強化である場合は、既存のドキュメントに使用例を追加する必要がある可能性が最も高いです。これは、ドキュメントに関するセクションに従って行うことができます。さらに、この機能が追加された時期をユーザーに知らせるために、versionadded
ディレクティブが使用されます。そのsphinxの構文は次のとおりです。
.. versionadded:: 2.1.0
これにより、Sphinxディレクティブを配置した場所に「バージョン2.1.0で新規」というテキストが表示されます。これは、新しい関数またはメソッド(例)または新しいキーワード引数(例)を追加するときにも、docstringに含める必要があります。