uvx pytestでModuleNotFoundErrorになる理由|uv runでプロジェクト環境を使う

uv runはプロジェクトの.venvへ進み、uvx pytestは隔離ツール環境へ進む違いを示した模式図

uv addでアプリの依存パッケージを入れたのに、uvx pytestではModuleNotFoundErrorになる。その場合は、まずpyproject.tomlがあるプロジェクトルートでpytestを開発依存に追加し、プロジェクト環境から実行します。

uv add --dev pytest
uv run -m pytest

uv run pytestでもプロジェクトの.venvを使います。この記事では、なぜuvxだけでimportに失敗することがあるのかと、-mの意味を混同せずに整理します。

目次

原因はpytestではなく、実行環境が分かれていること

uvxはuv tool runの別名です。uv公式のToolsでは、ツールを一時的な仮想環境に入れ、現在のプロジェクトから隔離して実行すると説明されています。そのため、プロジェクトの.venvにある依存パッケージは、uvx pytestの環境へ自動では共有されません。

一方、uv runはプロジェクト環境を作成・更新したうえでコマンドを実行します。pytestやmypyのように、対象プロジェクトやその依存へアクセスする必要があるツールについて、uv公式もuvxではなくuv runを案内しています。

コマンド使う環境プロジェクト依存
uvx pytest隔離されたtool環境.venvから自動共有されない
uv run pytestプロジェクトの.venvpyproject.tomlの宣言を利用
uv run -m pytestプロジェクトの.venv同上。さらにPythonの-mでpytestを起動

正しい設定手順

既存プロジェクトではpytestだけを開発依存へ追加する

pyproject.tomlがすでにあるなら、uv initをやり直す必要はありません。プロジェクトルートで次を実行します。

uv add --dev pytest
uv run -m pytest

アプリ本体が使うパッケージも、グローバル環境へ入れるのではなくプロジェクト依存として宣言します。たとえばidnaが実行時に必要なら次の形です。

uv add idna==3.10

uv公式の依存関係管理によると、通常のuv addは依存をproject.dependenciesへ追加し、uv add --devは開発用のdevグループへ追加します。uv runでは既定でdevグループも環境へ含まれます。

空のフォルダーから最小再現する

動作を切り分けたい場合だけ、新しい空フォルダーで次を実行します。既存プロジェクトに対してこの初期化手順は不要です。

mkdir uvx-pytest-demo
cd uvx-pytest-demo
uv init
uv add idna==3.10
uv add --dev pytest

test_dependency.pyを作り、次の内容を保存します。

import idna

def test_dependency():
    assert idna.encode("example.com") == b"example.com"

続けて、隔離ツール環境とプロジェクト環境を比較します。

uvx pytest -q
uv run -m pytest -q
uv run pytest -q

編集側の再現では、Windows、uv 0.13.0、CPython 3.13.7、pytest 9.1.1、idna 3.10を使用しました。uv tool run pytest -qはテスト収集時に次のエラーとなり、終了コードは2でした。uvxはuv tool runと同じ動作なので、同じ隔離の問題を確認できます。

ModuleNotFoundError: No module named 'idna'

同じ条件でuv run -m pytest -qとuv run pytest -qは、どちらも1 passedでした。ツール側とプロジェクト側のPythonはともに3.13.7へそろえているため、この再現での差はPython版ではなく、idnaを持つ環境を使ったかどうかです。実行時間は環境やキャッシュの影響を受けるため、性能差の根拠にはしません。

プロジェクト環境を直接確認するには、次のようにPythonの場所と依存の版を表示できます。編集側の再現では、プロジェクトの.venv配下のPythonと3.10が表示されました。

uv run python -c "import sys, idna; print(sys.executable); print(idna.__version__)"
uv runとuvxの環境差を順に示す5段階
コマンドの違いは、pytestをどのPython環境から起動するかの違いです。 · 図を拡大

uv runへの変更と-mの効果は別

今回の中心的な修正は、uvxからuv runへ変えてプロジェクト環境を使うことです。-mは依存パッケージを隔離ツール環境へ追加する機能ではありません。

pytest公式によると、pytestコマンドとpython -m pytestの主な違いは、後者が現在のディレクトリをsys.pathへ追加する点です。uvのuv run -mはpython -m相当であり、このPython標準の起動方法をプロジェクト環境内で使います。

  • uv run:どの環境で動かすかを決める
  • -m pytest:そのPythonでpytestモジュールをどう起動するかを決める

したがって、uv run pytestでもプロジェクト依存は利用できます。一方、現在ディレクトリをimport探索に含めたいケースでは、uv run -m pytestとの差が表れることがあります。

uvx pytestですべて失敗するわけではない

uvxを使うと必ず全テストが失敗する、という説明は正確ではありません。uv公式のツールガイドも、srcディレクトリを使わない単純なflat layoutでは、プロジェクト自体のインストールが不要なためuvxで動く場合があると説明しています。

この記事で扱っているのは、project.dependenciesに宣言した第三者パッケージがプロジェクトの.venvにはある一方、隔離されたtool環境にはなく、テスト収集時にimportできないケースです。

uv run -m pytestでも直らないとき

実行環境を直してもエラーが残るなら、次の別原因を順に確認します。

  • 依存宣言漏れ:uv run python -c "import パッケージ名"でも失敗するなら、そのパッケージがproject.dependenciesなど適切な依存グループに入っているか確認します。
  • srcレイアウトの自作パッケージが未インストール:src/your_packageをimportする構成では、プロジェクトがパッケージとして環境へ入る設定になっているか確認します。
  • 同名ファイルによる隠蔽:pytest.py、idna.py、または依存パッケージと同名のフォルダーがプロジェクト内にないか確認します。
  • 違う場所から実行:pyproject.tomlのあるプロジェクトルートで実行しているか確認します。

PYTHONPATHの無条件追加、グローバルなpip install、uv cache cleanを最初の修正にはしません。前二者は依存宣言の不備を隠しやすく、uv cache cleanで隔離tool環境を削除しても次回は自動作成されるため、プロジェクト依存が共有されない原因は直りません。まずuv runで正しいプロジェクト環境を使い、そこでimportできるかを確かめるのが近道です。

まとめ

uv addした依存を使うpytestは、原則としてプロジェクトルートからuv run -m pytestで実行します。uv run pytestも同じプロジェクト環境を使います。uvx pytestは便利ですが、隔離されたツール環境で動くため、プロジェクトの.venvにある依存を当然には参照できません。

公式資料

仕様確認日:2026年10月16日

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次