コンテンツにスキップ

推奨設定例

Pythonプロジェクト向けの推奨構成例(pyproject.toml・pre-commit・タスクランナー・CI設定)。 設定して実行するところから始める場合ははじめにを参照。

pyproject.toml

pyfltr本体の設定([tool.pyfltr])と、呼び出される各ツール(ruff / mypy / pytest)の設定を1つのpyproject.tomlにまとめた例。

  • preset = "latest": 各時点での推奨ツール構成。詳細はプリセット設定を参照
  • python = true: Python系ツールのゲートを開ける。推奨ツール(ruff-format / ruff-check / mypy / pylint / pyright / pytest / uv-sort)を一式有効化する
    • Python系ツール一式は本体依存に同梱されているため、uvx pyfltr単発で利用できる
    • dev依存に固定する場合はuv add --dev "pyfltr[python]"(pip環境ではpip install pyfltr)を使う
  • pylint-args: pylintに追加で渡す引数。--load-plugins=pylint_pydantic--enable-error-code=unused-awaitable(mypy)は自動オプションで既定有効のため個別指定不要
  • [tool.pylint."messages control"]: pylintのdisableリストをpyproject.tomlに集約することで、 .pylintrcを別途配置する必要がなくなり設定の所在がpyproject.toml1ファイルにまとまる
    • ruffのDカテゴリがmissing-*-docstring相当を検出するため、 pylint側の同系ルールは無効化しても品質低下は招かない
    • missing-class-docstringはテストクラスに対する儀礼的docstring付与を無効化する
    • missing-module-docstring__init__.py等でモジュールdocstring要求を緩和する目的で無効化する
  • ruffの per-file-ignores: テストコード(**_test.py)とpackage init(__init__.py)のdocstring要求を除外する実用的な調整

uvx pyfltrでの実行ではpyproject.tomlにpyfltrを記述する必要はなく、[tool.pyfltr]セクションのみで完結する。 dev依存に固定する場合のみ[dependency-groups] dev"pyfltr[python]"を追加する(後置の併記例)。

[tool.pyfltr]
preset = "latest"
python = true
pylint-args = ["--jobs=4"]

[tool.pylint."messages control"]
disable = [
    "broad-exception-caught",
    "duplicate-code",
    "fixme",
    "invalid-name",
    "line-too-long",
    "logging-fstring-interpolation",
    "logging-not-lazy",
    "missing-class-docstring",
    "missing-function-docstring",
    "missing-module-docstring",
    "no-else-return",
    "too-few-public-methods",
    "too-many-arguments",
    "too-many-boolean-expressions",
    "too-many-branches",
    "too-many-instance-attributes",
    "too-many-locals",
    "too-many-nested-blocks",
    "too-many-positional-arguments",
    "too-many-public-methods",
    "too-many-return-statements",
    "too-many-statements",
]

[tool.ruff]
# https://docs.astral.sh/ruff/configuration/
line-length = 128

[tool.ruff.lint]
# https://docs.astral.sh/ruff/linter/#rule-selection
select = [
    # pydocstyle
    "D",
    # pycodestyle
    "E",
    # Pyflakes
    "F",
    # pyupgrade
    "UP",
    # flake8-bugbear
    "B",
    # flake8-simplify
    "SIM",
    # flake8-import-conventions
    "ICN",
    # isort
    "I",
]
ignore = [
    "D107", # Missing docstring in `__init__`
    "D415", # First line should end with a period
    "D403", # First word of the first line should be properly capitalized(日本語docstringにそぐわないため)
]

[tool.ruff.lint.pydocstyle]
convention = "google"

[tool.ruff.lint.per-file-ignores]
"**_test.py" = ["D"]
"**/__init__.py" = ["D104"]  # Missing docstring in public package

[tool.mypy]
# https://mypy.readthedocs.io/en/stable/config_file.html
allow_redefinition = true
check_untyped_defs = true
ignore_missing_imports = true
strict_optional = true
strict_equality = true
warn_no_return = true
warn_redundant_casts = true
warn_unused_configs = true
show_error_codes = true

[tool.pytest.ini_options]
# https://docs.pytest.org/en/latest/reference/reference.html#ini-options-ref
addopts = "--showlocals -p no:cacheprovider --maxfail=5 --durations=30 --durations-min=0.5 --timeout=60 -n 4"
log_level = "DEBUG"
xfail_strict = true
asyncio_mode = "strict"
asyncio_default_fixture_loop_scope = "session"
asyncio_default_test_loop_scope = "session"

--timeout=60pytest-timeoutプラグインが必要。 -n 4pytest-xdistプラグインが必要で、4プロセス並列でテストを実行する。 ランナー別の有効条件は次の通り。

  • python-runner = "direct"経路では、pyfltrのvenv配下のpytestを直接起動するため 本体依存のpytest-timeoutpytest-xdistがそのまま利用できる
  • uvx pyfltruv.lock不在のディレクトリで実行する標準シナリオでは、uv経路の前提が満たされず shutil.whichによるdirectフォールバックが発生する
    • この場合もpyfltrの本体venv同梱のpytestが採用されるため本体依存のプラグインが有効
  • uv経路(既定)でcwdのuv.lockにpytestが登録されている場合は、 利用者プロジェクトのvenvでpytestが解決される
    • プロジェクト側にpytest-timeoutpytest-xdistを導入する (uv add --dev pytest-timeout pytest-xdist等)
  • uvx経路(per-tool直接指定でpytest用の独立環境が生成される場合)は、 当該環境側へのpytest-timeoutpytest-xdistの導入が別途必要
  • pytest-xdistの並列実行下では、ポート番号・一時ファイル名・グローバル状態の競合に注意する
    • 間欠失敗するテストは並列前提に修正するか-p no:xdist等でxdist対象外へ退避させる

typosの許可語設定

プロジェクト固有の許可語がある場合はpyproject.toml[tool.typos]セクションに追記する。 typos-cliはpyproject.toml[tool.typos]を公式にサポートしているため、_typos.tomlを別ファイルとして管理する必要はない。

[tool.typos.default.extend-words]
teh = "teh"
hte = "hte"

識別子(変数名・関数名)単位で許可したい場合は[tool.typos.default.extend-identifiers]を使う。 詳細はtypos公式ドキュメントを参照。

依存の脆弱性監査の有効化(任意)

依存パッケージの脆弱性をpyfltrの枠組みでまとめて監査したい場合はuv-auditを有効化する。 uv audit(uv 0.10.8以降)がpyproject.tomlを対象にPython依存の既知脆弱性を検査する。 外部脆弱性データベースへ問い合わせるためネットワーク接続が必須で結果が変動する。 ネットワークが不安定なCIで失敗扱いを避けたい場合はuv-audit-severity = "warning"で警告扱いに切り替える。

[tool.pyfltr]
uv-audit = true
# ネットワーク不調時に失敗ではなく警告として扱う場合
# uv-audit-severity = "warning"

既定引数uv-audit-args = ["audit", "--frozen", "--no-progress"]--frozenを含み、監査時にuv.lockを書き換えない。

脆弱性監査の結果はコード変更と無関係に外部データベースの更新で変動する。 このためコミット毎やpre-commitではなく、日次・週次の定期実行に向く。 監査ツールのみをまとめて実行する場合は--commands=auditを指定する。 GitHub Actionsではscheduleトリガーの専用ワークフローへ切り出し、 通常のpush/PR用CIへ混在させない構成を推奨する。 SARIF出力(--output-format=sarif)とgithub/codeql-action/upload-sarifを 組み合わせると、GitHub Code Scanningにアラート管理を委ねられる。 同一の脆弱性は1件のアラートに集約され、解消後の定期実行で自動クローズされる。

JS/TSを併用するプロジェクトでの推奨設定

JS/TSを併用するプロジェクトでは、js-runnerをプロジェクトのパッケージマネージャーに合わせる。 既定のpnpxはツールを都度取得するため、CIで毎回ダウンロードが発生する。 pnpmnpmなど、プロジェクトで使用しているパッケージマネージャーを指定すると、 package.jsonで管理済みのパッケージを再利用できる。

[tool.pyfltr]
js-runner = "pnpm"

pnpm / npm / yarn / directではtextlint-packagesは無視される(package.json側でインストールする前提のため)。 textlintのプリセットやルールもpackage.jsondevDependenciesで管理する。

詳細は設定項目(ツール別)の「js-runner経由で実行するツール」を参照。

.pre-commit-config.yaml

  - repo: local
    hooks:
      - id: pyfltr
        name: pyfltr
        entry: uvx pyfltr fast
        types_or: [python, markdown, toml]
        require_serial: true
        language: system

注意: default_language_versionにはプロジェクトが要求するPythonバージョンを指定する。 PEP 695型パラメーター構文(def f[T](): ...)を使用するプロジェクトではPython 3.12以上が必要。 バージョンが不一致だとcheck-astdebug-statementsフックがSyntaxErrorで失敗する。

ポイント。

  • uvx pyfltr fast: uvがキャッシュするため2回目以降は実用速度で動作し、毎回最新版を取得して実行する
    • dev依存にpyfltrを加えている場合はentry: uv run --frozen pyfltr fastに置き換えてもよい
  • fast: mypy / pylint / pytestなど重いコマンドを除外した高速サブセット
    • formatterがファイルを修正しただけではフックを失敗と判定しない
  • types_or: 必要な種別を列挙する
    • markdownはtextlint / markdownlint、TOML(pyproject.toml)でuv-sort
  • require_serial: true: pyfltr自身が内部で並列化するため、pre-commit側での多重起動を抑止する

pre-commit統合の自動スキップなど双方向の挙動はトラブルシューティングを参照。

pyfltrとpre-commitの呼び出し経路

pyfltrはpre-commitを内部で呼び出し、pre-commitはpyfltrをフックとして呼び出す。 git commit経由でpre-commitが起動した場合は、pyfltrがPRE_COMMIT=1を検出して 内部のpre-commit統合を自動スキップし、二重実行を防ぐ。

sequenceDiagram
    participant U as git commit
    participant PC as pre-commit
    participant PH as pre-commit-hooks
    participant PF as pyfltr fast

    U->>PC: フック起動
    PC->>PH: check-yaml, trailing-whitespace等
    PC->>PF: pyfltr fast(local hook)
    Note over PF: PRE_COMMIT=1 検出で<br/>pre-commit統合をスキップ
    PF->>PF: ruff-format, ruff-check等

逆にmake test等からpyfltr runを呼び出した場合は、pyfltr側がSKIP=pyfltr付きで pre-commit runを変更ファイル指定(--files <対象>)で起動する。 各hook内部のtypestypes_orfilesexcludeフィルタはファイル指定起動でも適用されるため、関係するhookのみ動作する。 これによりpre-commit-hooks(check-yaml等)を統合実行できる。 詳細な挙動と無効化手順はトラブルシューティングを参照。

タスクランナー

pyfltrを呼び出すタスクランナーの設定例。 言語を問わずuvx pyfltrを利用できる。 pre-commitはpyfltrの依存に含まれるため、uvx pre-commitで利用可能になる。

Makefile

uvx方式ではuvがキャッシュするため2回目以降は実用速度で動作し、毎回最新版を取得して実行できる。

.PHONY: format test

# フォーマット + 軽量lint(開発時の手動実行用。自動修正あり)
format:
    uvx pyfltr fast

# 全チェック実行(これを通過すればコミット可能)
test:
    uvx pyfltr run

dev依存にpyfltrを固定する運用ではUV_FROZENでlockfileを尊重しつつ、 uv sync後にuv run pyfltr ...を呼び出す形に置き換えることができる。

export UV_FROZEN := 1

help:
    @cat Makefile

# 開発環境のセットアップ
setup:
    uv sync --all-groups --all-extras
    uvx pre-commit install

# 依存パッケージをアップグレードし全テスト実行
update:
    env --unset UV_FROZEN uv sync --upgrade --all-groups
    uvx pre-commit autoupdate
    $(MAKE) test

# フォーマット + 軽量lint(開発時の手動実行用。自動修正あり)
format:
    uv run pyfltr fast

# 全チェック実行(これを通過すればコミット可能)
test:
    uv run pyfltr run

.PHONY: help setup update format test

mise.toml

言語を問わず利用可能。

[tools]
...
uv = "latest"

[tasks.setup]
description = "開発環境のセットアップ"
run = [
  "...",
  "uvx pre-commit install",
]

[tasks.format]
description = "フォーマット + 軽量lint(開発時の手動実行用。自動修正あり)"
run = [
  "uvx pyfltr fast",
]

[tasks.test]
description = "全チェック (pyfltr run がpre-commitを内部で呼び出す)"
run = [
  "uvx pyfltr run",
]

[tasks.ci]
description = "CI向け全チェック (差分検知で失敗)"
run = [
  "uvx pyfltr ci",
]

ポイント:

  • setup: 開発環境のセットアップ
  • format: pyfltr fast(fix段→formatter段→軽量linter段 + 内部pre-commit統合)を実行する
    • pre-commit-fastが既定でTrueのため、pre-commit-hooks(check-yaml等)もこの1コマンドで実行される
  • test: ローカル開発用
    • pyfltr runがpre-commitを内部で呼び出すため、1コマンドで全チェックが完結する
  • ci: CI用
    • pyfltr ciはformatter差分も含めて失敗扱いにする

.markdownlint-cli2.yaml

markdownlint-cli2が読み込む設定ファイル。$schemaを指定してエディタ補完を有効化する。

$schema: https://raw.githubusercontent.com/DavidAnson/markdownlint-cli2/v0.20.0/schema/markdownlint-cli2-config-schema.json
config:
  # コードブロック/表を除外し、127文字を上限とする
  line-length:
    line_length: 127
    code_blocks: false
    tables: false
  # コードブロック内でタブ文字を許可(Makefileなど用)
  no-hard-tabs:
    code_blocks: false

.textlintrc.yaml

textlintで技術文書向けの複数プリセットと誤用語チェックを併用する例。 対応するtextlint-packagesの設定例は本ページ後半の「textlint-packagesのカスタマイズ」節を参照。

rules:
  preset-ja-technical-writing:
    # ラベル型見出し("ポイント:", "例:" など)のため、文末句点の強制を無効化する
    ja-no-mixed-period: false
    # 技術文書における自然な助詞連結(「〜かどうかを検討するか」など)が頻出するため無効化する
    no-doubled-joshi: false
    # 全角丸括弧の閉じが改行をまたぐ書き方をfalse positiveとして誤検出するため無効化する
    no-unmatched-pair: false
    # 入れ子括弧の閉じ`))`連続をfalse positiveとして誤検出するため無効化する
    ja-no-successive-word: false
    # 引用文や詳細な技術説明で100文字超過が避けられないため緩和する
    sentence-length:
      max: 120
    # ドキュメントを常体(である調)で統一する方針のため
    no-mix-dearu-desumasu:
      preferInHeader: ""
      preferInBody: "である"
      preferInList: "である"
      strict: false
  preset-jtf-style:
    "1.1.3.箇条書き":
      shouldUsePoint: false # 箇条書きは「。」をつけない
    # 和文の半角ピリオド・カンマ禁止ルール。`.gitignore`等のコード識別子の半角ピリオドを
    # 句点へ自動変換して破壊するため無効化する(lintは通過し検出できないため)
    "1.2.1.句点(。)と読点(、)": false
    "4.1.3.ピリオド(.)、カンマ(,)": false
    # 改行折り返し時に全角括弧の前後スペースをfalse positiveとして誤検出するため無効化する
    "3.3.かっこ類と隣接する文字の間のスペースの有無": false
    # コロン終端のラベル記法を多用するため無効化する
    "4.2.7.コロン(:)": false
  ja-no-abusage: true

textlint-packagesのカスタマイズ

追加のtextlintプリセットを使う場合はtextlint-packagesにパッケージ名を列挙する (pnpx / npx起動時に--package / -pとして展開される)。

[tool.pyfltr]
textlint-packages = [
    "textlint-rule-preset-ja-technical-writing",
    "textlint-rule-preset-jtf-style",
    "textlint-rule-ja-no-abusage",
]

共通のコマンドライン引数を追加したい場合は textlint-args を使う。 lint専用のオプション(--format=compact など)は textlint-lint-args に分離する。

[tool.pyfltr]
textlint-args = []
textlint-lint-args = ["--format", "compact"]

旧版のtextlint-args = ["--format", "compact", ...]をそのまま引き継いでもクラッシュしない。 pyfltrはfix段の起動コマンドから--formatペアを自動除去するため。 ただし新規設定ではtextlint-lint-argsに書くことを推奨する。

呼び出し方の使い分け

状況に応じてpyfltrの呼び出し方を以下のいずれかから選ぶ。 コンテナ外では「常に最新版を使う」(uvx pyfltr ...)か「dev依存にバージョンを固定する」 (uv run pyfltr ...)かをプロジェクト判断で選択する。

状況 呼び出し方 補足
公式Dockerイメージ内(CI推奨構成) pyfltr ... イメージ同梱の本体を直接呼ぶ。uvキャッシュ経由の解決を経由しない
コンテナ外・常に最新版を使う uvx pyfltr ... uvが毎回最新を解決する。ローカル開発・軽量CIで使用
コンテナ外・dev依存に固定する uv run pyfltr ... uv add --dev "pyfltr[python]"済みのプロジェクトで使う。UV_FROZENとの併用が有効

CI

GitHub Actionsでpyfltrを実行する構成の例。 リリース時に発行する公式Dockerイメージghcr.io/ak110/pyfltrcontainer:として利用する形が標準的。 uv / pnpm / mise / hadolint / pinact / shellcheck等が同梱されているため、 セットアップステップを毎回実行する必要がない。 キャッシュディレクトリは/cache配下にまとめて配置済み(uv/cache/uvpnpm/cache/pnpmmise/cache/mise)。

Dockerイメージにpyfltr本体を同梱しているため、CI内ではpyfltrを直接呼び出す。 uvx pyfltrを使うとコンテナ起動ごとにuvキャッシュ経由のツール解決が実行され、コンテナ同梱版を使う利点が薄れるため。

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12", "3.13"]
    container:
      image: ghcr.io/ak110/pyfltr:latest
    defaults:
      run:
        shell: bash
    env:
      UV_PYTHON: ${{ matrix.python-version }}
    steps:
      - uses: actions/checkout@v6

      - name: Cache /cache
        uses: actions/cache@v5
        with:
          path: /cache
          key: pyfltr-cache-${{ runner.os }}-py${{ matrix.python-version }}

      - name: Run pyfltr
        run: pyfltr ci --output-format=github-annotations

ポイント。

  • image: ghcr.io/ak110/pyfltr:latest: vX.Y.Zタグも併発行されるため、再現性を重視する場合は固定タグを指定する
    • イメージにはUV_FROZEN=1pnpm config set minimum-release-age 1440が事前設定されているため、 CIワークフロー側で同じ環境変数や設定を再指定する必要はない
  • defaults.run.shell: bash: GitHub Actionsのcontainer:既定シェルはshであり、 既存ワークフローで多用されるset -euo pipefail等のbash前提の記述を通すために指定する
  • UV_PYTHON: uv runが必要なCPythonをロックファイルとmatrix値に従って自動取得する
    • actions/setup-pythonは不要
    • 単一バージョンで十分な場合はstrategy.matrixUV_PYTHONを省ける
  • actions/cache: /cache配下を一括キャッシュする
    • uv / pnpm / miseのキャッシュは内容アドレス指定のため、ロックファイル変更時も追加分がそのまま蓄積される
    • キーはOSのみで足り、ロックファイルhashなどの細かい無効化は不要
  • pyfltr ci: イメージ同梱のpyfltrをそのまま使う
    • uvキャッシュを介した解決を毎回経由せず、コンテナビルド時に確定したバージョンで実行できる
    • 特定バージョンに固定したい場合はimage:のタグ(vX.Y.Z)で揃える
  • --output-format=github-annotations: ::error file=... / ::warning file=...形式の行を標準出力へ出力する
    • プル要求の該当ファイル行にコメントとして表示される

追加のシステムパッケージが必要な場合

公式Dockerイメージの既定ユーザーは非root(sudo無し)のため、aptでシステムパッケージを追加するには container.options--user rootを指定する。 ただし--user root実行時は、checkoutステップが用意したワークスペースの所有者と、コンテナ実行ユーザー(root)が一致しない。 この不一致によりpre-commit等のgit操作がdubious ownershipで停止する。 git config --global --add safe.directoryで対象ワークスペースを信頼対象に追加して回避する。 PDFを画像化するpdf2imageが必要とするpoppler-utilsの導入とsafe.directory設定を含めた例を次に示す。

    container:
      image: ghcr.io/ak110/pyfltr:latest
      options: --user root
    steps:
      - uses: actions/checkout@v6
      - name: safe.directory の設定
        run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
      - name: システムパッケージ導入
        run: |
          apt-get update
          apt-get install -y --no-install-recommends poppler-utils

Dockerイメージを使わない場合(setup-uv方式)

自前runner制約等でDockerイメージを使用できない場合は、astral-sh/setup-uvでuvを導入し、Node.js / pnpmを別途セットアップする。 UV_FROZENPYTHONDEVMODE等の環境変数やpnpm config set minimum-release-age 1440は ワークフロー側で個別指定が必要となる(Dockerイメージでは事前設定済み)。

env:
  PYTHONDEVMODE: "1"
  UV_FROZEN: "1"

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.11", "3.12", "3.13", "3.14"]
    steps:
      - uses: actions/checkout@v6
      - uses: astral-sh/setup-uv@v8
        with:
          python-version: ${{ matrix.python-version }}
          enable-cache: true
      - uses: actions/setup-node@v6
        with:
          node-version: "lts/*"
      - uses: pnpm/action-setup@v6
        with:
          version: latest
      - run: pnpm config set minimum-release-age 1440 --global
      - run: uvx pyfltr ci --output-format=github-annotations
      - run: uv cache prune --ci

uv cache prune --ciはプレビルドwheelをキャッシュから除去し、ソースビルドwheelのみ保持する。 GitHub Actionsのような高帯域環境ではキャッシュサイズを削減できる。 帯域が限られる環境では--ciを外し、プレビルドwheelもキャッシュに残す方が再取得を回避できる。

PRの差分ファイルのみを対象にする

PR(プルリクエスト)で変更したファイルだけを対象に実行する場合は--changed-sinceを使う。 ベースブランチとの差分ファイルのみにチェック対象を限定し、大規模リポジトリでの実行時間を短縮できる。

      - name: Test with pyfltr (changed files only)
        run: uvx pyfltr ci --changed-since=origin/main --output-format=github-annotations

--changed-since=origin/mainorigin/mainからの差分ファイルに限定して実行する。 対象はgit diff --name-onlyが返すコミット差分・trackedファイルの作業ツリー差分・staged差分の和集合となり、 untrackedの新規ファイルは対象外。 gitが不在またはrefが解決できない場合は警告を出力して全体実行へフォールバックする。

GitLab CIでMerge Requestへ表示する

GitLab CIでは--output-format=code-qualityでCode Climate JSON issue形式のサブセットを出力する。 これをartifacts:reports:codequalityとしてアップロードするとMerge Request画面のCode Quality widgetに反映される。 MR diffインライン表示はUltimate tier限定。

stages:
  - lint

pyfltr:
  stage: lint
  image: ghcr.io/astral-sh/uv:python3.13-bookworm
  variables:
    PYTHONDEVMODE: "1"
    UV_FROZEN: "1"
  script:
    - uvx pyfltr ci --output-format=code-quality --output-file=code-quality-report.json
  artifacts:
    when: always
    reports:
      codequality: code-quality-report.json

ポイント。

  • --output-format=code-quality: Code Climate JSON issue形式の配列を出力する
    • --output-fileを指定するとstdoutには従来のtext整形出力が並行して出るため、ジョブログで進捗を確認できる
  • artifacts:reports:codequality: 生成したJSONファイルをGitLabに取り込む
    • Merge Request画面のCode Quality widget(全tier)とMR diffインライン表示(Ultimate tier)に反映される
  • when: always: ジョブが失敗してもアーティファクトを残す指定
    • lintエラーでexit 1したときもレポートを取り込めるようにする

Python以外のプロジェクトでの推奨設定例については推奨設定例(非Pythonプロジェクト)を参照。