claudy_writes_ruff_conf: a skill for linting to our (my) "wonky code style" #88
|
|
@ -0,0 +1,72 @@
|
|||
---
|
||||
name: lint
|
||||
description: >
|
||||
Run ruff lint checks on piker Python files for
|
||||
docstring style (D2xx) and code convention
|
||||
compliance. Checks staged files by default.
|
||||
user-invocable: true
|
||||
argument-hint: "[--all|--fix|--stats|paths...]"
|
||||
allowed-tools: Bash(python3 *), Bash(ruff *), Read, Edit
|
||||
---
|
||||
|
||||
# Ruff Lint Checker
|
||||
|
||||
Run piker's ruff config against staged files,
|
||||
specific paths, or the full codebase.
|
||||
|
||||
## Available scripts
|
||||
|
||||
- **`scripts/check.py`** — self-contained (PEP 723)
|
||||
wrapper around `ruff check` that defaults to staged
|
||||
files and uses the project's `ruff.toml`.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
# check staged Python files (default)
|
||||
python3 scripts/check.py
|
||||
|
||||
# check full codebase
|
||||
python3 scripts/check.py --all
|
||||
|
||||
# auto-fix fixable violations
|
||||
python3 scripts/check.py --fix
|
||||
|
||||
# preview fixes without applying
|
||||
python3 scripts/check.py --diff
|
||||
|
||||
# show per-rule violation counts
|
||||
python3 scripts/check.py --stats
|
||||
|
||||
# check specific files
|
||||
python3 scripts/check.py piker/ui/_style.py
|
||||
```
|
||||
|
||||
## Common violations
|
||||
|
||||
| Rule | Meaning | Fixable? |
|
||||
|------|---------|----------|
|
||||
| D213 | summary not on 2nd line after `'''` | yes |
|
||||
| D205 | no blank line after summary | no |
|
||||
| D204 | no blank line after class docstring | yes |
|
||||
| D209 | closing quotes not on own line | yes |
|
||||
| D200 | ignored — piker always multiline | n/a |
|
||||
| W291 | trailing whitespace | yes |
|
||||
|
||||
## Fixing D213 (most common)
|
||||
|
||||
Convert this:
|
||||
```python
|
||||
"""Summary on first line."""
|
||||
```
|
||||
|
||||
To piker's `'''` multiline style:
|
||||
```python
|
||||
'''
|
||||
Summary on second line.
|
||||
|
||||
'''
|
||||
```
|
||||
|
||||
For D205, insert a blank line between the summary
|
||||
and description paragraphs inside the docstring.
|
||||
|
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env python3
|
||||
# /// script
|
||||
# requires-python = ">=3.12"
|
||||
# dependencies = []
|
||||
# ///
|
||||
'''
|
||||
Ruff lint checker for piker docstring and code style.
|
||||
|
||||
Checks staged files by default, or the full `piker/`
|
||||
tree with `--all`. Uses the project's `ruff.toml`.
|
||||
|
||||
'''
|
||||
import argparse
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def get_staged_py_files() -> list[str]:
|
||||
'''
|
||||
Return staged Python file paths (added/copied/modified).
|
||||
|
||||
'''
|
||||
result = subprocess.run(
|
||||
[
|
||||
'git', 'diff',
|
||||
'--cached',
|
||||
'--name-only',
|
||||
'--diff-filter=ACM',
|
||||
'--', '*.py',
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
return [
|
||||
f for f in result.stdout.strip().split('\n')
|
||||
if f
|
||||
]
|
||||
|
||||
|
||||
def main() -> int:
|
||||
'''
|
||||
Parse args and run `ruff check` against targets.
|
||||
|
||||
'''
|
||||
ap = argparse.ArgumentParser(
|
||||
description=(
|
||||
"Run ruff check with piker's ruff.toml config.\n"
|
||||
'\n'
|
||||
'Default: checks staged .py files only.\n'
|
||||
'Use --all for the full piker/ tree.'
|
||||
),
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
)
|
||||
ap.add_argument(
|
||||
'paths',
|
||||
nargs='*',
|
||||
help='files/dirs to check (default: staged)',
|
||||
)
|
||||
ap.add_argument(
|
||||
'--all', '-a',
|
||||
action='store_true',
|
||||
dest='check_all',
|
||||
help='check entire piker/ directory',
|
||||
)
|
||||
ap.add_argument(
|
||||
'--fix',
|
||||
action='store_true',
|
||||
help='auto-fix fixable violations',
|
||||
)
|
||||
ap.add_argument(
|
||||
'--diff',
|
||||
action='store_true',
|
||||
help='preview fixes as unified diff (dry-run)',
|
||||
)
|
||||
ap.add_argument(
|
||||
'--stats',
|
||||
action='store_true',
|
||||
help='show per-rule violation counts only',
|
||||
)
|
||||
args = ap.parse_args()
|
||||
|
||||
# determine check targets
|
||||
if args.check_all:
|
||||
targets: list[str] = ['piker/']
|
||||
elif args.paths:
|
||||
targets = args.paths
|
||||
else:
|
||||
targets = get_staged_py_files()
|
||||
if not targets:
|
||||
print(
|
||||
'No staged Python files found.\n'
|
||||
'Use --all for full codebase '
|
||||
'or pass file paths.'
|
||||
)
|
||||
return 0
|
||||
print(
|
||||
f'Checking {len(targets)} staged file(s)..\n'
|
||||
)
|
||||
|
||||
# ensure ruff is available on PATH
|
||||
if not shutil.which('ruff'):
|
||||
print(
|
||||
'Error: `ruff` not found on PATH.\n'
|
||||
'On NixOS: add to flake.nix or '
|
||||
'`nix-shell -p ruff`\n'
|
||||
'Otherwise: `uv sync --group lint` or '
|
||||
'`pip install ruff`'
|
||||
)
|
||||
return 127
|
||||
|
||||
# build ruff command
|
||||
cmd: list[str] = ['ruff', 'check']
|
||||
|
||||
if args.diff:
|
||||
cmd.append('--diff')
|
||||
elif args.fix:
|
||||
cmd.append('--fix')
|
||||
|
||||
if args.stats:
|
||||
cmd.append('--statistics')
|
||||
|
||||
cmd.extend(targets)
|
||||
|
||||
result = subprocess.run(cmd)
|
||||
return result.returncode
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
62
ruff.toml
62
ruff.toml
|
|
@ -35,7 +35,7 @@ exclude = [
|
|||
line-length = 88
|
||||
indent-width = 4
|
||||
|
||||
# Assume Python 3.9
|
||||
# !XXX sync with `pyproject.toml`!
|
||||
target-version = "py312"
|
||||
|
||||
# ------ - ------
|
||||
|
|
@ -44,11 +44,43 @@ target-version = "py312"
|
|||
|
||||
|
||||
[lint]
|
||||
# Enable Pyflakes (`F`) and a subset of the pycodestyle (`E`) codes by default.
|
||||
# Unlike Flake8, Ruff doesn't enable pycodestyle warnings (`W`) or
|
||||
# McCabe complexity (`C901`) by default.
|
||||
select = ["E4", "E7", "E9", "F"]
|
||||
ignore = []
|
||||
select = [
|
||||
# pycodestyle error subsets (pre-existing)
|
||||
"E4", "E7", "E9",
|
||||
# pyflakes (pre-existing)
|
||||
"F",
|
||||
|
||||
# -- pydocstyle: enforce piker's ''' multiline style --
|
||||
# D2xx whitespace and formatting rules; most are
|
||||
# auto-fixable via `ruff check --fix`.
|
||||
# NOTE: D1xx (missing-docstring) rules intentionally
|
||||
# excluded to avoid noise across the existing codebase.
|
||||
"D2",
|
||||
# D4xx content checks (cherry-picked)
|
||||
"D402", # first line != function signature
|
||||
"D403", # capitalize first word
|
||||
"D419", # no empty docstrings
|
||||
# NOTE: D3xx skipped entirely since D300 enforces
|
||||
# triple-double-quotes `"""` which conflicts with
|
||||
# piker's `'''` convention (the formatter's
|
||||
# `quote-style = "single"` handles conversion).
|
||||
|
||||
# pycodestyle warnings
|
||||
"W",
|
||||
]
|
||||
ignore = [
|
||||
# -- pydocstyle ignores for piker conventions --
|
||||
# piker ALWAYS uses multiline docstring style, never
|
||||
# single-line, so disable the "fit on one line" rule.
|
||||
"D200",
|
||||
# piker uses NO blank line before class docstrings
|
||||
# (D211) not 1-blank-line (D203); these conflict.
|
||||
"D203",
|
||||
# piker puts the summary on the SECOND line after
|
||||
# an opening `'''` (D213); not on the same line as
|
||||
# the opening quotes (D212); these conflict.
|
||||
"D212",
|
||||
]
|
||||
ignore-init-module-imports = false
|
||||
|
||||
[lint.per-file-ignores]
|
||||
|
|
@ -79,16 +111,10 @@ skip-magic-trailing-comma = false
|
|||
# Like Black, automatically detect the appropriate line ending.
|
||||
line-ending = "auto"
|
||||
|
||||
# Enable auto-formatting of code examples in docstrings. Markdown,
|
||||
# reStructuredText code/literal blocks and doctests are all supported.
|
||||
#
|
||||
# This is currently disabled by default, but it is planned for this
|
||||
# to be opt-out in the future.
|
||||
docstring-code-format = false
|
||||
# Auto-format code examples inside docstrings
|
||||
# (>>> blocks, code fences, etc.)
|
||||
docstring-code-format = true
|
||||
|
||||
# Set the line length limit used when formatting code snippets in
|
||||
# docstrings.
|
||||
#
|
||||
# This only has an effect when the `docstring-code-format` setting is
|
||||
# enabled.
|
||||
docstring-code-line-length = "dynamic"
|
||||
# Use piker's 67-char target for code inside docstrings
|
||||
# (only applies when `docstring-code-format = true`).
|
||||
docstring-code-line-length = 67
|
||||
|
|
|
|||
Loading…
Reference in New Issue