Python 開発環境最適化編 - uv + pre-commit + GitHub Actions

blog
tech
tech-python
tech-vscode

uv と pre-commit で Python 開発環境を最適化。 VS Code 以外のエディタや GitHub Actions でも統一された品質を保てます

Author

uma-chan

Published

2025-07-28

Modified

2025-08-07

本記事は3部構成の2本目です。

  1. Claude Code 対応 Dev Container 環境構築編 - VS Code でもそれ以外でも
  2. Python 開発環境最適化編 - uv + pre-commit + GitHub Actions
  3. Databricks Connect 実践編 - ローカルから Databricks コンピュートを利用

1. はじめに

前回の Dev Container 環境に Python 開発環境を追加します。

uv によるパッケージ管理と pre-commit による品質管理で、チーム開発でも個人開発でも快適な Python 環境を構築できます。

2. 対象読者

  • Python開発環境を効率化したい方
  • チーム開発でコード品質を統一したい方
  • VS Code以外のエディタでも同じ環境を使いたい方

3. uv とは

今回利用している Python 環境管理ツールです。

https://github.com/astral-sh/uv

最低限理解しておくべき要点は以下です。

  • uv 自体は Python エコシステムの外にあるツール
  • インストールしたい Python パッケージを pyproject.toml で管理する
  • pyproject.toml のパッケージの依存関係解決結果を uv.lock に保存し環境の再現性を担保している

4. Python 環境設定

Python コーディング向け環境設定について説明していきます。

基本的なライブラリと pre-commit で Black isort, flake8 を利用していきます。

4.1. pyproject.toml

pyproject.toml
[project]
name = "your-project-name"
version = "0.1.0"
description = "Add your description here"
requires-python = "~=3.13.0"
dependencies = [
  "ipykernel",
  "jupyterlab",
  "matplotlib",
  "numpy",
  "pandas",
  "python-dotenv",
  "requests",
  "seaborn",
]

[dependency-groups]
dev = [
  "pre-commit~=4.2.0",
  "black==25.1.0",
  "isort==6.0.0",
  "flake8==7.3.0",
  "flake8-pyproject",
]

[tool.black]
line-length = 88
target-version = ['py313']

[tool.isort]
profile = "black"
line_length = 88

[tool.flake8]
max-line-length = 88
extend-exclude = [".venv"]
extend-ignore = [
    "E203",  # Whitespace before ':'
    "E701",  # Multiple statements on one line (colon)
]

4.2. pre-commit 設定

Formatter設定をVS Codeから切り離すことでVS Code以外のエディタやGitHub Actionsでも利用できるようになります。

.pre-commit-config.yaml
default_stages: [pre-commit]
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: check-added-large-files
      - id: check-json
      - id: check-yaml
      - id: detect-private-key
      - id: end-of-file-fixer
      - id: mixed-line-ending
        args: [--fix=no]
      - id: trailing-whitespace
        args: [--markdown-linebreak-ext=md]

  - repo: local
    hooks:
      - id: black
        name: black
        entry: uv run --no-sync black
        language: system
        types: [python]

  - repo: local
    hooks:
      - id: isort
        name: isort (python)
        entry: uv run --no-sync isort
        language: system
        types: [python]

  - repo: local
    hooks:
      - id: flake8
        name: flake8
        entry: uv run --no-sync flake8
        language: system
        types: [python]

  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.28.0
    hooks:
      - id: gitleaks

4.3. GitHub Actions での pre-commit

GitHub Actionsでpre-commitを実行するための設定です。

.github/workflows/pre-commit.yaml
name: pre-commit
run-name: ${{ github.event_name }} on ${{ github.ref_name }} by @${{ github.actor }}

on:
  workflow_dispatch:
  push:
    branches:
      - main
  pull_request:
    branches:
      - main
    types:
      - opened
      - synchronize
      - reopened

permissions: {}

defaults:
  run:
    shell: bash

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  pre-commit:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    permissions:
      contents: read

    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          # プルリクエスト時はソースブランチ(github.head_ref)を、
          # 手動実行時は実行対象ブランチ(github.ref_name)をチェックアウト
          ref: ${{ github.event_name == 'pull_request' && github.head_ref || github.ref_name }}
          persist-credentials: false

      - name: Install uv
        uses: astral-sh/setup-uv@v6
        with:
          version: "0.8.3"

      - name: Set up Python
        run: |
          uv sync --only-group dev

      - name: Cache pre-commit
        uses: actions/cache@v4
        with:
          path: ~/.cache/pre-commit
          key: |
            pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}-${{ hashFiles('uv.lock') }}
          # NOTE: 設定ファイルの変更時に必ずキャッシュを更新する
          restore-keys: |
            pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}-${{ hashFiles('uv.lock') }}

      - name: Install pre-commit
        run: |
          uv run --no-sync pre-commit install

      - name: Run pre-commit
        run: |
          uv run --no-sync pre-commit run --all-files

5. Dev Container への統合

5.1. devcontainer.json の更新

前回の devcontainer.json に以下の設定を追加します。

{
    "customizations": {
        "vscode": {
            "extensions": [
                "editorconfig.editorconfig",
                "elagil.pre-commit-helper",
                "ms-python.python",
                "ms-toolsai.jupyter"
            ]
        }
    },
    "remoteEnv": {
        "UV_LINK_MODE": "copy"
    },
    "postStartCommand": "uv sync --frozen --group dev && uv run pre-commit install"
}

5.2. JupyterLab 起動スクリプト

VS Code ユーザーは不要ですが一応 JupyterLab を起動するスクリプトも用意しました。

ポートは自動割り当てにしています。

.devcontainer/start-jupyter.sh
#!/usr/bin/env bash
set -o errexit

# JupyterLab起動スクリプト
# 快適な開発用

echo "🔥 JupyterLab 環境を起動中..."

# ポート設定(環境変数または自動割り当て)
# JUPYTER_PORT=8888 # NOTE: この行をコメントアウトすると自動ポート検索が有効になります
JUPYTER_PORT=${JUPYTER_PORT:-0}

if [ "$JUPYTER_PORT" = "0" ]; then
  echo "📡 利用可能なポートを自動検索中..."
else
  echo "📡 指定ポート ${JUPYTER_PORT} で起動します"
fi

# Python環境の確認
if [ -f "uv.lock" ]; then
  echo "📦 uv環境を使用してJupyterLabを起動します"
  uv sync --frozen
  nohup uv run jupyter lab --ip=0.0.0.0 --port=${JUPYTER_PORT} --no-browser --allow-root \
    --NotebookApp.token='' --NotebookApp.password='' \
    --ServerApp.allow_origin='*' --ServerApp.disable_check_xsrf=True >/dev/null 2>&1 &
elif [ -d ".venv" ]; then
  echo "📦 venv環境を使用してJupyterLabを起動します"
  source .venv/bin/activate
  nohup jupyter lab --ip=0.0.0.0 --port=${JUPYTER_PORT} --no-browser --allow-root \
    --NotebookApp.token='' --NotebookApp.password='' \
    --ServerApp.allow_origin='*' --ServerApp.disable_check_xsrf=True >/dev/null 2>&1 &
else
  echo "❌ Python仮想環境が見つかりません"
  echo "uvまたはvenvで環境をセットアップしてください"
  exit 1
fi

# 実際に割り当てられたポートを取得(リトライ機能付き)
for i in {1..10}; do
  sleep 1
  ACTUAL_PORT=$(ss -tlnp 2>/dev/null | grep jupyter-lab | awk '{print $4}' | cut -d: -f2 | head -1)
  if [ -n "$ACTUAL_PORT" ]; then
    break
  fi
done

if [ "$JUPYTER_PORT" = "0" ] && [ -n "$ACTUAL_PORT" ]; then
  echo "🌐 JupyterLabは http://localhost:${ACTUAL_PORT}/lab でアクセス可能です"
else
  echo "🌐 JupyterLabは http://localhost:${JUPYTER_PORT}/lab でアクセス可能です"
fi

6. Python 仮想環境更新手順

  1. 必要に応じて .python-versionpyproject.toml を更新します。

  2. uv.lock を更新します。

    $ uv lock --upgrade
  3. .venv を更新します。

    $ uv sync --frozen --group dev

6.1. パッケージ追加手順

# 新しいパッケージを追加
uv add pandas matplotlib

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

7. おわりに

これで Python 開発環境が完成しました。

次回はこの環境に Databricks Connect を追加してクラウドデータウェアハウスと連携する方法を紹介します。

Databricks Connect 実践編 - ローカルから Databricks コンピュートを利用