nikkie-ftnextの日記

イベントレポートや読書メモを発信

依存ライブラリのバージョン違いのテストをHatchで実現しました

はじめに

七尾百合子さん、お誕生日 20日 おめでとうございます! nikkieです。

自作のsphinx-revealjs-copycodeの開発の中での学びです。

目次

sphinx-revealjs v2とv3の両方でテストしたい

sphinx-revealjs-copycodeはv0.2.0sphinx-revealjs v3 をサポートしました1
v2とv3の両方をサポートしたわけです。

sphinx-revealjsは v2 -> v3 でバンドルされるReveal.jsのパスが変わっています。
https://sphinx-revealjs.readthedocs.io/en/stable/upgrade/3.x/#change-bundled-revealjs-path

sphinx-revealjs-copycodeは、sphinx-revealjsの静的ファイルの中にCopyCodeプラグインを追加する実装です。
sphinx-revealjsのバージョンを見て、CopyCodeプラグインを配置する必要がありました2

  • v2ではrevealjs4の下に配置
  • v3ではrevealjsの下に配置

そのため、テストにはsphinx-revealjs v2とv3の2環境が必要となりました。
ここにHatchを使いました。

Hatchを使って依存ライブラリのバージョンが異なるテスト環境を用意

Hatch3用途ごとに仮想環境を用意できます。
uvや人力4ではプロジェクトで扱う仮想環境は1つだけですが、Hatchは複数の仮想環境をラップしてくれています。
これを使ってテスト向けの仮想環境を2つ作ります

% hatch --version
Hatch, version 1.14.0

pyproject.toml

[tool.hatch.envs.test-sphinx-revealjs2]
dependencies = ["pytest", "sphinx-revealjs>=2,<3"]
[tool.hatch.envs.test-sphinx-revealjs2.scripts]
run = "pytest -s -v"

[tool.hatch.envs.test-sphinx-revealjs3]
dependencies = ["pytest", "sphinx-revealjs>=3,<4"]
[tool.hatch.envs.test-sphinx-revealjs3.scripts]
run = "pytest -s -v"

sphinx-revealjs v2の仮想環境とv3の仮想環境の2つを作成しました。
sphinx-revealjs-copycodeはどちらの仮想環境にもeditable installされます。

1つのコマンドtask testで両環境のテストが流れるようにしました。
プロジェクト直下の仮想環境にtaskipy5を含む依存をインストールしています。

[tool.taskipy.tasks]
test = "task test_v2 && task test_v3"
test_v2 = "hatch env run -e test-sphinx-revealjs2 run"
test_v3 = "hatch env run -e test-sphinx-revealjs3 run"

依存ライブラリのバージョンを見てテストをスキップ(pytest)

テストコード6にはpytestのマーカ pytest.mark.skipif を使いました。

from importlib.metadata import version

import pytest


@pytest.mark.skipif(
    not version("sphinx-revealjs").startswith("2."),
    reason="requires sphinx-revealjs v2",
)
@pytest.mark.sphinx("revealjs", testroot="default")
def test_arrange_copycode_plugin_sphinx_revealjs_v2(
    app: SphinxTestApp,
) -> None:
    app.build()

    assert (app.outdir / "_static/revealjs4/plugin/copycode").exists()


@pytest.mark.skipif(
    not version("sphinx-revealjs").startswith("3."),
    reason="requires sphinx-revealjs v3",
)
@pytest.mark.sphinx("revealjs", testroot="default")
def test_arrange_copycode_plugin_sphinx_revealjs_v3(
    app: SphinxTestApp,
) -> None:
    app.build()

    assert (app.outdir / "_static/revealjs/plugin/copycode").exists()

importlib.metadata.versionで環境のsphinx-revealjsのバージョンを取得し、スキップ判定しています。

  • sphinx-revealjs v2の環境
    • v2のテスト(revealjs4というパスの検証)のみ実行
    • v3向けのテスト(revealjsというパスの検証)をスキップ
  • sphinx-revealjs v3の環境
    • v2向けのテスト(revealjs4というパスの検証)をスキップ
    • v3のテスト(revealjsというパスの検証)のみ実行

終わりに

sphinx-revealjs-copycodeの開発の中で、sphinx-revealjs v2とv3それぞれでテストを実行したくなりました。

  • 1プロジェクトに複数の仮想環境を用意できるHatchで2つのテスト環境を用意
  • pytestのマーカで、sphinx-revealjsのバージョンを見てスキップ

uvが圧倒的に注目されていますが、今回のユースケースは(私の知る範囲の)uv単体では難しくて7Hatchが輝く状況だったように思います。
とはいえ依存ライブラリのバージョンが異なるテスト環境を用意することは、そんなに頻繁にはないでしょう。


  1. nikkie v2025.02 リリースのお知らせ - nikkie-ftnextの日記 で言及しています
  2. これはv0.2.0時点の話で、今の私はsphinx-revealjs-copycodeの方にCopyCodeプラグインを置くという選択肢も持てています
  3. Hatchとはなんぞやという方には
  4. Pythonチュートリアルで案内される、開発者が仮想環境を操作する方法です
  5. taskipyはいいぞ
  6. このテストコードを読む際の参考記事
  7. 名前だけ聞いていたtox-uvの出番なのかも