Metadata-Version: 2.4
Name: pyfltr
Version: 1.14.0
Summary: Python Formatters, Linters, and Testers Runner.
Project-URL: Homepage, https://github.com/ak110/pyfltr
Author-email: "aki." <mark@aur.ll.to>
License: MIT
License-File: LICENSE
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Topic :: Software Development :: Quality Assurance
Requires-Python: <4.0,>=3.11
Requires-Dist: autoflake>=2.0
Requires-Dist: black>=22.0
Requires-Dist: dill>=0.3
Requires-Dist: flake8-bugbear>=23.0
Requires-Dist: flake8-tidy-imports>=4.11.0
Requires-Dist: isort>=5.0
Requires-Dist: mypy>=1.0
Requires-Dist: natsort>=8.4.0
Requires-Dist: pylint>=3.0
Requires-Dist: pyproject-flake8>=7.0
Requires-Dist: pytest-asyncio>=1.0.0
Requires-Dist: pytest>=7.0
Requires-Dist: pyupgrade>=3.0
Requires-Dist: ruff>=0.12.1
Requires-Dist: textual>=8.0.0
Provides-Extra: pyright
Requires-Dist: pyright[nodejs]>=1.1.403; extra == 'pyright'
Description-Content-Type: text/markdown

# pyfltr: Python Formatters, Linters, and Testers Runner

[![CI](https://github.com/ak110/pyfltr/actions/workflows/ci.yaml/badge.svg)](https://github.com/ak110/pyfltr/actions/workflows/ci.yaml)
[![PyPI version](https://badge.fury.io/py/pyfltr.svg)](https://badge.fury.io/py/pyfltr)

Pythonの各種ツールをまとめて呼び出すツール。

- Formatters
  - pyupgrade
  - autoflake
  - isort
  - black
  - ruff format (既定では無効)
  - ruff check --fix (既定では無効)
- Linters
  - pflake8 + flake8-bugbear + flake8-tidy-imports
  - mypy
  - pylint
  - pyright (既定では無効、`pip install pyfltr[pyright]`でインストール可能)
- Testers
  - pytest

## コンセプト

- 各種ツールをまとめて呼び出したい (時間節約のために並列で)
- 各種ツールのバージョンにはできるだけ依存したくない (ので設定とかは面倒見ない)
- exclude周りは各種ツールで設定方法がバラバラなのでできるだけまとめて解消したい (のでpyfltr側で解決してツールに渡す)
- blackやisortはファイルを修正しつつエラーにもしたい (CIとかを想定) (pyupgradeはもともとそういう動作)
- 設定はできるだけ`pyproject.toml`にまとめる

## インストール

```shell
pip install pyfltr
# pip install pyfltr[pyright]  # pyrightを使う場合
```

## ドキュメント

- [docs/README.md](./docs/README.md)

## 主な使い方

### 通常

```shell
pyfltr [files and/or directories ...]
```

対象を指定しなければカレントディレクトリを指定したのと同じ扱い。

指定したファイルやディレクトリの配下のうち、pytest以外は`*.py`のみ、pytestは`*_test.py`のみに対して実行される。

終了コード。

- 0: Formattersによるファイル変更無し、かつLinters/Testersでのエラー無し
- 1: 上記以外

`--exit-zero-even-if-formatted`を指定すると、Formattersによるファイル変更があっても
Linters/Testersでのエラー無しなら終了コードは0になる。

### 特定のツールのみ実行

```shell
pyfltr \
  --commands=pyupgrade,autoflake,isort,black,ruff-format,\
ruff-check,pflake8,mypy,pylint,pyright,pytest \
  [files and/or directories ...]
```

カンマ区切りで実行するツールだけ指定する。

以下のエイリアスも使用可能。(例: `--commands=fast`)

- `format`: `pyupgrade` `autoflake` `isort` `black` `ruff-format`
- `lint`: `ruff-check` `pflake8` `mypy` `pylint` `pyright`
- `test`: `pytest`
- `fast`: `pyupgrade` `autoflake` `isort` `black` `ruff-format` `ruff-check` `pflake8`

※ 後述の`pyproject.toml`の`[tool.pyfltr]`で無効になっているコマンドは無視される。

### UI

ターミナル上で実行すると、Textual ベースの TUI が自動的に有効になる。

- Summaryタブ: 各コマンドのステータス・エラー数・経過時間をリアルタイム表示
- Errorsタブ: エラー発生時のみ出現し、全コマンドのエラー箇所を`ファイル:行番号`形式で一覧表示
- 各コマンドタブ: コマンドの出力をリアルタイム表示

Errorsタブのエラー一覧は`ファイル:行番号: [コマンド名] メッセージ`形式で、
VSCodeのターミナルからクリックして該当箇所にジャンプできる。

- `--no-ui`: UIを無効化し、出力を直接ターミナルに表示（エラー一覧はサマリー後に表示）
- `--ci`: CI環境向け (`--no-shuffle --no-ui` 相当)

その他のオプションは `pyfltr --help` を参照。

## 設定

`pyproject.toml`で設定する。

### 例

```toml
[tool.pyfltr]
preset = "latest"
pyupgrade-args = ["--py38-plus"]
pylint-args = ["--jobs=4"]
extend-exclude = ["foo", "bar.py"]
```

### 設定項目

設定項目と既定値は`pyfltr --generate-config`で確認可能。

- preset : プリセット設定(後述)
- {command} : 各コマンドの有効/無効
- {command}-path : 実行するコマンド
- {command}-args : 追加のコマンドライン引数
- exclude : 除外するファイル名/ディレクトリ名パターン(既定値あり)
- extend-exclude : 追加で除外するファイル名/ディレクトリ名パターン(既定値は空)

### プリセット設定

`preset`を設定することで、一括して設定を変更できる。
`"latest"` または日付指定 (`"20250710"`) が使用可能。

```toml
[tool.pyfltr]
preset = "latest"
```

これらのプリセットは、以下の設定を自動的に適用する。

- `pyupgrade = false`
- `autoflake = false`
- `pflake8 = false`
- `isort = false`
- `black = false`
- `ruff-format = true`
- `ruff-check = true`

`preset = "latest"`は予告なく動作を変更する可能性あり。

### カスタムコマンド

`[tool.pyfltr.custom-commands]`で任意のツールを追加できる。

```toml
[tool.pyfltr.custom-commands.bandit]
type = "linter"
path = "bandit"
args = ["-r", "-f", "custom"]
targets = "*.py"
error-pattern = '(?P<file>[^:]+):(?P<line>\d+):(?P<col>\d+):\s*(?P<message>.+)'
```

設定項目。

- `type` (必須): `"formatter"` / `"linter"` / `"tester"`
  - formatterは直列実行、linter/testerは並列実行
- `path`: 実行コマンド（省略時はコマンド名）
- `args`: 追加引数（省略時は空）
- `targets`: 対象ファイルパターン（省略時は`"*.py"`）
- `error-pattern`: エラーパース用正規表現（省略可）
  - `file`と`line`と`message`の名前付きグループが必須
  - `col`は任意
  - 指定するとErrorsタブやエラー一覧に表示される

ビルトインコマンド（mypy等）は自動的にエラーパースされる。
カスタムコマンドも`--{name}-args`やenable/disableが使用可能。

## 各種設定例

### pyproject.toml

```toml
[dependency-groups]
dev = [
    ...
    "pyfltr",
]

...

[tool.pyfltr]
preset = "latest"
pyright = true
pylint-args = ["--jobs=4"]
mypy-args = ["--enable-error-code=unused-awaitable"]

[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
]

[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"
log_level = "DEBUG"
xfail_strict = true
asyncio_mode = "strict"
asyncio_default_fixture_loop_scope = "session"
asyncio_default_test_loop_scope = "session"
```

### .pre-commit-config.yaml

```yaml
  - repo: https://github.com/DavidAnson/markdownlint-cli2
    rev: v0.21.0
    hooks:
      - id: markdownlint-cli2

  - repo: local
    hooks:
      - id: textlint
        name: textlint
        entry: textlint
        language: node
        files: \.(md|mdown|markdown)$
        args: []
        require_serial: false
        additional_dependencies:
          - textlint@15.5.1
          - textlint-rule-preset-ja-technical-writing@12.0.2
          - textlint-rule-preset-japanese@10.0.4

  - repo: local
    hooks:
      - id: pyfltr
        name: pyfltr
        entry: uv run pyfltr --commands=fast
        types: [python]
        require_serial: true
        language: system
```

### CI

```yaml
  - uv install --no-interaction
  - uv run pyfltr
```
