第4章:プロジェクト設計
更新日:2025年12月9日
1. ディレクトリ構成
Pythonプロジェクトのディレクトリ構成には主に2つのパターンがある。
1.1 srcレイアウト:パッケージをsrc/ディレクトリに配置するパターン。PyPA(Python Packaging Authority)が推奨[1]。
myproject/
├── src/
│ └── mypackage/
│ ├── __init__.py
│ ├── core.py
│ ├── utils.py
│ └── cli.py
├── tests/
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_core.py
│ └── test_utils.py
├── docs/
│ └── index.md
├── pyproject.toml
├── README.md
├── LICENSE
└── .gitignore
1.2 flatレイアウト:パッケージをルート直下に配置するパターン。小規模プロジェクトで使用される。
myproject/
├── mypackage/
│ ├── __init__.py
│ ├── core.py
│ └── utils.py
├── tests/
│ └── test_core.py
├── pyproject.toml
└── README.md
Table 1. srcレイアウト vs flatレイアウト
| 観点 | srcレイアウト | flatレイアウト |
|---|---|---|
| インストール要否 | 必要(editable install) | 不要(直接import可能) |
| テスト信頼性 | 高(インストール後のコードをテスト) | 低(ローカルコードをテスト) |
| 名前空間 | クリーン | ルートと混在リスク |
| 推奨用途 | ライブラリ、中〜大規模 | スクリプト、小規模 |
1.3 アプリケーション向け構成:Webアプリケーション等では機能別のモジュール分割が有効。
myapp/
├── src/
│ └── myapp/
│ ├── __init__.py
│ ├── main.py # エントリーポイント
│ ├── config.py # 設定管理
│ ├── api/ # APIエンドポイント
│ │ ├── __init__.py
│ │ ├── routes.py
│ │ └── dependencies.py
│ ├── models/ # データモデル
│ │ ├── __init__.py
│ │ └── user.py
│ ├── services/ # ビジネスロジック
│ │ ├── __init__.py
│ │ └── user_service.py
│ └── repositories/ # データアクセス
│ ├── __init__.py
│ └── user_repository.py
├── tests/
├── alembic/ # DBマイグレーション
├── pyproject.toml
├── Dockerfile
└── docker-compose.yml
Fig. 1に構成選択のフローを示す。
2. pyproject.toml詳解
pyproject.tomlはPEP 518/621で標準化されたプロジェクト設定ファイルである[2]。ビルドシステム、依存関係、ツール設定を一元管理できる。
2.1 基本構造:
# pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "mypackage"
version = "0.1.0"
description = "A sample Python package"
readme = "README.md"
license = {text = "MIT"}
requires-python = ">=3.10"
authors = [
{name = "Your Name", email = "you@example.com"}
]
classifiers = [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
]
keywords = ["sample", "python"]
dependencies = [
"requests>=2.28.0",
"pydantic>=2.0.0",
]
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"pytest-cov>=4.0.0",
"ruff>=0.1.0",
"mypy>=1.0.0",
]
docs = [
"mkdocs>=1.5.0",
"mkdocs-material>=9.0.0",
]
[project.scripts]
mypackage = "mypackage.cli:main"
[project.urls]
Homepage = "https://github.com/yourname/mypackage"
Documentation = "https://yourname.github.io/mypackage"
Repository = "https://github.com/yourname/mypackage"
2.2 ビルドバックエンド:Table 2に主要なビルドバックエンドを示す。
Table 2. ビルドバックエンドの比較
| バックエンド | 特徴 | 用途 |
|---|---|---|
| hatchling | 高速、シンプル、モダン | 一般的なパッケージ |
| setuptools | 伝統的、高機能 | C拡張、レガシー |
| poetry-core | Poetry統合 | Poetryプロジェクト |
| flit-core | 最小限、高速 | 純粋Pythonパッケージ |
| maturin | Rust拡張対応 | PyO3プロジェクト |
2.3 ツール設定セクション:各ツールの設定を統合できる。
# ツール設定
[tool.ruff]
target-version = "py312"
line-length = 88
[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "UP"]
[tool.mypy]
python_version = "3.12"
strict = true
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-v --cov=src/mypackage --cov-report=term-missing"
[tool.coverage.run]
source = ["src/mypackage"]
branch = true
[tool.coverage.report]
exclude_lines = [
"pragma: no cover",
"if TYPE_CHECKING:",
"raise NotImplementedError",
]
3. テスト戦略
3.1 pytest
pytestはPythonの事実上の標準テストフレームワークである[3]。シンプルな記法と強力なプラグインエコシステムを持つ。
# tests/test_core.py
import pytest
from mypackage.core import Calculator
class TestCalculator:
"""Calculatorクラスのテスト"""
def test_add(self):
calc = Calculator()
assert calc.add(2, 3) == 5
def test_divide(self):
calc = Calculator()
assert calc.divide(10, 2) == 5.0
def test_divide_by_zero(self):
calc = Calculator()
with pytest.raises(ZeroDivisionError):
calc.divide(10, 0)
@pytest.mark.parametrize("a,b,expected", [
(1, 1, 2),
(0, 0, 0),
(-1, 1, 0),
(100, 200, 300),
])
def test_add_parametrized(self, a, b, expected):
calc = Calculator()
assert calc.add(a, b) == expected
3.1.1 フィクスチャ:テストのセットアップ・ティアダウンを管理。
# tests/conftest.py
import pytest
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
@pytest.fixture
def db_session():
"""テスト用データベースセッション"""
engine = create_engine("sqlite:///:memory:")
Session = sessionmaker(bind=engine)
session = Session()
yield session
session.close()
@pytest.fixture
def sample_user():
"""サンプルユーザーデータ"""
return {
"name": "Test User",
"email": "test@example.com",
"age": 25
}
# スコープ指定(session: テスト全体で1回)
@pytest.fixture(scope="session")
def api_client():
"""APIクライアント(セッション全体で共有)"""
from mypackage.client import APIClient
client = APIClient()
yield client
client.close()
3.1.2 モックとパッチ:外部依存をテストから分離。
# tests/test_service.py
from unittest.mock import Mock, patch
from mypackage.services import UserService
def test_get_user_from_api():
# Mockオブジェクトの使用
mock_client = Mock()
mock_client.get.return_value = {"id": 1, "name": "Alice"}
service = UserService(client=mock_client)
user = service.get_user(1)
assert user["name"] == "Alice"
mock_client.get.assert_called_once_with("/users/1")
@patch("mypackage.services.requests.get")
def test_fetch_external_data(mock_get):
# patchデコレータによるモック
mock_get.return_value.json.return_value = {"data": "test"}
mock_get.return_value.status_code = 200
from mypackage.services import fetch_data
result = fetch_data("https://api.example.com")
assert result == {"data": "test"}
3.2 カバレッジ
pytest-covによりテストカバレッジを測定できる。
# テスト実行とカバレッジ測定
pytest --cov=src/mypackage --cov-report=html
# カバレッジレポートの確認
open htmlcov/index.html
3.2.1 カバレッジ目標:Table 3にカバレッジ目標の目安を示す。
Table 3. カバレッジ目標の目安
| カバレッジ | 評価 | 備考 |
|---|---|---|
| 90%以上 | 優秀 | 重要なライブラリ向け |
| 80-90% | 良好 | 一般的なプロジェクト目標 |
| 70-80% | 許容 | 最低限の品質保証 |
| 70%未満 | 要改善 | テスト追加を検討 |
3.2.2 カバレッジ100%を目指すことは必ずしも効率的ではない。重要なビジネスロジックとエッジケースに集中することが推奨される。
4. CI/CDパイプライン
GitHub Actionsによる継続的インテグレーション/デリバリーの設定例を示す[4]。
4.1 基本的なCIワークフロー:
# .github/workflows/ci.yml
name: CI
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Set up Python ${{ matrix.python-version }}
run: uv python install ${{ matrix.python-version }}
- name: Install dependencies
run: uv sync --all-extras
- name: Run linter
run: uv run ruff check .
- name: Run formatter check
run: uv run ruff format --check .
- name: Run type checker
run: uv run mypy src/
- name: Run tests
run: uv run pytest --cov=src/ --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
file: coverage.xml
4.2 リリースワークフロー:タグ作成時にPyPIへ自動公開。
# .github/workflows/release.yml
name: Release
on:
push:
tags:
- "v*"
jobs:
publish:
runs-on: ubuntu-latest
permissions:
id-token: write # Trusted publishing
steps:
- uses: actions/checkout@v4
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Build package
run: uv build
- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
# Trusted publishingにより認証情報不要
4.3 ブランチ戦略との連携:Fig. 2にCI/CDフローを示す。
5. ドキュメント生成
MkDocsはMarkdownベースのドキュメントサイトジェネレータである[5]。mkdocs-materialテーマにより高品質なドキュメントサイトを構築できる。
5.1 セットアップ:
# インストール
uv add mkdocs mkdocs-material mkdocstrings[python] --dev
# プロジェクト初期化
mkdocs new .
# ローカルサーバー起動
mkdocs serve
# ビルド
mkdocs build
5.2 mkdocs.yml設定:
# mkdocs.yml
site_name: MyPackage Documentation
site_url: https://yourname.github.io/mypackage/
repo_url: https://github.com/yourname/mypackage
repo_name: yourname/mypackage
theme:
name: material
palette:
- scheme: default
primary: indigo
accent: indigo
toggle:
icon: material/brightness-7
name: ダークモードに切替
- scheme: slate
primary: indigo
accent: indigo
toggle:
icon: material/brightness-4
name: ライトモードに切替
features:
- navigation.instant
- navigation.tabs
- content.code.copy
plugins:
- search
- mkdocstrings:
handlers:
python:
options:
docstring_style: google
nav:
- Home: index.md
- Getting Started:
- Installation: getting-started/installation.md
- Quick Start: getting-started/quickstart.md
- User Guide:
- Configuration: guide/configuration.md
- Usage: guide/usage.md
- API Reference: api/reference.md
- Contributing: contributing.md
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
- pymdownx.superfences
- admonition
- toc:
permalink: true
5.3 docstringからAPIドキュメント生成:mkdocstringsによりソースコードのdocstringを自動抽出。
# src/mypackage/core.py
class Calculator:
"""基本的な計算機能を提供するクラス。
Attributes:
precision: 計算結果の精度(小数点以下桁数)
Example:
>>> calc = Calculator(precision=2)
>>> calc.add(1.111, 2.222)
3.33
"""
def __init__(self, precision: int = 10) -> None:
"""Calculatorを初期化する。
Args:
precision: 計算結果の精度。デフォルトは10。
"""
self.precision = precision
def add(self, a: float, b: float) -> float:
"""2つの数値を加算する。
Args:
a: 第1の数値
b: 第2の数値
Returns:
aとbの和(precision桁で丸め)
Raises:
TypeError: 数値以外が渡された場合
"""
return round(a + b, self.precision)
# docs/api/reference.md
# API Reference
::: mypackage.core.Calculator
options:
show_source: true
5.4 GitHub Pagesへのデプロイ:
# .github/workflows/docs.yml
name: Deploy Docs
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v4
- run: uv sync --group docs
- run: uv run mkdocs gh-deploy --force
References
[1] PyPA, "Python Packaging User Guide - src layout," packaging.python.org, 2024.
[2] P. Ganssle et al., "PEP 621 -- Storing project metadata in pyproject.toml," python.org, 2020.
[3] pytest, "pytest: helps you write better programs," docs.pytest.org, 2024.
[4] GitHub, "GitHub Actions Documentation," docs.github.com, 2024.
[5] MkDocs, "MkDocs - Project documentation with Markdown," mkdocs.org, 2024.
本コンテンツは2025年12月時点の情報に基づいて作成されている。GitHub ActionsやPyPIのAPIは変更される可能性があるため、最新の公式ドキュメントを参照されたい。