こんにちは。テックリードの私だ。
日々のPython開発、お疲れ様である。CI/CDパイプラインが突然の依存関係解決エラーで赤く染まり、`setup.py` の奥底にある古いボイラープレートコードと格闘した夜や、`requirements.txt` の野良パッケージのバージョン競合に頭を抱えた経験はないだろうか?
ネットを検索すれば「とりあえず `pip install` しろ」「`requirements.txt` を作れ」という情報の残骸があふれているが、現代の大規模開発や複数ツール(Poetry、uv、pip)が混在するエコシステムにおいて、そのアプローチは技術的負債の温床でしかない。
今日は、Pythonパッケージングの歴史的転換点であり、将来を見据えたインフラ設計の必須教養である PEP 621 と、それを軸にした `pyproject.toml` への完全移行について、アーキテクトの視点から徹底的に解説しよう。
—
なぜ `setup.py` と `requirements.txt` は「悪」なのか?
かつて、Pythonのビルドバックエンドは `setuptools` の独壇場であり、メタデータはすべて命令型のスクリプトである `setup.py` に記述されていた。これは「Pythonコードを実行してパッケージ情報を動的に生成する」という設計であり、セキュリティ上のリスクや、静的解析ツール(IDEや依存関係チェッカー)にとってのブラックボックスを生んだ。
また、開発環境用の `requirements.txt` と、配布用の `setup.py`(あるいは `setup.cfg`)で依存関係の定義が二重管理され、バージョンが乖離するという地獄を何度も見てきたはずだ。
PEP 621 がもたらした「宣言的メタデータ」の革命
Python Packaging Authority (PyPA) が策定した PEP 621 は、プロジェクトのメタデータ(プロジェクト名、バージョン、依存関係など)を `pyproject.toml` の `[project]` テーブルに静的かつ標準化された形式で記述することを定めた。
これにより、以下の圧倒的なメリットがもたらされる。
1. ツールの非依存性: Poetryを使おうが、新進気鋭の超高速ツール `uv` を使おうが、あるいは伝統的な `pip` + `setuptools/hatchling` を使おうが、メタデータの書き方が共通化される。
2. 高速な静的解析: コードを実行せずにTOMLをパースするだけなので、IDEの補完やセキュリティスキャンの速度が劇的に向上する。
3. サプライチェーンセキュリティの強化: 動的なスクリプト実行を排除することで、悪意あるコードの混入リスクを根本から断つ。
—
現場で即採用すべき:PEP 621 準拠 `pyproject.toml` のベストプラクティス
口で言うだけではプロのアーキテクトとは言えない。実際に、私がコンサルティングした現場で標準採用している、最も堅牢で拡張性の高い `pyproject.toml` の実例を提示しよう。ビルドバックエンドには、モダンで高速な `hatchling` を採用している。
[build-system]
ビルドを処理するバックエンドを指定。hatchlingは高速かつPEP 621に完全準拠している
requires = [“hatchling>=1.21.0”]
build-backend = “hatchling.build”
[project]
— 必須メタデータ —
name = “enterprise-core-engine”
version = “2.4.1”
description = “高スループットな非同期データ処理パイプラインの中核エンジン”
readme = “README.md”
requires-python = “>=3.11”
license = { text = “MIT” }
authors = [
{ name = “DevOps Architecture Team”, email = “devops-lead@example.com” }
]
classifiers = [
“Programming Language :: Python :: 3.11”,
“Programming Language :: Python :: 3.12”,
“License :: OSI Approved :: MIT License”,
“Operating System :: POSIX :: Linux”,
]
— 本番稼働に必要なコア依存関係(PEP 621標準) —
dependencies = [
“fastapi>=0.110.0”,
“pydantic>=2.6.0”,
“uvicorn[standard]>=0.27.0”,
“sqlalchemy>=2.0.25”,
“asyncpg>=0.29.0”,
]
— 開発・テスト・CI環境用のオプショナル依存関係(グループ化) —
[project.optional-dependencies]
dev = [
“pytest>=8.0.0”,
“pytest-asyncio>=0.23.0”,
“pytest-cov>=4.1.0”,
“ruff>=0.2.1”, # 次世代の超高速リンター/フォーマッター
“mypy>=1.8.0”,
]
— ツール固有の設定領域 —
[tool.hatch.build.targets.wheel]
パッケージに含めるソースコードのルートディレクトリを明示
packages = [“src/engine”]
[tool.ruff]
Ruffの設定:コードベース全体の品質を機械的に担保する
target-version = “py311”
line-length = 88
[tool.ruff.lint]
select = [“E”, “F”, “I”, “N”, “W”, “B”] # Pyflakes, pycodestyle, isort, pep8-naming, flake8-bugbear
ignore = []
[tool.pytest.ini_options]
テストランナーの設定
asyncio_mode = “auto”
testpaths = [“tests”]
この構成の美しい点は、「パッケージの定義」と「開発ツールの設定」が1つのファイルに美しく直交して配置されている点にある。
—
主要ツール(pip / Poetry / uv)での運用と移行戦略
「で、ウチのチームはどのツールを使えばいいんだ?」という疑問に答えよう。結論から言うと、PEP 621に準拠していれば、ツールを自由に行き来できる。それぞれのツールにおけるアプローチを解説する。
1. pip (現代的運用: `pip install .`)
古い `setup.py` は捨てよ。PEP 621に対応したプロジェクトであれば、`pip` は単なるインストーラーとして完璧に機能する。
開発モードでコア依存関係とdevグループの依存関係を同時にインストール
pip install -e “.[dev]”
アーキテクトの知見: `requirements.txt` を手動で書く時代は終わった。CI環境などでは、後述する `uv` や Poetry が生成するロックファイルを活用すべきだが、最小限の環境であれば `pip install .[dev]` で十分戦える。
2. Poetry (依存関係解決の王道)
Poetryは独自の設定 (`[tool.poetry]`) を長年推進してきたが、最新のバージョンではPEP 621への歩み寄りを見せている。しかし、Poetryの真価は洗練された依存関係解決エンジンと `poetry.lock` にある。
PEP 621に完全移行しつつPoetryを使う場合、メタデータは `[project]` に寄せ、Poetry固有の設定は `[tool.poetry]` に分離・共存させることが可能だ。
PoetryでPEP 621準拠のプロジェクトを初期化・同期する
poetry install –with dev
3. uv (ゲームチェンジャー:Rust製超高速パッケージマネージャー)
現在、すべてのPythonインフラエンジニアが注目すべきなのが、Astral社が開発した `uv` である。`pip` や `poetry` の何十倍もの速度で依存関係を解決・インストールし、仮想環境の作成すら一瞬で終わる。
`uv` は最初から PEP 621 と `pyproject.toml` をファーストクラス市民としてサポートしている。
仮想環境の作成(一瞬で終わる)
uv venv –python 3.11
依存関係の高速インストール(PEP 621の optional-dependencies も完全網羅)
uv pip install -e “.[dev]”
または、uv独自のプロジェクト管理機能を使う場合(pyproject.tomlを自動認識)
uv sync –extra dev
—
チーム開発の生産性を爆発させる「隠しコマンド」とIDE設定
テックリードとして、チーム全体の開発スピードを極限まで引き上げるために設定している「秘伝のタレ」を伝授しよう。
VS Code / PyCharm の保存時自動フォーマット連携
Ruffとpyproject.tomlを組み合わせることで、保存するだけでPEP 8準拠かつ完璧にソートされたコードが生まれる。VS Codeの `.vscode/settings.json` に以下を記述し、チーム全体で共有せよ。
{
// 保存時に自動でコード整形とインポートの整理を実行
“editor.codeActionsOnSave”: {
“source.fixAll.ruff”: “explicit”,
“source.organizeImports.ruff”: “explicit”
},
// デフォルトのフォーマッターをRuffに固定
“[python]”: {
“editor.defaultFormatter”: “charliermarsh.ruff”,
“editor.formatOnSave”: true
}
}
CI/CD(GitHub Actions)でのキャッシュ戦略の極み
`uv` や `pip` をCIで使う際、依存関係のインストールをキャッシュすることで、パイプラインの実行時間を数秒単位に短縮できる。以下のGitHub Actionsのスニペットを模範とせよ。
name: CI Pipeline
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: “3.11”
# 超高速な uv をGitHub Actions環境にインストール
- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true
cache-dependency-file: “pyproject.toml”
# 仮想環境の作成と依存関係のインストール(キャッシュが効くため秒速で終わる)
- name: Install dependencies
run: |
uv venv
uv pip install -e “.[dev]”
# テストの実行
- name: Run Pytest with Coverage
run: |
.venv/bin/pytest –cov=src
—
まとめ:技術的負債を断ち切り、未来への投資を
`setup.py` や `requirements.txt` の泥沼から抜け出し、PEP 621 準拠の `pyproject.toml` へ移行すること。それは単なる「お作法」の変更ではない。
- どのパッケージマネージャー(pip, Poetry, uv)を使うかという宗教戦争から解放され、
- ツール間の移行コストを限りなくゼロにし、
- 静的解析やCI/CDの速度を極限まで高める――
これこそが、現代のプロフェッショナルな開発チームが手に入れるべき「真の開発生産性」である。
さあ、今すぐあなたのプロジェクトの `setup.py` を削除し、`pyproject.toml` を美しく書き直そう。チームメンバーから感謝の歓声が上がることを、私は保証する。