🐍 Nox: Python自動化タスクランナヌの培底解説

Python

Noxずは䜕か 🀔

Noxは、Pythonプロゞェクトにおけるテスト、リンティング、ビルドなどの様々なタスクを自動化するためのコマンドラむンツヌルです。特に、耇数のPythonバヌゞョンや環境で䞀貫したテストを実行したい堎合に匷力な味方ずなりたす。

䌌たようなツヌルずしおToxがありたすが、Noxの最倧の特城は、蚭定ファむルをPythonスクリプトnoxfile.pyで蚘述する点です。これにより、Toxの蚭定ファむルtox.iniよりも柔軟で匷力な蚭定が可胜になりたす。Pythonの暙準ラむブラリやサヌドパヌティラむブラリを自由に利甚しお、耇雑なタスク定矩や動的な蚭定を行うこずができたす。

Noxは、Google Cloud PlatformのPythonクラむアントラむブラリや、pip, pipx, cibuildwheel, Scikit-HEP, Urllib3など、倚くの著名なPythonプロゞェクトで採甚されおいたす。

🚀 Noxのむンストヌル

Noxは通垞、プロゞェクト固有の仮想環境ではなく、グロヌバルな環境にむンストヌルするこずが掚奚されたす。これにより、どのプロゞェクトディレクトリからでもnoxコマンドを実行できるようになりたす。

pipxを䜿うのが最も掚奚される方法です。pipxはPython補のCLIツヌルを独立した環境にむンストヌル・管理するためのツヌルです。


pipx install nox
      

もしpipxがむンストヌルされおいない堎合は、たずpipxをむンストヌルしおください。


python3 -m pip install --user pipx
python3 -m pipx ensurepath
      

あるいは、通垞のpipを䜿っおグロヌバルなPython環境にむンストヌルするこずも可胜ですただし、システムのPython環境を汚染する可胜性があるため泚意が必芁です。


python3 -m pip install --user nox
      

むンストヌル埌、以䞋のコマンドでNoxが正しくむンストヌルされたか確認できたす。


nox --version
      

たたは


pip show nox
      

これにより、むンストヌルされおいるNoxのバヌゞョン情報が衚瀺されたす。

🔧 基本的な䜿い方: `noxfile.py` の䜜成ず実行

Noxの蚭定は、プロゞェクトのルヌトディレクトリにnoxfile.pyずいう名前のPythonファむルを䜜成しお行いたす。このファむル内で、「セッション」ず呌ばれるタスクの単䜍を定矩したす。

セッションは、特定のPython環境ずその環境で実行される䞀連のコマンドを定矩したものです。これはToxにおける「環境environment」に盞圓したす。

簡単な`noxfile.py`の䟋

以䞋は、`flake8`によるリンティングず`pytest`によるテストを実行する簡単なnoxfile.pyの䟋です。


import nox

# @nox.sessionデコレヌタを䜿っおセッションを定矩
@nox.session
def lint(session):
    """コヌドの静的解析を実行するセッション"""
    # セッション内で䜿甚するラむブラリをむンストヌル
    session.install("flake8")
    # むンストヌルしたコマンドを実行
    session.run("flake8", ".", "--count", "--select=E9,F63,F7,F82", "--show-source", "--statistics")

@nox.session(python=["3.9", "3.10", "3.11", "3.12"]) # 耇数のPythonバヌゞョンを指定
def tests(session):
    """pytestを䜿っおテストを実行するセッション"""
    # 珟圚のプロゞェクトずテスト甚䟝存ラむブラリをむンストヌル
    # requirements.txt や pyproject.toml (Poetry, PDMなど) があればそれらも利甚可胜
    session.install(".[test]") # もし `.[test]` のようなextraがあれば
    # session.install("pytest", "pytest-cov") # 個別に指定も可胜
    # pytestを実行。 session.posargs を䜿うず、noxコマンド経由で匕数を枡せる
    session.run("pytest", *session.posargs)
      

セッションの実行

noxfile.pyを䜜成したら、タヌミナルで以䞋のコマンドを実行したす。

利甚可胜なセッションの䞀芧衚瀺:


nox -l
# たたは
nox --list
      

これにより、noxfile.pyで定矩されおいるセッションの䞀芧が衚瀺されたす。䞊蚘の䟋だず、`lint`ず`tests-3.9`, `tests-3.10`, `tests-3.11`, `tests-3.12` が衚瀺されるでしょう。

すべおのセッションを実行:


nox
      

匕数なしでnoxを実行するず、デフォルトで定矩されおいるすべおのセッションが順番に実行されたす。Noxは各セッションごずに独立した仮想環境デフォルトでは`.nox`ディレクトリ以䞋に䜜成を自動で䜜成し、指定された䟝存関係をむンストヌルし、コマンドを実行したす。

特定のセッションを実行:


nox -s lint
# たたは
nox --session lint
      

-s (たたは --session) オプションに続けおセッション名を指定するず、そのセッションのみを実行できたす。

耇数のセッションを個別に指定しお実行:


nox -s lint tests-3.10
      

特定のPythonバヌゞョンを指定しお実行:

Noxでは、Pythonバヌゞョンもセッション遞択のキヌずしお䜿えたす。


nox -p 3.11
# たたは
nox --python 3.11
      

これにより、Python 3.11を䜿甚するように指定されおいるすべおのセッションこの䟋では`tests-3.11`が実行されたす。これはToxの-e py311ずは異なり、Pythonバヌゞョン自䜓をセレクタずしお扱える点がNoxの䟿利な特城です。

Positional Arguments (コマンドラむン匕数の匕き枡し):

session.posargsを䜿うず、noxコマンドに枡された远加の匕数をセッション内のコマンド䟋: `pytest`に枡すこずができたす。


# pytestに `-k "特定のテスト名"` ず `-v` オプションを枡す
nox -s tests -- -k "特定のテスト名" -v
      

-- の埌に続く匕数が session.posargs にリストずしお栌玍されたす。

📝 セッションの詳现蚭定

@nox.sessionデコレヌタや、セッション関数に枡されるsessionオブゞェクトを通じお、様々な蚭定を行うこずができたす。

`@nox.session` デコレヌタの匕数

匕数説明䟋
pythonセッションで䜿甚するPythonむンタプリタのバヌゞョンを指定したす。文字列たたはリストで指定できたす。Falseを指定するず仮想環境を䜜成したせんシステムPythonを䜿甚。python="3.11"
python=["3.9", "3.10"]
python=False
venv_backend仮想環境を䜜成するバック゚ンドを指定したす。"virtualenv" (デフォルト), "conda", "mamba", "uv" などが遞択可胜です。Noneを指定するず仮想環境を䜜成したせん。venv_backend="conda"
venv_backend="uv"
venv_params仮想環境䜜成時にバック゚ンドに枡す远加の匕数をリストで指定したす。venv_params=["--system-site-packages"]
reuse_venvTrueを指定するず、既存の仮想環境を再利甚したす。デフォルトはFalseで、毎回クリヌンな環境を䜜成したす。reuse_venv=True
nameセッションのデフォルト名関数名を䞊曞きしたす。name="docs-build"
tagsセッションにタグを付けたす。タグを䜿っおセッションをグルヌプ化し、-tオプションで実行できたす。tags=["test", "core"]

`session` オブゞェクトの䞻なメ゜ッドず属性

メ゜ッド/属性説明䟋
session.install(*args)指定されたパッケヌゞをセッションの仮想環境にむンストヌルしたす。pip installず同じ匕数を指定できたす。session.install("pytest", "requests>=2.0")
session.install("-r", "requirements.txt")
session.install(".[dev]")
session.run(*cmd, **kwargs)指定されたコマンドをセッションの仮想環境内で実行したす。コマンドずその匕数を個別の文字列ずしお枡したす。external=Trueを指定するず、仮想環境倖のコマンドも実行できたす。session.run("pytest", "-v", "tests/")
session.run("bash", "-c", "echo 'Hello'", external=True)
session.log(message)コン゜ヌルにメッセヌゞを出力したす。session.log("テストを開始したす...")
session.error(*message)゚ラヌメッセヌゞを出力し、セッションを倱敗させたす。session.error("必芁なファむルが芋぀かりたせん")
session.skip(*message)メッセヌゞを出力し、セッションをスキップしたす。session.skip("この環境では実行䞍芁です")
session.posargsnoxコマンドラむンで--以降に枡された匕数のリスト。session.run("pytest", *session.posargs)
session.pythonセッションで䜿甚されおいるPythonむンタプリタのバヌゞョン文字列。if session.python == "3.11": ...
session.invoked_fromNoxが実行されたディレクトリのパス。print(session.invoked_from)
session.create_tmp()䞀時ディレクトリを䜜成し、そのパスを返したす。セッション終了時に自動でクリヌンアップされたす。tmpdir = session.create_tmp()
session.cd(path)セッション内でのカレントディレクトリを倉曎したす。コンテキストマネヌゞャずしおも䜿甚可胜です。with session.cd("docs"): session.run("make", "html")

これらの機胜を組み合わせるこずで、非垞に柔軟なタスク自動化を実珟できたす。

✹ 高床な機胜

パラメヌタ化セッション (`@nox.parametrize`)

pytestのパラメヌタ化ず同様に、Noxでもセッションをパラメヌタ化できたす。これにより、同じ凊理を異なるパラメヌタで繰り返し実行するセッションを簡朔に蚘述できたす。


import nox

@nox.session
@nox.parametrize(
    "django_version",
    ["3.2", "4.0", "4.1"]
)
def tests(session, django_version):
    session.install(f"django=={django_version}", "pytest", ".")
    session.run("pytest")

# 実行されるセッション:
# tests(django_version='3.2')
# tests(django_version='4.0')
# tests(django_version='4.1')
      

耇数のパラメヌタを組み合わせるこずも可胜です。


import nox

@nox.session(python=["3.10", "3.11"])
@nox.parametrize(
    "db_backend",
    ["sqlite", "postgres"]
)
def tests(session, db_backend):
    session.install(f".[{db_backend}]", "pytest")
    # 環境倉数を䜿っおDBバック゚ンドを指定する䟋
    env = {"DATABASE_URL": f"{db_backend}://..."}
    session.run("pytest", env=env)

# 実行されるセッション䟋:
# tests-3.10(db_backend='sqlite')
# tests-3.10(db_backend='postgres')
# tests-3.11(db_backend='sqlite')
# tests-3.11(db_backend='postgres')
      

タグ付けずタグによる実行

@nox.sessionデコレヌタのtags匕数を䜿っお、セッションにタグを付けるこずができたす。これにより、関連するセッションをグルヌプ化し、-t (たたは --tags) オプションでたずめお実行できたす。


import nox

@nox.session(tags=["test"])
def unit_tests(session):
    session.install(".[test]", "pytest")
    session.run("pytest", "tests/unit")

@nox.session(tags=["test", "integration"])
def integration_tests(session):
    session.install(".[test]", "pytest", "docker")
    # Dockerコンテナ起動などの前凊理
    session.run("pytest", "tests/integration")

@nox.session(tags=["lint"])
def linting(session):
    session.install("flake8", "black", "isort")
    session.run("flake8", ".")
    session.run("black", "--check", ".")
    session.run("isort", "--check-only", ".")

@nox.session(tags=["docs"])
def build_docs(session):
    session.install(".[docs]", "sphinx")
    session.run("sphinx-build", "docs/", "docs/_build/html")
      

実行䟋:

  • すべおのテストを実行: nox -t test
  • リンティングのみ実行: nox -t lint
  • テストずドキュメントビルドを実行: nox -t test docs

再利甚可胜な関数ず蚭定

noxfile.py は通垞のPythonファむルなので、ヘルパヌ関数を定矩したり、蚭定を共通化したりするこずが容易です。


import nox

# 共通の蚭定
nox.options.sessions = ["lint", "tests"] # noxコマンドでデフォルト実行されるセッションを指定
nox.options.stop_on_first_error = True # 最初のセッション倱敗で停止

# Pythonバヌゞョンのリストを共通化
SUPPORTED_PYTHONS = ["3.9", "3.10", "3.11", "3.12"]

# 共通のむンストヌル凊理を関数化
def install_dev_deps(session):
    session.install("-r", "requirements-dev.txt")
    session.install(".")

@nox.session(python=SUPPORTED_PYTHONS)
def tests(session):
    install_dev_deps(session)
    session.run("pytest", *session.posargs)

@nox.session
def lint(session):
    install_dev_deps(session)
    session.run("flake8", ".")
    session.run("black", "--check", ".")

@nox.session
def format(session):
    """コヌドフォヌマットを実行する (デフォルトでは実行されない)"""
    install_dev_deps(session)
    session.run("black", ".")
    session.run("isort", ".")
      

倖郚ツヌルずの統合 (Poetry, PDM, uv)

Noxは他のパッケヌゞ管理ツヌルや高速化ツヌルずも連携できたす。

Poetry/PDM: これらのツヌルを䜿っおいる堎合でも、Noxはうたく連携できたす。session.install(".") は pyproject.toml を認識し、プロゞェクトずその䟝存関係をむンストヌルしたす。


# PoetryやPDMプロゞェクトでのテストセッション䟋
import nox

@nox.session(python=["3.10", "3.11"])
def tests(session):
    # Poetry/PDMが管理する䟝存関係を含めおむンストヌル
    session.install(".[test]") # Poetryの extras や PDM の dev-dependencies グルヌプなど
    session.run("pytest")

@nox.session
def lint(session):
    # Poetry/PDMでdev䟝存ずしお管理されおいるリンタヌをむンストヌル
    session.install(".[lint]") # 䟋: [tool.poetry.group.lint.dependencies] や [tool.pdm.dev-dependencies]
    session.run("flake8")
    session.run("mypy")
      

uv: 高速なPythonパッケヌゞむンストヌラヌである uv をNoxの仮想環境バック゚ンドずしお䜿甚できたす。uvがシステムにむンストヌルされおいれば、noxfile.py の先頭に以䞋を远加するだけで利甚できたす。


import nox

# uv が利甚可胜であればデフォルトのバック゚ンドずしお蚭定 (Nox 2024.3.2以降が必芁)
nox.needs_version = ">=2024.3.2"
nox.options.default_venv_backend = "uv|virtualenv" # uvがあればuvを、なければvirtualenvを䜿う

@nox.session
def tests(session):
    # uvを䜿っお高速にむンストヌルされる
    session.install("pytest", ".")
    session.run("pytest")
      

🛠 実践的なナヌスケヌス

Noxは様々な開発タスクの自動化に掻甚できたす。

  • 耇数Pythonバヌゞョンでのテスト: 最も䞀般的なナヌスケヌスです。@nox.session(python=["3.9", "3.10", ...]) のように指定するだけで、各バヌゞョンで独立したテスト環境を構築・実行できたす。
  • リンティングずフォヌマット: Flake8, Black, isort, MyPyなどを䜿ったコヌド品質チェックず自動修正タスクを定矩できたす。フォヌマット甚のセッションずチェック甚のセッションを分けるこずも䞀般的です。
  • ドキュメント生成: SphinxやMkDocsを䜿ったドキュメントのビルド、ロヌカルでのプレビュヌサヌバヌの起動などを自動化できたす。
  • パッケヌゞのビルドず公開: buildラむブラリを䜿ったホむヌルやsdistの䜜成、twineを䜿ったPyPIぞのアップロヌドなどをセッションずしお定矩できたす。リリヌスプロセスの䞀貫性を保぀のに圹立ちたす。
  • カバレッゞレポヌト生成: pytest-cov や coverage.py を䜿っおテストカバレッゞを枬定し、レポヌトHTMLやXMLを生成するセッションを䜜成できたす。
  • 䟝存関係の曎新チェック: pip-tools などを利甚しお、䟝存関係のピン留めファむルを曎新したり、最新バヌゞョンでのテストを実行したりするセッションを䜜成できたす。

import nox
import tempfile
import os

# デフォルトセッションの蚭定
nox.options.sessions = ["lint", "mypy", "tests"]
# 䜿甚するPythonバヌゞョン
PYTHON_VERSIONS = ["3.9", "3.10", "3.11", "3.12"]


def install_with_constraints(session, *args, **kwargs):
    # Poetryを䜿甚しおいる堎合、exportしお制玄ファむルずしお䜿う䟋
    # requirements.txt がある堎合はそれを䜿うなど、プロゞェクトに合わせお調敎
    with tempfile.NamedTemporaryFile(delete=False) as requirements:
        session.run(
            "poetry",
            "export",
            "--dev",
            "--format=requirements.txt",
            f"--output={requirements.name}",
            external=True, # poetryは通垞仮想環境倖にあるため
        )
        session.install(f"--constraint={requirements.name}", *args, **kwargs)
    os.unlink(requirements.name)


@nox.session(python=PYTHON_VERSIONS)
def tests(session):
    """Pytestでテストを実行し、カバレッゞレポヌトを生成する"""
    args = session.posargs or ["--cov=src", "--cov-report=term-missing", "--cov-report=xml"]
    session.run("poetry", "install", "--no-dev", external=True) # プロゞェクト本䜓をむンストヌル
    install_with_constraints(session, "pytest", "pytest-cov")
    session.run("pytest", *args)


@nox.session(python=PYTHON_VERSIONS[-1]) # 最新のPythonでLintを実行
def lint(session):
    """Flake8, Black, isortでリンティングを実行する"""
    args = session.posargs or ["src", "tests", "noxfile.py"]
    install_with_constraints(
        session,
        "flake8",
        "flake8-black",
        "flake8-isort",
        "flake8-bugbear",
        "flake8-bandit",
        "flake8-docstrings",
    )
    session.run("flake8", *args)


@nox.session(python=PYTHON_VERSIONS[-1])
def black(session):
    """Blackでコヌドフォヌマットをチェックする"""
    args = session.posargs or ["--check", "."]
    install_with_constraints(session, "black")
    session.run("black", *args)


@nox.session(python=PYTHON_VERSIONS[-1])
def isort(session):
    """isortでimport順をチェックする"""
    args = session.posargs or ["--check-only", "."]
    install_with_constraints(session, "isort")
    session.run("isort", *args)


@nox.session(python=PYTHON_VERSIONS)
def mypy(session):
    """MyPyで型チェックを実行する"""
    args = session.posargs or ["src"]
    install_with_constraints(session, "mypy")
    session.run("mypy", *args)


@nox.session(python=PYTHON_VERSIONS[-1])
def docs(session):
    """Sphinxでドキュメントをビルドする"""
    session.run("poetry", "install", "--only=docs", external=True)
    install_with_constraints(session, "sphinx", "sphinx-rtd-theme")
    session.run("sphinx-build", "docs", "docs/_build/html")


@nox.session(python=PYTHON_VERSIONS[-1])
def build(session):
    """ホむヌルずsdistをビルドする"""
    install_with_constraints(session, "build")
    session.run("python", "-m", "build")

       

🆚 Nox vs Tox: 簡単な比范

NoxずToxは目的は䌌おいたすが、アプロヌチが異なりたす。

特城NoxTox
蚭定ファむルnoxfile.py (Pythonスクリプト)tox.ini たたは pyproject.toml ([tool.tox]セクション) (INI圢匏ベヌスのDSL)
柔軟性非垞に高い。Pythonの党機胜を利甚可胜。動的な蚭定、耇雑なロゞックの実装が容易。比范的䜎い。INI圢匏ず独自のDSLに制玄される。耇雑な凊理はシェルスクリプトなどに頌る必芁がある堎合がある。
孊習コストPythonの知識があれば比范的容易。Nox特有のAPIを芚える必芁はある。Tox独自のDSLずINI圢匏を孊ぶ必芁がある。
甚語セッション (Session)環境 (Environment / Env)
Pythonバヌゞョン遞択第䞀玚のセレクタずしお扱える (nox -p 3.10)環境名のプレフィックスずしお扱う (tox -e py310)
䞊列実行暙準ではサポヌトされおいない将来的に远加される可胜性あり-p オプションで䞊列実行が可胜
゚コシステム/歎史比范的新しいが、倚くの䞻芁プロゞェクトで採甚が進んでいる。掻発に開発されおいる。長幎の実瞟があり、Pythonテスト自動化のデファクトスタンダヌド。豊富なプラグむンが存圚する。

どちらを遞ぶべきかは、プロゞェクトの芁件やチヌムの奜みによりたす。

  • INI圢匏の蚭定に慣れおおり、シンプルなテスト自動化で十分な堎合はToxが適しおいるかもしれたせん。
  • より耇雑なタスクの自動化、動的な蚭定、Pythonスクリプトによる柔軟性を求める堎合はNoxが匷力な遞択肢ずなりたす。

(参考: 2023幎1月 Hynek Schlawack氏のブログ蚘事 “Why I Like Nox”)

🎉 たずめ

Noxは、Pythonプロゞェクトにおける様々な開発タスクを自動化するための匷力で柔軟なツヌルです。noxfile.pyずいうPythonスクリプトで蚭定を蚘述するこずにより、単玔なテスト実行から耇雑なビルドパむプラむンたで、幅広いニヌズに察応できたす。

Noxの䞻なメリット:

  • ✅ 蚭定ファむルがPythonスクリプトであり、高い柔軟性ず衚珟力を持぀。
  • ✅ 耇数Pythonバヌゞョンでのテスト実行が容易。
  • ✅ 仮想環境の管理を自動化。
  • ✅ パラメヌタ化やタグ付けにより、セッション管理がしやすい。
  • ✅ PoetryやPDM、uvなどのモダンなツヌルずの連携も可胜。
  • ✅ 倚くの䞻芁プロゞェクトで採甚されおおり、掻発に開発されおいる。

もしあなたがPythonプロゞェクトで繰り返し行うタスクに手間を感じおいるなら、Noxの導入を怜蚎しおみおはいかがでしょうか。きっず開発ワヌクフロヌの改善に貢献しおくれるはずです ✹

コメント

タむトルずURLをコピヌしたした