CI やローカルで長く使ってきた python setup.py test が、ある日から次のエラーで止まることがあります。
usage: setup.py [global_opts] cmd1 [cmd1_opts] [cmd2 [cmd2_opts] ...]
or: setup.py --help [cmd1 cmd2 ...]
or: setup.py --help-commands
or: setup.py cmd --help
error: invalid command 'test'
プロジェクトのコードは何も変えていないのに、テスト実行のコマンドだけが失敗します。原因は、setuptools が 72.0.0 で test コマンドを削除したことです。python setup.py test という起動方法そのものが無くなったので、テストは別の起動方法に移します。setuptools を入れ直しても戻りません。
急いで直すなら、テストランナーを直接呼びます。
# setup.py test の代わりに、標準ライブラリの unittest で実行する
python -m unittest
# pytest を使っているプロジェクトなら
pytest
なぜ起きるのか:setup.py test は非推奨を経て削除された
python setup.py test は、setuptools がテスト実行のために提供していたコマンドです。これは長く非推奨で、実行すると「将来削除される」旨の警告を出し続けていました。予告どおり setuptools 72.0.0 で削除され、test は setuptools が知らないコマンドになりました。
エラーが invalid command 'test'(usage の一覧つき)という形なのは、setup.py が受け付けるコマンドの中に test がもう無いからです。存在しないコマンドを渡したときの一般的な応答で、test に固有の失敗ではありません。
なぜ setuptools 72 以降で出るのか
境界は setuptools の版で、Python の版でもプロジェクトのコードでもありません。setup.py test は setuptools 71 系までは(非推奨の警告つきで)動きます。72.0.0 以降で削除されているので、次のような食い違いで表面化します。
- CI やコンテナで setuptools が新しく入った:クリーンな環境を作ると、そのとき入手できる新しい setuptools が入ります。ローカルの古い環境に残っている setuptools が 71 以下なら動き、CI だけが 72 以降で
invalid command 'test'になります。 - 仮想環境を作り直した/
pip install --upgrade setuptoolsした:更新で 72 の境界を越えると、同じpython setup.py testが通らなくなります。
まず python -c "import setuptools; print(setuptools.__version__)" で版を確認します。72 以降なら、この記事の削除に当たっています。
直し方:テストの起動を setup.py から外す
setup.py test がしていたのは、テストを探して実行することです。同じことは、テストランナーを直接呼べばできます。setuptools のバージョンに依存しません。
標準ライブラリの unittest で実行する場合、テストが test_*.py の形なら自動で見つかります。
python -m unittest # カレント配下の test_*.py を探して実行
python -m unittest discover -s tests # tests ディレクトリに置いている場合
pytest を使うなら、そのまま呼びます。
pytest
Makefile や CI の設定に python setup.py test が書いてあるなら、その行を上のコマンドに置き換えます。tox や nox でテスト環境を定義している場合は、実行コマンドをテストランナー呼び出しに変えます。依存としてのテスト設定(setup.py の tests_require など、setup.py test が読んでいた項目)も併せて不要になるので、テスト依存は requirements-dev.txt や pyproject.toml の任意依存へ移します。
setuptools を入れ直しても戻らない
このエラーで最初に試したくなるのは setuptools の再インストールや更新ですが、それでは戻りません。test コマンドは削除されていて、新しい setuptools にも入っていないからです。更新するほど、削除された状態に近づきます。
古い setuptools(71 以下)に固定すれば setup.py test は動きますが、それは削除前の版に張り付く延命で、非推奨だったコマンドに依存し続けることになります。setuptools は近年、setup.py <command> という直接起動そのものを減らす方向に進んでいるので、テスト起動をランナー側に移すほうが後を引きません。これは、pkg_resources が setuptools 82 で削除されて pip install setuptools では戻らない No module named ‘pkg_resources’ の直し方と同じ形で、「入れ直し」ではなく「削除された機能から移る」のが直し方です。
切り分け(うまくいかないとき)
testではなく別のコマンドでinvalid commandが出る:setup.pyの各種コマンド(ptrなどのテスト系プラグインを含む)も、setuptools やプラグインの版で使えなくなることがあります。エラーは同じ「そのコマンドは無い」形で、対処は同じくその機能を提供するツールを直接呼ぶことです。python setup.py自体にSetuptoolsDeprecationWarningが出る:testに限らず、setup.pyを直接コマンド起動すること全体が非推奨として案内されています。ビルドはpython -m build、インストールはpip install .、テストはランナー直接、と用途ごとの標準コマンドへ移します。setup.pyの中でfrom setuptools.command.test import testを import している:72.1.0 以降、このモジュール自体は import できる形(スタブ)で残っていますが、testコマンドとしては使えません(実行してもinvalid command 'test'の側になります)。import ごと外して、テスト起動をランナーへ移します。python -m unittestでテストが 1 件も見つからない:既定の探索はtest_*.py(とTestCaseサブクラス)です。ファイル名が違う、tests/に__init__.pyが無い、といった場合はpython -m unittest discover -s <ディレクトリ> -p "<パターン>"で明示します。- 手元では通るのに CI でだけ落ちる:setuptools の版が違います。CI のログで実際に入った setuptools の版を確認します。72 以降なら本記事の削除です。
検証環境
python:3.13.14-slim、ネットワーク有り- 再現:
pip install "setuptools==72.0.0"の後、最小のsetup.py(setup(name=..., version=...)だけ)に対してpython setup.py testがerror: invalid command 'test'で終了コード 1 - 修正:同じ
setup.pyと同じテスト(unittest.TestCaseを1つ)をpython -m unittestで実行すると終了コード 0・シグネチャ消滅
再現から修正までは errfix の検証ハーネスが機械的に確認しています。版境界は、setuptools==71.1.0 では同じ python setup.py test が(非推奨の警告つきで)終了コード 0 で通ることを実測して裏づけました。72.0.0 は PyPI で yanked ですが、削除境界を固定して再現するために使っています。削除が 72 以降で継続していること(invalid command 'test' のまま)は、現行の setuptools(83.0.0)でも同じエラーになることを別途実測して確認しました。setup.py test が非推奨を経て 72.0.0 で削除された経緯や、setup.py 直接起動全体の非推奨方針は、setuptools の変更履歴に沿っています。