python-dev について
python-devは、Pythonプロジェクトの新規作成や近代化に使うセットアップスキルです。依存関係管理、型チェック、Lint、テスト、タスク実行のツール構成を定め、設定テンプレートと移行手順も提供します。
主な機能
- 01uvによるプロジェクトと依存関係の設定
- 02tyの型チェック設定
- 03ruffによるLintとフォーマット
- 04pytestとjustのワークフロー
- 05lefthookのGitフック設定
活用シーン
- 01新しいPythonプロジェクトの設定
- 02既存プロジェクトのスタック移行
- 03開発ワークフローへのチェックとテストの追加
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ tenequm/ skills --skill python-devインストール後、Claude Code で/python-devを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
CHANGELOG.mdLICENSE.txtreferences/justfile-reference.mdpytest-reference.mdruff-reference.mdty-reference.mduv-reference.md詳細
Python 開発環境のセットアップ
意見を取り入れた、本番環境にも対応できる Python 開発スタックです。選択に迷う必要はありません。これを使うだけです。
使用する場面
- 新しい Python プロジェクトを始めるとき
- 既存のプロジェクトを最新化するとき(pip/poetry/mypy/black/flake8 から移行)
- リント、フォーマット、型チェック、テストを設定するとき
- プロジェクトコマンド用の Justfile を作成するとき
- pyproject.toml を唯一の信頼できる情報源として設定するとき
スタック
| ツール | 役割 | 置き換えるもの |
|---|---|---|
| uv 0.12+ | パッケージマネージャー、Python バージョン、ランナー | pip、poetry、pyenv、virtualenv |
| ty(ベータ版) | 型チェッカー(Astral、Rust) | mypy、pyright |
| ruff | リンター+フォーマッター | flake8、black、isort、pyupgrade |
| pytest | テスト | unittest |
| just | コマンドランナー | make |
| lefthook 2.1+ | Git フック(単一バイナリ、並列実行) | pre-commit |
ty に関する注意:ty はベータ版(0.0.x)であり、安定した API はありません。また、推論の挙動がどの 2 つのバージョン間でも変わる可能性があるため、バージョンを固定してください。Pydantic はもはや妥当な不満の理由ではありません。ty は 0.0.57 から、Pydantic 専用のライブラリサポートトラック(コンストラクター、
model_config、BaseSettings、RootModel、strict と lax)を提供しています。Django と SQLAlchemy にはまだそのようなサポートがなく、誤検出の原因となる可能性が高いままです。チェッカー全体を置き換える前に、[tool.ty.analysis] replace-imports-with-any = ["sqlalchemy.**"]を使ってください。すべてではなく、問題のある依存関係 1 つだけを無効化できます。今すぐ堅牢な型チェックが必要なら、tyをpyrightに置き換え、スタックのほかの部分はそのままにしてください。
クイックスタート:新規プロジェクト
# 1. Create project with src layout (uv 0.12+ packages by default; --package is redundant)
uv init my-project
cd my-project
# 2. Pin Python version
uv python pin 3.13
# 3. Add dev dependencies
uv add --dev ruff ty pytest pytest-asyncio lefthook
# 4. Create Justfile and lefthook.yml (see templates below)
# 5. Configure pyproject.toml (see template below)
# 6. Install git hooks
uv run lefthook install
# 7. Run checks
just check
pyproject.toml テンプレート
このファイルが唯一の設定ファイルです。これをコピーして [project] の各フィールドを調整してください。
[project]
name = "my-project"
version = "0.1.0"
description = "Project description"
readme = "README.md"
requires-python = ">=3.13"
license = {text = "MIT"}
dependencies = []
[project.scripts]
my-project = "my_project:main" # CLI: `uv run my-project` -> main() in src/my_project/__init__.py
[dependency-groups]
dev = [
"ruff>=0.16.6", # 0.16 changed the default rule set - see the lint note below
"ty>=0.0.79", # beta: pin tight, inference changes between minors
"pytest>=9.1.1", # 9.1 fixed addopts strictness being ignored
"pytest-asyncio>=1.4.0", # 1.4 added the loop-factories hook
"lefthook>=2.1.12",
]
# uv 0.12+ generates `uv_build` here instead. Keep that unless you need a hatchling
# plugin - this block is a deliberate override, not what `uv init` gives you.
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/my_project"]
# =============================================================================
# RUFF - Loose, helpful rules only
# =============================================================================
[tool.ruff]
target-version = "py313"
line-length = 100
[tool.ruff.lint]
# Ruff 0.16 enables 413 rules by default (up from 59). Do NOT write a `select`
# list here unless you mean to shrink that - `select = ["E","F","I","UP"]` now
# makes ruff weaker than no config at all. Narrow with `extend-select`/`ignore`.
ignore = [
"E501", # line too long - formatter handles it
"UP007", # X | Y unions - Optional[X] is more readable
]
exclude = [".git", ".venv", "__pycache__", "build", "dist"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
line-ending = "lf"
# Ruff 0.16 formats Python blocks inside Markdown by default. Drop this line
# only if you want README code fences reformatted too.
exclude = ["*.md"]
# =============================================================================
# TY - Type Checker
# =============================================================================
[tool.ty.environment]
python-version = "3.13"
[tool.ty.src]
include = ["src"]
# =============================================================================
# PYTEST
# =============================================================================
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
asyncio_mode = "auto"
# pytest 9.0 silently ignored --strict-markers/--strict-config passed via
# addopts. Use the ini keys, which work on 9.0 and 9.1 alike. `strict = true`
# is the shorthand and also covers strict_xfail + strict_parametrization_ids.
strict = true
addopts = ["-ra"]
asyncio_default_fixture_loop_scope は意図的に上記で未設定にしています。未設定の場合、pytest-asyncio は実行のたびに警告を出します。警告を抑え、動作を固定するには "function" に設定してください。
Justfile テンプレート
# Check types, lint, and formatting (non-mutating; mirrors CI)
check:
uv run ty check
uv run ruff check
uv run ruff format --check
# Run tests
test *ARGS:
uv run pytest {{ARGS}}
# Run tests with coverage
test-cov:
uv run pytest --cov=src --cov-report=term-missing
# Auto-fix and format
fix:
uv run ruff check --fix
uv run ruff format
# Install/sync all dependencies
install:
uv sync --all-groups
uv run lefthook install
# Update all dependencies
update:
uv lock --upgrade
uv sync --all-groups
# Clean build artifacts
clean:
rm -rf dist/ build/ .pytest_cache/ .ruff_cache/ htmlcov/
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
Lefthook の設定
ここで Lefthook が pre-commit に取って代わる理由は、go-dev で Lefthook を使う理由と同じです。単一のバイナリでフックを並列実行できること、そして Python では特に重要な理由として、フックがフック設定で別途固定した 2 つ目のコピーではなく、uv.lock のあなたの ruff を実行することです。プラットフォーム用ホイールとして PyPI からインストールされるため、uv add --dev lefthook だけでインストールは完了し、Go ツールチェーンは必要ありません。
uv run lefthook install # writes .git/hooks/pre-commit and pre-push
# lefthook.yml
assert_lefthook_installed: true
pre-commit:
piped: true # a failed job stops the rest; lint must fix before format runs
jobs:
- name: guards
group:
parallel: true
jobs:
- name: private-key
run: "! grep -lE 'BEGIN [A-Z ]*PRIVATE KEY' {staged_files}"
- name: merge-conflict
run: "! grep -lE '^(<<<<<<<|>>>>>>>) ' {staged_files}"
- name: large-files
exclude: ["uv.lock"]
run: "! find {staged_files} -type f -size +1000k | grep ."
- name: ruff-check
glob: "*.{py,pyi,ipynb}"
run: uv run ruff check --force-exclude --fix {staged_files}
stage_fixed: true
- name: ruff-format
glob: "*.{py,pyi,ipynb}"
run: uv run ruff format --force-exclude {staged_files}
stage_fixed: true
- name: ty
run: uv run ty check # no glob - see the glob trap below
pre-push:
jobs:
- name: test
run: uv run pytest
--force-exclude は必須であり、飾りではありません。 Ruff は、コマンドラインで明示的に渡されたパスに対して、自身の exclude 設定を無視します。また、{staged_files} はパスを明示的に渡します。このフラグがないと、[tool.ruff] exclude の下にあるステージ済みファイルもリントされてしまいます。確認済みの例:フラグを付けて ruff check --force-exclude src/generated/gen.py を実行すると All checks passed と表示されますが、付けずに同じコマンドを実行するとエラーが見つかります。pre-commit のユーザーがこの問題に遭遇しなかったのは、ruff-pre-commit がフックエントリーにこのフラグを組み込んでいるためです。lefthook では自分で忘れずに指定する必要があります。
stage_fixed: true は pre-commit より使いやすい点です。 pre-commit はフックがファイルを書き換えるとコミットを失敗させ、再ステージして再実行するよう求めます。lefthook は修正済みファイルに対して git add を再実行するため、コミットを続行できます。2.1.12 以降では、git add が失敗するとフックも失敗するため、修正が未ステージのまま見過ごされることはありません。
piped: true を設定する理由は実行順序です。 Ruff の推奨は、フォーマットの前に修正付きのリントを実行することです。--fix が出力するコードは、その後フォーマットが必要になるためです。また、piped によってガードが先に実行され、漏洩したキーがあればツールが時間を費やす前にコミットを停止できます。
この設定を編集する前に知っておくべき注意事項:
globは一致するものがないとジョブ全体を何も通知せずにスキップします。これは利便性ではなく、ゲートの抜け穴です。runに{staged_files}が含まれていないglob付きのジョブも、その glob でフィルタリングされます。そのため、glob: "*.{py,pyi}"を指定したtyは、pyproject.tomlだけを変更したコミットではスキップされます。まさに[tool.ty]と依存関係のピン留めが記載されているファイルです。検証済みです。このようなコミットではty (skip) no matching staged filesと表示され、型チェックが一切行われずに記録されます。ステージ済みファイルではなくプロジェクトをチェックするジョブには、glob を指定してはいけません。上記の ruff ジョブは{staged_files}だけを対象にするため、glob を指定したままにします。**はディレクトリが 0 個以上ではなく、1 個以上ある場合に一致します。glob: "src/**/*.py"はsrc/main.pyには一致しません。他のすべてのツールと同じ動作にするには、glob_matcher: doublestarを使用してください。- 設定ファイルの場所は重要です。 lefthook が自動検出するのは、リポジトリのルートまたは
.config/(後者は v1.11.12 以降)だけです。それ以外の場所に置くと、git がフックを直接呼び出し、タスクランナーのレシピでは割り込めないため、コミット時にフックが何も通知せずに実行されなくなります。 - 変更を加えるジョブを
pre-pushに置かないでください。 そこでファイルを書き換えるジョブはプッシュを失敗させ、未コミットの編集を残します。--fixはstage_fixedが処理するpre-commitに置き、pre-pushは上記のpytestジョブのように読み取り専用にしてください。 - glob の条件が大まかすぎる場合は、
file_typesを利用できます。text、binary、executable、symlinkや、text/x-pythonを含む MIME タイプを指定できます。 - 削除したファイルは
{staged_files}から除外されます。ジョブはno files for inspectionとしてスキップされるため、削除のみのコミットでエラーにはなりません。 - フックの実行中は未ステージの変更が隠され、終了後に復元されます。そのため、ゲートは作業ツリーの変更状態ではなく、実際にコミットする内容を判定します。2.1.12 で検証済みです。クリーンなファイルをステージし、壊れたコピーを未ステージのまま残しても、クリーンなバージョンがコミットされ、未ステージの編集は元に戻ります。
- lefthook はインストールするまで機能しません。
assert_lefthook_installed: trueを設定すると、バイナリがない場合にフックが何も通知せずに実行されないのではなく、失敗します。オンボーディングの手順にlefthook installを含めてください。 lefthook validateは CI で不正な設定を検出し、lefthook dumpは統合後の有効な設定を出力します。git で無視されるlefthook-local.ymlを使えば、チームメイトに影響を与えずにジョブを追加またはスキップできます。
pre-commit を使わなくすることで失われるもの。 pre-commit-hooks ライブラリには lefthook に相当するものがありません。そのフックのうち、残す価値のあった 3 つは、上記の guards グループで独自に実装しています。detect-private-key、check-merge-conflict、check-added-large-files です。4 つのフック、check-yaml、check-toml、end-of-file-fixer、trailing-whitespace、mixed-line-ending は再現していません。ruff のフォーマッターが Python ファイルの空白と末尾の改行をすでに処理し、残りのフックは Python 以外のファイルだけを対象としていたためです。リポジトリに手作業で編集する YAML ファイルが多い場合は、シェルジョブとして再度追加してください。また、pre-commit autoupdate とホスト型の pre-commit.ci も使えなくなります。その代わり、ツールのバージョンを更新する場所は uv lock --upgrade に一本化されます。
プロジェクト構成
常に src レイアウトを使用してください。
my-project/
src/
my_project/
__init__.py
cli.py
models.py
tests/
conftest.py
test_models.py
pyproject.toml
Justfile
uv.lock
.python-version
lefthook.yml
.gitignore
日々のワークフロー
just check # Type check + lint + format
just test # Run tests
just test -x # Stop on first failure
just fix # Auto-fix lint issues
uv add httpx # Add a dependency
uv add --dev hypothesis # Add dev dependency
uv sync # main deps + dev (dev is in default-groups)
uv sync --all-groups # everything in [dependency-groups]
uv run python -m my_project # Run the project
CI(GitHub Actions)
CI で just check と just test を実行します。以下を .github/workflows/ci.yml に追加してください。
name: CI
on: [push, pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
- uses: astral-sh/[email protected] # pin the full version - see below
- run: uv sync --all-groups
- run: uv run ty check
- run: uv run ruff check --output-format github
- run: uv run ruff format --check
- run: uv run pytest
- run: uv run lefthook validate
astral-sh/setup-uv は uv をインストールし、.python-version で指定された Python を管理し、依存関係解決のキャッシュを行います。別途 setup-python のステップを追加する必要はありません。
このバージョン指定について、次の 2 点に注意してください。
@v10ではなく、完全なバージョンを指定してください。 v8 以降、setup-uv は 2 種類の浮動タグの公開を停止しました。「セキュリティをさらに強化するため、マイナータグの公開を停止します。今後は@v8や@v8.0を使用できなくなります。」 確認したところ、v6とv7の参照は解決されますが、v8/v9/v10は 404 になります。存在するのはパッチバージョンを正確に指定したものだけなので、パッチバージョンかコミット SHA を指定してください。enable-cache: trueは設定しないでください。 デフォルトはautoであり、v10 以降はキャッシュポイズニングを防ぐため、pull_request_target、workflow_run、releaseでは意図的にキャッシュを無効にします。これを強制的に有効にすると、その保護が無効になります。
既存プロジェクトの移行
# 1. Install uv if not present
brew install uv
# 2. Convert requirements.txt to pyproject.toml deps
uv add -r requirements.txt
# 3. Replace mypy with ty
uv remove --dev mypy
uv add --dev ty
# 4. Replace black/flake8/isort with ruff
uv remove --dev black flake8 isort
uv add --dev ruff
# 5. Replace pre-commit with lefthook
uv run pre-commit uninstall # while it can still read its own config
uv remove --dev pre-commit
rm .pre-commit-config.yaml
uv add --dev lefthook
uv run lefthook install
# 6. Apply pyproject.toml config sections from template above
# 7. Create Justfile and lefthook.yml from templates above
# 8. Run: just check
lefthook install は既存のフックを上書きしません。.git/hooks/pre-commit.old に名前を変更し、そのことを通知します。先に pre-commit uninstall を実行しておけば、残ったファイルを削除する手間を省けます。
リファレンスドキュメント
各ツールの詳細なガイドは references/ にあります。
- uv-reference.md - プロジェクトの初期化、依存関係、ロック/同期、Python バージョン、ビルド/公開
- ty-reference.md - 設定、ルール、CLI フラグ、既知の制限事項
- ruff-reference.md - ルールセット、フォーマッターのオプション、ファイルごとの無視設定、CI との統合
- pytest-reference.md - プラグイン、フィクスチャ、非同期テスト、conftest のパターン
- justfile-reference.md - 構文、変数、パラメーター、シェバンレシピ、設定