Documentation Python

Pythonプロジェクトの依存関係を管理するrequirements.txtファイルを自動生成する方法を解説します。pip freezepipreqsの使い分けから、モダンなパッケージ管理ツールまで紹介します。

requirements.txt生成ツールの比較

ツール対象長所短所
pip freeze環境内全パッケージ確実に環境を再現不要なパッケージも含む
pipreqs使用中のパッケージのみ最小限の依存関係動的インポートを見逃す
pip-tools依存関係ツリーロック機能付き追加学習が必要
poetryプロジェクト全体モダンな管理設定ファイルが異なる

pip freeze の使い方

基本的な使用方法

# 環境内のすべてのパッケージをリストアップ
pip freeze

# requirements.txt に保存
pip freeze > requirements.txt

# 出力例
# certifi==2024.2.2
# charset-normalizer==3.3.2
# numpy==1.26.4
# pandas==2.2.1
# requests==2.31.0

requirements.txt からインストール

# 依存関係を一括インストール
pip install -r requirements.txt

# 特定のファイルからインストール
pip install -r requirements-dev.txt

pip freeze の問題点

# 問題: 環境内のすべてのパッケージが含まれる
pip freeze > requirements.txt
# 結果: 100個以上のパッケージがリストされることも

# 問題: 開発用パッケージも混在
# pytest, black, flake8 などが本番用ファイルに含まれてしまう

# 問題: 仮想環境を使用していない場合
# システム全体のパッケージが記録される

ベストプラクティス: 仮想環境と組み合わせる

# 仮想環境を作成
python -m venv venv

# 仮想環境を有効化
# Windows
venv\Scripts\activate
# macOS/Linux
source venv/bin/activate

# 必要なパッケージのみをインストール
pip install requests pandas numpy

# クリーンなrequirements.txtを生成
pip freeze > requirements.txt

pipreqs の使い方

pipreqsは、プロジェクトのソースコードを分析し、実際にインポートされているパッケージのみをリストアップします。

インストールと基本使用

# pipreqs をインストール
pip install pipreqs

# プロジェクトディレクトリでrequirements.txtを生成
pipreqs /path/to/project

# カレントディレクトリの場合
pipreqs .

# 既存のファイルを上書き
pipreqs . --force

# エンコーディングを指定(Windows向け)
pipreqs . --encoding=utf-8

pipreqs のオプション

# 差分のみを表示(ファイルは作成しない)
pipreqs . --diff requirements.txt

# 標準出力に表示
pipreqs . --print

# 特定のディレクトリを除外
pipreqs . --ignore tests,docs,venv

# 出力ファイル名を指定
pipreqs . --savepath requirements-prod.txt

# デバッグモード
pipreqs . --debug

pipreqs の仕組み

# pipreqs はソースコードの import 文を解析
# 以下のようなインポートを検出

import requests  # requests パッケージ
import pandas as pd  # pandas パッケージ
from flask import Flask  # flask パッケージ
from sklearn.model_selection import train_test_split  # scikit-learn

# 標準ライブラリは除外される
import os
import sys
import json

pipreqs の注意点

# 動的インポートは検出されない
module_name = "requests"
module = __import__(module_name)  # 検出されない

# 条件付きインポートは検出される可能性がある
try:
    import optional_package  # 検出される
except ImportError:
    pass

# requirements.txt の内容を確認し、必要に応じて手動で追加

pip-tools の使い方

pip-toolsは、依存関係を明確に管理し、ロックファイルを生成できます。

インストールと基本使用

# pip-tools をインストール
pip install pip-tools

# requirements.in を作成(直接依存のみ)
echo "requests" > requirements.in
echo "pandas" >> requirements.in
echo "flask" >> requirements.in

# requirements.txt を生成(依存関係も含む)
pip-compile requirements.in

# 依存関係を同期(環境に反映)
pip-sync requirements.txt

requirements.in と requirements.txt

# requirements.in(手動で管理)
# 直接依存するパッケージのみを記述
requests>=2.28.0
pandas~=2.0
flask

# 開発用依存関係は別ファイルに
# requirements-dev.in
-r requirements.in
pytest
black
flake8
# requirements.txt を生成
pip-compile requirements.in -o requirements.txt

# 開発用も生成
pip-compile requirements-dev.in -o requirements-dev.txt

# アップグレード可能なパッケージを更新
pip-compile --upgrade requirements.in

依存関係の分離

環境別の requirements ファイル

project/
├── requirements/
│   ├── base.txt       # 共通の依存関係
│   ├── production.txt # 本番環境用
│   ├── development.txt # 開発環境用
│   └── test.txt       # テスト環境用
# requirements/base.txt
requests==2.31.0
pandas==2.2.1
flask==3.0.2

# requirements/production.txt
-r base.txt
gunicorn==21.2.0
psycopg2-binary==2.9.9

# requirements/development.txt
-r base.txt
pytest==8.0.2
black==24.2.0
flake8==7.0.0
ipython==8.22.1

# requirements/test.txt
-r base.txt
pytest==8.0.2
pytest-cov==4.1.0
factory-boy==3.3.0
# 本番環境
pip install -r requirements/production.txt

# 開発環境
pip install -r requirements/development.txt

モダンな代替ツール

Poetry

# Poetry をインストール
pip install poetry

# プロジェクトを初期化
poetry init

# パッケージを追加
poetry add requests pandas flask

# 開発用パッケージを追加
poetry add --group dev pytest black flake8

# requirements.txt をエクスポート
poetry export -f requirements.txt --output requirements.txt

# 開発用も含めてエクスポート
poetry export -f requirements.txt --with dev --output requirements-dev.txt

pyproject.toml(Poetry)

[tool.poetry]
name = "my-project"
version = "0.1.0"
description = "My Python project"

[tool.poetry.dependencies]
python = "^3.10"
requests = "^2.31.0"
pandas = "^2.2.1"
flask = "^3.0.2"

[tool.poetry.group.dev.dependencies]
pytest = "^8.0.2"
black = "^24.2.0"
flake8 = "^7.0.0"

uv(高速な新しいツール)

# uv をインストール
pip install uv

# 依存関係をインストール
uv pip install -r requirements.txt

# pip freeze 相当
uv pip freeze > requirements.txt

# pip-compile 相当
uv pip compile requirements.in -o requirements.txt

実践的なスクリプト

requirements.txt 生成スクリプト

#!/usr/bin/env python3
"""requirements.txt を生成するユーティリティスクリプト"""

import subprocess
import sys
from pathlib import Path

def run_pip_freeze(output_file: str = "requirements.txt"):
    """pip freeze の結果を保存"""
    result = subprocess.run(
        [sys.executable, "-m", "pip", "freeze"],
        capture_output=True,
        text=True
    )

    Path(output_file).write_text(result.stdout)
    print(f"Created {output_file} with {len(result.stdout.splitlines())} packages")

def run_pipreqs(project_path: str = ".", output_file: str = "requirements.txt"):
    """pipreqs を使用して依存関係を生成"""
    try:
        subprocess.run(
            ["pipreqs", project_path, "--force", "--savepath", output_file],
            check=True
        )
        print(f"Created {output_file} using pipreqs")
    except subprocess.CalledProcessError as e:
        print(f"Error running pipreqs: {e}")
    except FileNotFoundError:
        print("pipreqs not found. Install with: pip install pipreqs")

def compare_requirements(file1: str, file2: str):
    """2つのrequirements.txtを比較"""
    packages1 = set(Path(file1).read_text().splitlines())
    packages2 = set(Path(file2).read_text().splitlines())

    only_in_1 = packages1 - packages2
    only_in_2 = packages2 - packages1

    if only_in_1:
        print(f"Only in {file1}:")
        for pkg in sorted(only_in_1):
            print(f"  - {pkg}")

    if only_in_2:
        print(f"Only in {file2}:")
        for pkg in sorted(only_in_2):
            print(f"  + {pkg}")

if __name__ == "__main__":
    # pip freeze で生成
    run_pip_freeze("requirements-freeze.txt")

    # pipreqs で生成
    run_pipreqs(".", "requirements-pipreqs.txt")

    # 比較
    compare_requirements("requirements-freeze.txt", "requirements-pipreqs.txt")

バージョン指定のベストプラクティス

# requirements.txt のバージョン指定方法

# 完全一致(再現性重視)
requests==2.31.0

# 互換性のあるバージョン(パッチバージョンは許容)
requests~=2.31.0  # >=2.31.0, <2.32.0

# 最小バージョン
requests>=2.28.0

# バージョン範囲
requests>=2.28.0,<3.0.0

# 除外バージョン
requests>=2.28.0,!=2.29.0

# バージョン指定なし(非推奨)
# requests

まとめ

ツール使用場面
pip freeze環境の完全な再現が必要な場合
pipreqsプロジェクトで使用しているパッケージのみが必要な場合
pip-tools依存関係のロックと管理が必要な場合
poetryモダンなプロジェクト管理が必要な場合

推奨されるワークフロー:

  1. 仮想環境を使用する
  2. 本番用と開発用で依存関係を分離する
  3. バージョンを明示的に指定する
  4. 定期的に依存関係を更新する

参考文献

円