uvxでポータブルなPythonアプリケーションを作る
はじめに
これは「mohikanz Advent Calendar 2025」6日目の記事です。
Pythonでアプリケーションを配布したいときの悩み
Pythonで作成したCLIツールやスクリプトを同僚などに配布したいとき、以下のような悩みに遭遇すると思います。
- 相手の環境にPythonがインストールされているとは限らない
- Pythonや依存ライブラリのバージョンがばらばら
- 「まず仮想環境を構築して...」といった手順は、非エンジニアや非Pythonエンジニアには敷居が高い
こういった悩み自体はPython以外でも起こりうるものですが、Pythonにはお手軽なパッケージマネージャが標準で存在しないことが悩みを加速します。
uvとは
uvxの前に、前提となるuvというパッケージマネージャについて説明します。
詳しい説明記事は世の中にたくさんあるので、要点を掻い摘まんで説明すると、
- Rustで記述された高速なPython向けパッケージマネージャ
- Pythonのバージョン管理 (pyenv相当)、仮想環境管理 (venv相当)、パッケージ管理 (pip/poetry等に相当) をこれ1つで完結できる
といった特徴があります。
uvxとは
uvxはuvに含まれるサブコマンドで、単一のコマンドからPythonパッケージを実行可能なアプリケーションとして動かすための仕組みを提供します。
Node.jsのエコシステムにおけるnpxに相当するものと考えると分かりやすいかもしれません。
pipenv/poetryといった他のパッケージマネージャを使う場合と比べて、エンドユーザにPython自体の環境構築・管理を要求しないのが大きな利点です。
また、GitHub上に存在するアプリケーションを直接実行することも可能です。
uvxが実際に利用されているアプリケーションの例としてSerena MCP Serverがありますが、コードをcloneすることなく、以下のように実行することができます。
uvx --from git+https://github.com/oraios/serena serena start-mcp-server --help
ポータブルなアプリケーションの作り方
以下に、実際にuvxを用いてアプリケーションを作成する場合の例を示します。
全体のソースコードは https://github.com/kiakiraki/uvx-demo に格納されています。
プロジェクト全体は以下のように構成されています。
uvx-demo/
├── pyproject.toml # プロジェクト設定
├── README.md # 説明
└── uvx_demo/ # メインパッケージ
├── __init__.py
└── main.py # CLI エントリーポイント
uv自体の細かな操作方法についてはこの場では説明しませんので、必要に応じて公式ドキュメントなどを参照してください。
プロジェクト設定ファイルの作成
pyproject.toml にプロジェクト情報とエントリーポイントを定義します。
[project]
name = "uvx-demo"
version = "0.1.0"
# 依存パッケージがあればここに列挙する
dependencies = ["pyfiglet"]
[project.scripts]
uvx-demo = "uvx_demo.main:main"
ポイントは [project.scripts] です。
-
uvx-demoが「コマンド名」 -
"uvx_demo.main:main"が「実際に呼び出される関数」
これにより、uvx_demo.main モジュールの main() 関数が起動されるようになります。
パッケージ構造の作成
Pythonパッケージのディレクトリを作成します。
mkdir uvx_demo
uvx_demo/__init__.py を作成します。
"""uvx-demo: A portable Python CLI application demo using uvx."""
__version__ = "0.1.0"
メインアプリケーションの実装
uvx_demo/main.py を作成します。
"""Main CLI application for uvx-demo."""
import pyfiglet
def main() -> None:
"""Display uvx ASCII art."""
# Generate ASCII art using pyfiglet
ascii_art = pyfiglet.figlet_format("UVX", font="slant")
print(ascii_art)
print("Welcome to uvx-demo!")
print("This is a portable Python CLI application that can be run with uvx.")
if __name__ == "__main__":
main()
ローカルでの実行確認
アプリケーションを手元で実行してみます。
$ uv run uvx-demo
__ ___ ___ __
/ / / / | / / |/ /
/ / / /| | / /| /
/ /_/ / | |/ // |
\____/ |___//_/|_|
Welcome to uvx-demo!
This is a portable Python CLI application that can be run with uvx.
uvxによる直接実行
以下のような一行でプログラムを実行できます。
uvx --from git+https://github.com/kiakiraki/uvx-demo uvx-demo
応用的な使い方
GitHub上の特定ブランチやタグを指定して実行する
uvxの--fromには、ブランチ名・タグ名・コミットハッシュを指定することができます。
uvx --from git+https://github.com/kiakiraki/uvx-demo@ブランチ名 uvx-demo
uvx --from git+https://github.com/kiakiraki/uvx-demo@タグ名 uvx-demo
uvx --from git+https://github.com/kiakiraki/uvx-demo@コミットハッシュ uvx-demo
一つのリポジトリ内に複数のコマンドを同居させる
アプリケーションがサブディレクトリに存在する場合
例えば、リポジトリ構成が以下のようになっているとします。
repo-root/
├── cli-tool/
│ ├── pyproject.toml
│ └── cli_tool/...
└── web-app/
├── pyproject.toml
└── web_app/...
GitHub上のサブディレクトリを指定する場合は、以下のようにsubdirectoryオプションを利用します。
# cli-tool サブディレクトリを対象に実行
uvx --from git+https://github.com/owner/repo@main#subdirectory=cli-tool cli-command
# web-app サブディレクトリを対象に実行
uvx --from git+https://github.com/owner/repo@main#subdirectory=web-app web-command
1つのサブディレクトリに複数コマンドがある場合
以下のように、一つのディレクトリに複数のコマンドが存在するとします。
uvx-multi-app/
├── pyproject.toml
└── uvx_multi_app/
├── __init__.py
├── cli.py # CLI 用エントリポイント
└── web.py # Web UI 用エントリポイント
このパターンでは、pyproject.tomlの[project.scripts]で複数スクリプトを定義します。
[project]
name = "uvx-multi-app"
version = "0.1.0"
[project.scripts]
my-cli = "uvx_multi_app.cli:main"
my-web = "uvx_multi_app.web:main"
実行時には、以下のようにコマンドを選択します。
# CLI 版を実行
uvx --from git+https://github.com/owner/uvx-multi-app my-cli
# Web UI 版を実行
uvx --from git+https://github.com/owner/uvx-multi-app my-web
まとめ
uvxを活用することで、Pythonアプリケーションの配布はとてもシンプルになります。
- 開発者: 依存関係地獄や環境差異のサポートから解放される。
- ユーザー: 環境構築なしで、コマンド一発でツールを利用できる。
Pythonで業務効率化ツールなどを作ったら、是非uvxを利用してみてください。
Discussion