Pythonパッケージの作成: 開発からPyPIへの公開ガイド

コードからPyPIへのデプロイまで、Pythonパッケージングをマスターしましょう。

目次

Python パッケージング は大幅に進化し、現代のツールと標準により、コードの配布がこれまで以上に簡単になりました。

このガイドでは、プロフェッショナルな Python パッケージの作成と PyPI への公開方法について説明します。Python に初めて触れる方や、クイックリファレンスが必要な場合は、Python クイックリファレンス を確認して、Python の基本を素早く理解してください。

Python パッケージ

なぜ Python のコードをパッケージ化するのか?

Python プロジェクトをパッケージ化することには、いくつかの利点があります:

  • 再利用性: 複数のプロジェクト間でコードをコピー&ペーストすることなく共有できます
  • 配布: pip install で簡単に他の人にコードをインストールさせられます
  • 依存関係管理: 依存関係を明確に指定して管理できます
  • バージョニング: リリースをトラックし、後方互換性を維持できます
  • プロフェッショナルな基準: Python コミュニティのベストプラクティスに従います
  • ドキュメント: ドキュメント作成とテストを促進する構造になります

現代的な Python パッケージ構造

よく整理されたパッケージは次の構造を採用します:

my-package/
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   └── test_core.py
├── docs/
│   └── index.md
├── .github/
│   └── workflows/
│       └── publish.yml
├── pyproject.toml
├── README.md
├── LICENSE
└── .gitignore

src-layout はフラット構造よりも現在推奨されています。その理由は以下の通りです:

  • 開発中にインストールされていないコードを誤ってインポートするのを防ぎます
  • インストールを強制してテストをより信頼性高くします
  • ソースコードを他のプロジェクトファイルから明確に分離します

パッケージの内部構造を整理する際には、コードの保守性とテスト性を高めるためにクリーンアーキテクチャの原則を適用することを検討してください。Python のクリーンアーキテクチャ向け設計パターン では、SOLID 原則、依存性注入、およびレイヤードアーキテクチャパターンについて説明しています。

pyproject.toml ファイル: 現代的な設定

pyproject.toml (PEP 518, 621) は、setup.py に代わる Python プロジェクト設定の現代的な標準です:

[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mypackage"
version = "0.1.0"
description = "A fantastic Python package"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
    {name = "Your Name", email = "you@example.com"}
]
keywords = ["example", "tutorial", "package"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.8",
    "Programming Language :: Python :: 3.9",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
]
dependencies = [
    "requests>=2.28.0",
    "click>=8.1.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.4.0",
    "black>=23.0.0",
    "flake8>=6.0.0",
    "mypy>=1.5.0",
]
docs = [
    "sphinx>=7.0.0",
    "sphinx-rtd-theme>=1.3.0",
]

[project.urls]
Homepage = "https://github.com/yourusername/mypackage"
Documentation = "https://mypackage.readthedocs.io"
Repository = "https://github.com/yourusername/mypackage"
Issues = "https://github.com/yourusername/mypackage/issues"

[project.scripts]
mypackage-cli = "mypackage.cli:main"

[tool.setuptools.packages.find]
where = ["src"]

主な設定セクション

  1. build-system: ビルドバックエンド(setuptools、hatchling、poetry-core、flit-core)を指定します
  2. project: コアメタデータと依存関係
  3. project.optional-dependencies: エクストラ機能の依存関係(dev、docs、テスト)
  4. project.scripts: コマンドラインエントリポイント
  5. tool.*: ツール固有の設定(black、pytest、mypy など)

ビルドバックエンドの選択

Setuptools(標準的な選択)

最も広く使用されており、互換性が良いオプションです:

[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

利点: 普遍的な互換性、豊富な機能、大規模なエコシステム
欠点: 新しい代替手段に比べて設定が冗長で、速度が遅い

Hatchling(現代的で高速)

軽量でパフォーマンスが良い現代的なバックエンドです:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

利点: 高速なビルド、シンプルな設定、デフォルトが良い
欠点: setuptools よりもプラグインが少ない

Poetry(オールインワン)

依存関係と環境管理を完全に統合します:

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "mypackage"
version = "0.1.0"
description = "A fantastic Python package"
authors = ["Your Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
requests = "^2.28.0"

利点: 依存関係解決、ロックファイル、統合ワークフロー
欠点: 仕事フローが異なる、ツールの表面積が大きい

Flit(ミニマル)

シンプルなパッケージに最適なツールです:

[build-system]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"

利点: 非常にシンプル、設定が最小限
欠点: 複雑なパッケージには機能が限られている

パッケージのビルド

ビルドツールのインストール

# モダンなビルドツールをインストール
pip install build

# Poetry を使用する場合
pip install poetry

# Hatchling を使用する場合
pip install hatch

ディストリビューションファイルのビルド

build パッケージを使用(任意のバックエンドと互換性があります):

# 以前のビルドをクリーンアップ
rm -rf dist/ build/ *.egg-info

# ソースディストリビューションと wheel をビルド
python -m build

# これにより生成されるファイル:
# dist/mypackage-0.1.0.tar.gz    (ソースディストリビューション)
# dist/mypackage-0.1.0-py3-none-any.whl  (wheel)

Poetry を使用する場合:

poetry build

Hatch を使用する場合:

hatch build

ディストリビューション形式の理解

ソースディストリビューション (sdist) - .tar.gz

  • ソースコードとビルド指示を含みます
  • ユーザーの pip がインストール時にビルドします
  • テスト、ドキュメント、その他の開発ファイルも含まれます

Wheel - .whl

  • 事前にビルドされたバイナリディストリビューション
  • インストールが迅速(ビルドステップ不要)
  • プラットフォーム固有または純粋な Python
  • ディストリビューションに推奨される形式

PyPI への公開

方法1: Twine を使用した手動アップロード(伝統的)

# Twine をインストール
pip install twine

# アップロード前にパッケージを確認
twine check dist/*

# まず TestPyPI にアップロードすることを推奨
twine upload --repository testpypi dist/*

# TestPyPI からインストールをテスト
pip install --index-url https://test.pypi.org/simple/ mypackage

# 本番 PyPI にアップロード
twine upload dist/*

~/.pypirc に資格情報を設定します:

[distutils]
index-servers =
    pypi
    testpypi

[pypi]
username = __token__
password = pypi-AgEIcHlwaS5vcmc...

[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-AgENdGVzdC5weXBpLm9yZw...

方法2: 信頼された公開(CI/CD で推奨)

信頼された公開は、CI/CD プラットフォームからトークンを保存せずに OpenID Connect (OIDC) を使用して認証します。

PyPI での設定:

  1. PyPI プロジェクト設定にアクセス
  2. 「公開」セクションに移動
  3. 新しい「pending publisher」を追加
  4. 次のように設定:
    • 所有者: あなたの GitHub ユーザー名/組織
    • リポジトリ: リポジトリ名
    • ワークフロー: publish.yml
    • 環境: release(オプションですが推奨)

GitHub Actions ワークフロー (.github/workflows/publish.yml):

name: Publish to PyPI

on:
  release:
    types: [published]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: Install build dependencies
        run: |
          python -m pip install --upgrade pip
          pip install build          
      
      - name: Build package
        run: python -m build
      
      - name: Store distribution packages
        uses: actions/upload-artifact@v3
        with:
          name: python-package-distributions
          path: dist/

  publish:
    needs: build
    runs-on: ubuntu-latest
    environment: release
    permissions:
      id-token: write
    
    steps:
      - name: Download distributions
        uses: actions/download-artifact@v3
        with:
          name: python-package-distributions
          path: dist/
      
      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1

利点:

  • 管理またはセキュリティ対策が必要な API トークンがありません
  • OIDC による自動認証
  • 環境保護ルールを通じたセキュリティの向上
  • 公開履歴のトレーサビリティ

ベストプラクティス

1. バージョン管理

セマンティックバージョニング (SemVer) を使用: MAJOR.MINOR.PATCH

# バージョンアップツールをインストール
pip install bump2version

# バージョンを上げる
bump2version patch  # 0.1.0 -> 0.1.1
bump2version minor  # 0.1.1 -> 0.2.0
bump2version major  # 0.2.0 -> 1.0.0

.bumpversion.cfg で設定:

[bumpversion]
current_version = 0.1.0
commit = True
tag = True

[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"

[bumpversion:file:src/mypackage/__init__.py]
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"

2. 必要なファイルの含まれる

README.md: 明確なプロジェクト説明、インストール、使用例
LICENSE: 適切なライセンスを選択(MIT、Apache 2.0、GPL など)
CHANGELOG.md: バージョン間の変更を記録
.gitignore: ビルドアーティファクト、キャッシュ、仮想環境を除外

3. 継続的なテスト

# テスト依存関係をインストール
pip install pytest pytest-cov

# テストを実行してカバレッジを取得
pytest --cov=mypackage tests/

# カバレッジレポートを生成
pytest --cov=mypackage --cov-report=html tests/

pyproject.toml で設定:

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "--strict-markers --cov=mypackage"

4. コード品質ツール

# フォーマット
black src/ tests/

# リント
flake8 src/ tests/

# 型チェック
mypy src/

# インポートの並べ替え
isort src/ tests/

pre-commit ハックに統合(.pre-commit-config.yaml):

repos:
  - repo: https://github.com/psf/black
    rev: 23.9.1
    hooks:
      - id: black
  
  - repo: https://github.com/pycqa/flake8
    rev: 6.1.0
    hooks:
      - id: flake8
  
  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.5.1
    hooks:
      - id: mypy

5. ドキュメンテーション

Sphinx を使用してドキュメンテーションを作成します:

# Sphinx をインストール
pip install sphinx sphinx-rtd-theme

# ドキュメンテーションを初期化
cd docs
sphinx-quickstart

# ドキュメンテーションをビルド
make html

または、Markdown ベースのドキュメンテーションのために MkDocs を使用します:

pip install mkdocs mkdocs-material
mkdocs new .
mkdocs serve

6. 連続インテグレーション

Python パッケージをさまざまなプラットフォームや環境でテストすることは、信頼性にとって重要です。Python のパフォーマンスが異なるデプロイシナリオにおける詳細については、AWS Lambda のパフォーマンス: JavaScript vs Python vs Golang を参照してください。この記事では、Python がサーバーレス環境でどのようにパフォーマンスを発揮するかを探索しています。

完全な CI/CD ワークフロー (.github/workflows/ci.yml):

name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']
    
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
      
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -e ".[dev]"          
      
      - name: Run tests
        run: pytest --cov=mypackage --cov-report=xml
      
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage.xml
  
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      
      - name: Install dependencies
        run: |
          pip install black flake8 mypy          
      
      - name: Black formatting check
        run: black --check src/ tests/
      
      - name: Flake8
        run: flake8 src/ tests/
      
      - name: MyPy
        run: mypy src/

一般的な問題とその解決策

問題1: インストール後のインポートエラー

問題: パッケージはインストールされているが、インポートに失敗します

解決策: __init__.py ファイルと正しい pyproject.toml 設定を使用して、適切なパッケージ構造を保証してください:

[tool.setuptools.packages.find]
where = ["src"]
include = ["mypackage*"]

問題2: 依存関係が不足している

問題: パッケージはインストールされるが、実行時に必要な依存関係が不足しています

解決策: すべてのランタイム依存関係を pyproject.toml に宣言してください:

[project]
dependencies = [
    "requests>=2.28.0",
    "click>=8.1.0",
]

問題3: バージョンの競合

問題: パッケージは開発環境では動作するが、本番環境では失敗します

解決策: バーチャル環境を使用し、最小バージョンを指定してください:

# イソレーションされた環境を作成
python -m venv .venv
source .venv/bin/activate  # Windows: .venv\Scripts\activate

# 開発用に編集可能なモードでインストール
pip install -e ".[dev]"

問題4: パッケージサイズが大きい

問題: パッケージのダウンロード/インストールに時間がかかりすぎる

解決策: 不要なファイルを MANIFEST.in で除外してください:

include README.md
include LICENSE
include pyproject.toml
recursive-include src *.py
recursive-exclude * __pycache__
recursive-exclude * *.py[co]
recursive-exclude tests *
recursive-exclude docs *

問題5: プラットフォーム固有の問題

問題: パッケージは1つのOSでは動作するが、他のOSでは失敗します

解決策: CI/CD マトリクスビルドを使用して、複数のプラットフォームでテストしてください(上記の CI 例を参照)

公開チェックリスト

パッケージを PyPI に公開する前に確認してください:

  • すべての Python バージョンでテストが成功している
  • コードはフォーマットされ、リントされている
  • README.md は例を含めて完成している
  • LICENSE ファイルが含まれている
  • バージョン番号が更新されている
  • CHANGELOG.md が更新されている
  • 依存関係が正しく指定されている
  • パッケージがエラーなくビルドされている (python -m build)
  • TestPyPI でパッケージがテストされている
  • ドキュメンテーションが最新になっている
  • Git リポジトリがバージョンタグでマークされている
  • GitHub リリースが作成されている(信頼された公開用)

高度なトピック

エントリポイントと CLI ツール

コマンドラインインターフェースを作成します:

[project.scripts]
mypackage = "mypackage.cli:main"

src/mypackage/cli.py での実装:

import click

@click.command()
@click.option('--name', default='World', help='Greet this name')
def main(name):
    """Simple CLI tool example"""
    click.echo(f'Hello, {name}!')

if __name__ == '__main__':
    main()

Python パッケージが外部 API と相互作用する現実の例については、Ollama と Python の統合 を参照してください。

プラグインシステム

プラグインの検出を有効にします:

[project.entry-points."mypackage.plugins"]
plugin_a = "mypackage_plugin_a:PluginA"

C 拡張

パフォーマンスが重要なコードに使用します:

[tool.setuptools.ext-modules]
name = "mypackage._speedups"
sources = ["src/mypackage/_speedups.c"]

ネームスペースパッケージ

共通ネームスペースの下で配布されるパッケージに使用します:

[tool.setuptools.packages.find]
where = ["src"]
include = ["company.*"]

[tool.setuptools.package-data]
"company.mypackage" = ["py.typed"]

有用なツールとリソース

パッケージ開発ツール

  • cookiecutter: Python パッケージ用のプロジェクトテンプレート
  • tox: Python バージョンにわたるテスト自動化
  • nox: トックスの代替として柔軟なテスト自動化
  • pre-commit: コード品質用の Git ハックフレームワーク
  • commitizen: コミットメッセージとバージョニングの標準化

ドキュメンテーションプラットフォーム

  • Read the Docs: 無料のドキュメンテーションホスティング
  • GitHub Pages: MkDocs や Sphinx ドキュメンテーションをホスティング
  • pdoc: シンプルな API ドキュメンテーション生成器

パッケージリポジトリ

  • PyPI: 公式 Python パッケージインデックス (pypi.org)
  • TestPyPI: テスト環境 (test.pypi.org)
  • Anaconda.org: Conda パッケージ配布
  • GitHub Packages: プライベートパッケージホスティング

モニタリングと分析

  • PyPI Stats: パッケージのダウンロード統計
  • Libraries.io: 依存関係のモニタリングとアラート
  • Snyk: セキュリティ脆弱性スキャン
  • Dependabot: 依存関係の自動更新

結論

現代的な Python パッケージングは、pyproject.toml や強力なビルドバックエンド、セキュアな信頼された公開など、開発者フレンドリーな標準に進化しています。このガイドに従うことで、ユーザーに効果的にサービスするプロフェッショナルで保守可能な Python パッケージを作成できます。

主要なポイント:

  1. 新しいプロジェクトでは pyproject.toml を使用してください
  2. ご自身のニーズに合った正しい ビルドバックエンド を選択してください
  3. 自動テスト と CI/CD を実装してください
  4. セキュアでトークンフリーのデプロイに 信頼された公開 を使用してください
  5. セマンティックバージョニング を実行し、変更履歴を維持してください
  6. 総合的な ドキュメンテーション と例を提供してください

Python パッケージングエコシステムは、ツール、標準、セキュリティ機能が改善され続けています。Python 拡張提案(PEPs)やコミュニティのベストプラクティスを常に更新して、パッケージを現代的で保守可能な状態に保つようにしてください。

有用なリンク

このサイトの他の有用な記事