pyclichecker 2.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyclichecker-2.4.1/.github/workflows/ci.yml +52 -0
- pyclichecker-2.4.1/.github/workflows/publish.yml +80 -0
- pyclichecker-2.4.1/AGENTS.md +120 -0
- pyclichecker-2.4.1/CHANGELOG.md +63 -0
- pyclichecker-2.4.1/CONTRIBUTING.md +58 -0
- pyclichecker-2.4.1/LICENSE +21 -0
- pyclichecker-2.4.1/PKG-INFO +219 -0
- pyclichecker-2.4.1/README.md +198 -0
- pyclichecker-2.4.1/pyproject.toml +69 -0
- pyclichecker-2.4.1/pyproject.toml.orig +57 -0
- pyclichecker-2.4.1/skills/pyclichecker/SKILL.md +68 -0
- pyclichecker-2.4.1/skills/pyclichecker/agents/openai.yaml +4 -0
- pyclichecker-2.4.1/src/pyclichecker/__init__.py +29 -0
- pyclichecker-2.4.1/src/pyclichecker/__main__.py +5 -0
- pyclichecker-2.4.1/src/pyclichecker/_version.py +10 -0
- pyclichecker-2.4.1/src/pyclichecker/cli.py +260 -0
- pyclichecker-2.4.1/src/pyclichecker/config.py +23 -0
- pyclichecker-2.4.1/src/pyclichecker/diagnostics.py +149 -0
- pyclichecker-2.4.1/src/pyclichecker/discovery.py +150 -0
- pyclichecker-2.4.1/src/pyclichecker/rules.py +2286 -0
- pyclichecker-2.4.1/tests/__init__.py +1 -0
- pyclichecker-2.4.1/tests/test_pyclichecker.py +1266 -0
- pyclichecker-2.4.1/uv.lock +43 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: ${{ github.workflow }}-${{ github.ref }}
|
|
12
|
+
cancel-in-progress: true
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
quality:
|
|
16
|
+
name: Python 3.14
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
timeout-minutes: 10
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Check out repository
|
|
22
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
23
|
+
|
|
24
|
+
- name: Set up uv and Python
|
|
25
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
26
|
+
with:
|
|
27
|
+
version: "0.12.5"
|
|
28
|
+
python-version: "3.14"
|
|
29
|
+
enable-cache: true
|
|
30
|
+
|
|
31
|
+
- name: Install locked development environment
|
|
32
|
+
run: uv sync --locked
|
|
33
|
+
|
|
34
|
+
- name: Run unit tests
|
|
35
|
+
run: uv run python -m unittest discover -v
|
|
36
|
+
|
|
37
|
+
- name: Run Ruff
|
|
38
|
+
run: |
|
|
39
|
+
uv run ruff check .
|
|
40
|
+
uv run ruff format --check .
|
|
41
|
+
|
|
42
|
+
- name: Run pyclichecker on itself
|
|
43
|
+
run: uv run pyclichecker src tests
|
|
44
|
+
|
|
45
|
+
- name: Build distributions
|
|
46
|
+
run: uv build
|
|
47
|
+
|
|
48
|
+
- name: Smoke-test built wheel
|
|
49
|
+
run: |
|
|
50
|
+
wheel="$(find dist -name '*.whl' -print -quit)"
|
|
51
|
+
uvx --from "$wheel" pyclichecker --version
|
|
52
|
+
uvx --from "$wheel" pyclichecker src tests
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: Publish to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
concurrency:
|
|
11
|
+
group: publish-${{ github.event.release.tag_name }}
|
|
12
|
+
cancel-in-progress: false
|
|
13
|
+
|
|
14
|
+
jobs:
|
|
15
|
+
publish:
|
|
16
|
+
name: Build and publish
|
|
17
|
+
if: github.event.release.prerelease == false
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
timeout-minutes: 10
|
|
20
|
+
environment:
|
|
21
|
+
name: pypi
|
|
22
|
+
url: https://pypi.org/p/pyclichecker
|
|
23
|
+
permissions:
|
|
24
|
+
contents: read
|
|
25
|
+
id-token: write
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- name: Check out repository
|
|
29
|
+
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
30
|
+
|
|
31
|
+
- name: Set up uv and Python
|
|
32
|
+
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
|
|
33
|
+
with:
|
|
34
|
+
version: "0.12.5"
|
|
35
|
+
python-version: "3.14"
|
|
36
|
+
enable-cache: true
|
|
37
|
+
|
|
38
|
+
- name: Install locked development environment
|
|
39
|
+
run: uv sync --locked
|
|
40
|
+
|
|
41
|
+
- name: Verify release tag
|
|
42
|
+
env:
|
|
43
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
44
|
+
run: |
|
|
45
|
+
project_version="$(uv run python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
|
|
46
|
+
test "$RELEASE_TAG" = "v$project_version"
|
|
47
|
+
|
|
48
|
+
- name: Run quality gates
|
|
49
|
+
run: |
|
|
50
|
+
uv run python -m unittest discover -v
|
|
51
|
+
uv run ruff check .
|
|
52
|
+
uv run ruff format --check .
|
|
53
|
+
uv run pyclichecker src tests
|
|
54
|
+
|
|
55
|
+
- name: Build distributions
|
|
56
|
+
run: uv build
|
|
57
|
+
|
|
58
|
+
- name: Inspect archives
|
|
59
|
+
run: |
|
|
60
|
+
for archive in dist/*.whl; do
|
|
61
|
+
uv run python -m zipfile -l "$archive"
|
|
62
|
+
done
|
|
63
|
+
for archive in dist/*.tar.gz; do
|
|
64
|
+
tar -tzf "$archive"
|
|
65
|
+
done
|
|
66
|
+
|
|
67
|
+
- name: Smoke-test built wheel
|
|
68
|
+
run: |
|
|
69
|
+
repo_root="$PWD"
|
|
70
|
+
wheel="$repo_root/dist/pyclichecker-${RELEASE_TAG#v}-py3-none-any.whl"
|
|
71
|
+
scratch="$(mktemp -d)"
|
|
72
|
+
cd "$scratch"
|
|
73
|
+
uvx --from "$wheel" pyclichecker --version
|
|
74
|
+
uvx --from "$wheel" pyclichecker --list-rules
|
|
75
|
+
uvx --from "$wheel" pyclichecker "$repo_root/src" "$repo_root/tests"
|
|
76
|
+
env:
|
|
77
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
78
|
+
|
|
79
|
+
- name: Publish distributions
|
|
80
|
+
run: uv publish --trusted-publishing always
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# AGENTS.md
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
This repository builds the `pyclichecker` command, a read-only AST linter for
|
|
6
|
+
Python quality defects associated with rushed or generated code.
|
|
7
|
+
|
|
8
|
+
## Runtime
|
|
9
|
+
|
|
10
|
+
- The project requires Python 3.14.
|
|
11
|
+
- Package and environment operations use `uv`.
|
|
12
|
+
- Runtime code has no third-party dependencies.
|
|
13
|
+
- MUST NOT add a runtime dependency when the standard library provides a clear implementation.
|
|
14
|
+
- MUST keep source under `src/pyclichecker/` and tests under `tests/`.
|
|
15
|
+
|
|
16
|
+
## Rule changes
|
|
17
|
+
|
|
18
|
+
- MUST give each new rule a permanent `SLP` code, severity, title, and actionable description.
|
|
19
|
+
- MUST NOT reuse or silently change the meaning of an existing code.
|
|
20
|
+
- MUST favor behavioral defects and high-signal maintainability risks over style checks already handled by Ruff.
|
|
21
|
+
- MUST add a positive test, a negative test, and relevant false-positive tests for every rule.
|
|
22
|
+
- MUST keep output deterministic by sorting diagnostics by path, line, column, and code.
|
|
23
|
+
- SHOULD explain the concrete risk and a practical correction in each diagnostic.
|
|
24
|
+
|
|
25
|
+
## Suppressions
|
|
26
|
+
|
|
27
|
+
- MUST parse suppression directives from comment tokens, not source substrings.
|
|
28
|
+
- MUST require an explicit `SLP` code for inline suppression.
|
|
29
|
+
- MUST NOT make bare `# noqa` suppress pyclichecker.
|
|
30
|
+
- SHOULD fix a finding before adding a suppression.
|
|
31
|
+
|
|
32
|
+
## CLI compatibility
|
|
33
|
+
|
|
34
|
+
- Exit `0` means a complete run with no finding at the configured failure
|
|
35
|
+
threshold.
|
|
36
|
+
- Exit `1` means findings met the configured failure threshold.
|
|
37
|
+
- Exit `2` means the scan was incomplete or could not operate.
|
|
38
|
+
- MUST preserve text, JSON, and GitHub output modes.
|
|
39
|
+
- MUST treat operational errors as exit `2`, even when findings are also present.
|
|
40
|
+
|
|
41
|
+
## Testing
|
|
42
|
+
|
|
43
|
+
Run every check from the repository root.
|
|
44
|
+
|
|
45
|
+
### Unit tests
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
uv run python -m unittest discover -v
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Every changed rule MUST have:
|
|
52
|
+
|
|
53
|
+
- A source example that reports the rule.
|
|
54
|
+
- A corrected example that does not report it.
|
|
55
|
+
- False-positive coverage for aliases, shadowing, delegation, test fixtures, or suppressions when relevant.
|
|
56
|
+
|
|
57
|
+
### Static checks
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
uv run ruff check .
|
|
61
|
+
uv run ruff format --check .
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Self-lint
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
uv run pyclichecker src tests
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Package build
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
uv build
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Built archive inspection
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
for archive in dist/*.whl; do
|
|
80
|
+
uv run python -m zipfile -l "$archive"
|
|
81
|
+
done
|
|
82
|
+
for archive in dist/*.tar.gz; do
|
|
83
|
+
tar -tzf "$archive"
|
|
84
|
+
done
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Installed CLI smoke tests
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
uvx --from . pyclichecker --version
|
|
91
|
+
uvx --from . pyclichecker --list-rules
|
|
92
|
+
uvx --from . pyclichecker src tests
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Fresh-directory package smoke test
|
|
96
|
+
|
|
97
|
+
Run the built wheel away from the repository so imports cannot accidentally resolve from the checkout:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
repo_root="$PWD"
|
|
101
|
+
version="$(uv run python -c 'import tomllib; print(tomllib.load(open("pyproject.toml", "rb"))["project"]["version"])')"
|
|
102
|
+
wheel="$repo_root/dist/pyclichecker-$version-py3-none-any.whl"
|
|
103
|
+
scratch="$(mktemp -d)"
|
|
104
|
+
(
|
|
105
|
+
cd "$scratch"
|
|
106
|
+
uvx --from "$wheel" pyclichecker --version
|
|
107
|
+
uvx --from "$wheel" pyclichecker --list-rules
|
|
108
|
+
uvx --from "$wheel" pyclichecker "$repo_root/src" "$repo_root/tests"
|
|
109
|
+
)
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Release changes
|
|
113
|
+
|
|
114
|
+
- MUST keep the project version and `CHANGELOG.md` aligned.
|
|
115
|
+
- MUST inspect both wheel and source archive contents before release.
|
|
116
|
+
- MUST run the built wheel from a fresh directory using only documented commands.
|
|
117
|
+
- MUST preserve the approved MIT license in package metadata and release
|
|
118
|
+
archives.
|
|
119
|
+
- MUST scan for credentials, personal paths, and internal references before a
|
|
120
|
+
public push.
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.4.1 - 2026-08-21
|
|
4
|
+
|
|
5
|
+
- Calibrated `SLP015` to report overridable constructor dispatch only while
|
|
6
|
+
definitely initialized instance state is still pending.
|
|
7
|
+
- Reduced `SLP016` false positives for `hasattr()` guards, test-and-set
|
|
8
|
+
initialization, and explicit `AttributeError` fallback.
|
|
9
|
+
- Exempted test modules from `SLP017`, where shared mutable containers are
|
|
10
|
+
commonly intentional recorders and fixtures.
|
|
11
|
+
- Added PyPI Trusted Publishing and documented the short
|
|
12
|
+
`uvx pyclichecker .` command.
|
|
13
|
+
- Updated the bundled Agent Skill to use the pinned PyPI release.
|
|
14
|
+
|
|
15
|
+
## 2.4.0 - 2026-08-21
|
|
16
|
+
|
|
17
|
+
- Added `SLP015` for constructor calls that dispatch to overridable same-class
|
|
18
|
+
methods before initialization is complete.
|
|
19
|
+
- Added `SLP016` for instance attributes that are initialized on only some
|
|
20
|
+
successful constructor paths and later read without a local assignment.
|
|
21
|
+
- Added `SLP017` for instance methods that mutate mutable list, dictionary, or
|
|
22
|
+
set state inherited from the class.
|
|
23
|
+
- Added conservative exemptions for final and private methods, class fallbacks,
|
|
24
|
+
dynamic attributes, `ClassVar`, per-instance initialization, explicit class
|
|
25
|
+
mutation, and nested scopes.
|
|
26
|
+
- Added positive, corrected, false-positive, and inline-suppression coverage
|
|
27
|
+
for all three class-correctness rules.
|
|
28
|
+
|
|
29
|
+
## 2.3.0 - 2026-08-21
|
|
30
|
+
|
|
31
|
+
- Added `SLP009` for unchecked `subprocess.run` outcomes.
|
|
32
|
+
- Added `SLP010` for synchronous network calls that omit a timeout or set it to
|
|
33
|
+
`None`.
|
|
34
|
+
- Added `SLP011` for HTTP responses consumed without a success check.
|
|
35
|
+
- Added `SLP012` for hardcoded paths tied to a user's home directory.
|
|
36
|
+
- Added `SLP014` for tests that can pass without an explicit result or
|
|
37
|
+
expected-failure oracle.
|
|
38
|
+
- Expanded `SLP006` to inspect environment defaults, mappings, keyword
|
|
39
|
+
arguments, and function defaults.
|
|
40
|
+
- Added import-alias, shadowing, delegation, suppression, and false-positive
|
|
41
|
+
coverage for the new checks.
|
|
42
|
+
- Added a version-pinned Agent Skill and portable `AGENTS.md` workflow for
|
|
43
|
+
running strict changed-file and repository gates through `uvx`.
|
|
44
|
+
- Added MIT licensing, public project metadata, contribution guidance, and
|
|
45
|
+
pinned GitHub Actions validation.
|
|
46
|
+
|
|
47
|
+
## 2.2.0 - 2026-08-21
|
|
48
|
+
|
|
49
|
+
- Converted the standalone script into a Python 3.14 `uv` package with a
|
|
50
|
+
`pyclichecker` console command.
|
|
51
|
+
- Added locked development tooling, module and console entry points, structured
|
|
52
|
+
modules, and a 26-test suite.
|
|
53
|
+
- Preserved all ten existing rules, including `SLP013` diagnostics for known
|
|
54
|
+
blocking calls inside async functions.
|
|
55
|
+
- Made suppressions token-aware so directive-like strings no longer hide
|
|
56
|
+
findings.
|
|
57
|
+
- Required explicit `SLP` codes for inline `noqa` suppression.
|
|
58
|
+
- Made zero-file scans return operational exit code `2`.
|
|
59
|
+
|
|
60
|
+
## 2.1.0
|
|
61
|
+
|
|
62
|
+
- This version number came from the original standalone script. No earlier
|
|
63
|
+
release notes were available in the source folder.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Contributions that improve signal, reduce false positives, or make the command
|
|
4
|
+
easier to use are welcome.
|
|
5
|
+
|
|
6
|
+
## Development setup
|
|
7
|
+
|
|
8
|
+
The project requires Python 3.14 and [uv](https://docs.astral.sh/uv/).
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
git clone https://github.com/ktreharrison/pyclichecker.git
|
|
12
|
+
cd pyclichecker
|
|
13
|
+
uv sync --locked
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Validation
|
|
17
|
+
|
|
18
|
+
Run every check before opening a pull request:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uv run python -m unittest discover -v
|
|
22
|
+
uv run ruff check .
|
|
23
|
+
uv run ruff format --check .
|
|
24
|
+
uv run pyclichecker src tests
|
|
25
|
+
uv build
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Rule changes
|
|
29
|
+
|
|
30
|
+
Every new rule needs a permanent `SLP` code, an actionable diagnostic, and
|
|
31
|
+
tests for:
|
|
32
|
+
|
|
33
|
+
- Code that must produce the finding.
|
|
34
|
+
- Corrected code that must not produce the finding.
|
|
35
|
+
- Relevant false-positive cases, including aliases, shadowing, delegation,
|
|
36
|
+
fixtures, and suppressions.
|
|
37
|
+
|
|
38
|
+
Prefer behavioral defects and high-signal maintainability risks over formatting
|
|
39
|
+
or style checks already covered by Ruff.
|
|
40
|
+
|
|
41
|
+
## Reporting a false positive
|
|
42
|
+
|
|
43
|
+
Open an issue with the rule code, a minimal source example, the actual result,
|
|
44
|
+
and the result you expected. Remove credentials and proprietary code before
|
|
45
|
+
posting.
|
|
46
|
+
|
|
47
|
+
## Maintainer releases
|
|
48
|
+
|
|
49
|
+
1. Update the version in `pyproject.toml`, refresh `uv.lock`, and add the
|
|
50
|
+
matching `CHANGELOG.md` entry.
|
|
51
|
+
2. Run every validation and package inspection command in `AGENTS.md`.
|
|
52
|
+
3. Merge the release commit to `main` and create a GitHub release tagged
|
|
53
|
+
`v<version>`.
|
|
54
|
+
4. The `publish.yml` workflow verifies the tag, rebuilds and smoke-tests the
|
|
55
|
+
archives, then publishes them to PyPI through Trusted Publishing.
|
|
56
|
+
|
|
57
|
+
The publishing job uses GitHub's short-lived OIDC identity. Do not add a PyPI
|
|
58
|
+
API token to repository secrets.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ken Harrison
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
|
7
|
+
the Software without restriction, including without limitation the rights to
|
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
10
|
+
so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pyclichecker
|
|
3
|
+
Version: 2.4.1
|
|
4
|
+
Summary: A high-signal Python linter for defects common in rushed and AI-generated code
|
|
5
|
+
Keywords: ai,ast,code-quality,linter,python
|
|
6
|
+
Author: Ken Harrison
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Environment :: Console
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
14
|
+
Classifier: Topic :: Software Development :: Quality Assurance
|
|
15
|
+
Requires-Python: >=3.14
|
|
16
|
+
Project-URL: Homepage, https://github.com/ktreharrison/pyclichecker
|
|
17
|
+
Project-URL: Repository, https://github.com/ktreharrison/pyclichecker
|
|
18
|
+
Project-URL: Issues, https://github.com/ktreharrison/pyclichecker/issues
|
|
19
|
+
Project-URL: Changelog, https://github.com/ktreharrison/pyclichecker/blob/main/CHANGELOG.md
|
|
20
|
+
Description-Content-Type: text/markdown
|
|
21
|
+
|
|
22
|
+
# pyclichecker
|
|
23
|
+
|
|
24
|
+
[](https://github.com/ktreharrison/pyclichecker/actions/workflows/ci.yml)
|
|
25
|
+
[](https://pypi.org/project/pyclichecker/)
|
|
26
|
+
[](LICENSE)
|
|
27
|
+
|
|
28
|
+
`pyclichecker` is a read-only Python linter for high-signal defects and
|
|
29
|
+
maintainability smells that often appear in rushed or generated code. It parses
|
|
30
|
+
source with Python's AST and token APIs and has no runtime dependencies.
|
|
31
|
+
|
|
32
|
+
It is a code-quality tool, not an AI-authorship detector. The same finding can
|
|
33
|
+
occur in human-written code, and every finding should be judged in context.
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
The project requires Python 3.14. Run it directly without installing:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
uvx pyclichecker .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
For a persistent command, install it with `uv`:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
uv tool install pyclichecker
|
|
47
|
+
pyclichecker .
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
It can lint one file, consume standard input, emit JSON for an agent, or emit
|
|
51
|
+
GitHub workflow annotations:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
pyclichecker app.py
|
|
55
|
+
printf 'def unfinished():\n pass\n' | pyclichecker -
|
|
56
|
+
pyclichecker . --format json
|
|
57
|
+
pyclichecker . --format github
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Pin a release when reproducibility matters:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uvx pyclichecker@2.4.1 .
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Reading a result
|
|
67
|
+
|
|
68
|
+
Text diagnostics use the conventional `path:line:column: code message` shape:
|
|
69
|
+
|
|
70
|
+
```text
|
|
71
|
+
app.py:8:1: SLP001 `load_config` is a concrete placeholder implementation
|
|
72
|
+
Found 1 issue(s) in 1 file(s).
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
A practical review loop is:
|
|
76
|
+
|
|
77
|
+
1. Open the reported file and line.
|
|
78
|
+
2. Decide whether the behavior is intentional.
|
|
79
|
+
3. Fix the implementation, error handling, or structure.
|
|
80
|
+
4. Run the same command again.
|
|
81
|
+
5. Suppress only the specific rule when the code is intentionally exceptional.
|
|
82
|
+
|
|
83
|
+
## Rules
|
|
84
|
+
|
|
85
|
+
`pyclichecker --list-rules` reports these rules:
|
|
86
|
+
|
|
87
|
+
| Code | Severity | Check | Typical correction |
|
|
88
|
+
|---|---|---|---|
|
|
89
|
+
| `SLP000` | error | Invalid Python syntax | Correct the reported syntax before trusting the rest of the scan. |
|
|
90
|
+
| `SLP001` | error | Placeholder implementation | Implement the function, remove it, or make the contract explicitly abstract. |
|
|
91
|
+
| `SLP002` | error | Silently swallowed exception | Handle, record, or re-raise the failure. |
|
|
92
|
+
| `SLP003` | warning | Broad exception converted to fallback behavior | Catch the failures you expect and preserve unexpected ones. |
|
|
93
|
+
| `SLP004` | warning | Async function with no async behavior | Make it synchronous or perform the intended awaited operation. |
|
|
94
|
+
| `SLP005` | warning | Duplicate implementation in the same file | Extract shared behavior so copies cannot drift. |
|
|
95
|
+
| `SLP006` | error | Obvious placeholder in configuration | Require a real configured value instead of shipping a dummy fallback. |
|
|
96
|
+
| `SLP007` | warning | Cluster of narrating comments | Remove narration or replace it with the reason behind non-obvious code. |
|
|
97
|
+
| `SLP008` | warning | Oversized function | Split distinct responsibilities and test them independently. |
|
|
98
|
+
| `SLP009` | warning | Unchecked `subprocess.run` result | Use `check=True`, inspect `returncode`, or deliberately return the result. |
|
|
99
|
+
| `SLP010` | warning | Synchronous network call omits a timeout or sets it to `None` | Pass an explicit timeout appropriate for the operation. |
|
|
100
|
+
| `SLP011` | warning | HTTP response consumed without a success check | Call `raise_for_status()` or validate the status before using the body. |
|
|
101
|
+
| `SLP012` | warning | Path tied to one user's home directory | Use `Path.home()`, a project-relative path, or configuration. |
|
|
102
|
+
| `SLP013` | warning | Known blocking API called inside async code | Use an async API or move the blocking call to a worker thread. |
|
|
103
|
+
| `SLP014` | warning | Test has no explicit result or failure oracle | Assert an observable result or declare the expected exception or failure. |
|
|
104
|
+
| `SLP015` | warning | Overridable method called before constructor state is initialized | Initialize state before dispatch, or make the hook private or final. |
|
|
105
|
+
| `SLP016` | warning | Instance state initialized on only some constructor paths | Initialize the attribute unconditionally before other methods can read it. |
|
|
106
|
+
| `SLP017` | warning | Shared mutable class state changed through an instance in production code | Initialize it per instance or mark intentional shared state as `ClassVar`. |
|
|
107
|
+
|
|
108
|
+
Rule selection accepts exact codes or prefixes:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
pyclichecker . --select SLP001,SLP002
|
|
112
|
+
pyclichecker . --ignore SLP004,SLP008
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Thresholds for function size, comment clusters, and duplicate bodies are
|
|
116
|
+
exposed as command-line options. Run `pyclichecker --help` for their names and
|
|
117
|
+
defaults.
|
|
118
|
+
|
|
119
|
+
For a first pass, fix `error` findings before reviewing `warning` findings.
|
|
120
|
+
Warnings are prompts for engineering judgment, not proof that the code is
|
|
121
|
+
wrong.
|
|
122
|
+
|
|
123
|
+
## Suppressions
|
|
124
|
+
|
|
125
|
+
Inline suppression requires an explicit pyclichecker rule code:
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
def intentional_stub(): # noqa: SLP001
|
|
129
|
+
pass
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def another_stub(): # slop: ignore [SLP001]
|
|
133
|
+
pass
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Bare `# noqa` and unrelated codes such as `# noqa: F401` do not suppress
|
|
137
|
+
pyclichecker. Directive-like text inside a string is also ignored.
|
|
138
|
+
|
|
139
|
+
To suppress an entire file, place this real comment within its first five
|
|
140
|
+
lines:
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
# slop: ignore-file
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Exit codes
|
|
147
|
+
|
|
148
|
+
- `0`: no finding met `--fail-on`, and the run had no operational error.
|
|
149
|
+
- `1`: at least one finding met the configured failure severity.
|
|
150
|
+
- `2`: the scan could not run completely, including missing paths, unreadable
|
|
151
|
+
files, unsupported inputs, or no discovered Python files.
|
|
152
|
+
|
|
153
|
+
`--fail-on warning` is the default. `--fail-on error` reports warnings without
|
|
154
|
+
failing, and `--fail-on never` reports all findings without failing.
|
|
155
|
+
|
|
156
|
+
## Agent use
|
|
157
|
+
|
|
158
|
+
The repository includes a reusable
|
|
159
|
+
[`pyclichecker` Agent Skill](skills/pyclichecker/SKILL.md). Skill-aware agents
|
|
160
|
+
can load that folder and run the linter through `uvx` without permanently
|
|
161
|
+
installing the package.
|
|
162
|
+
|
|
163
|
+
Agents should use the pinned release and JSON output for stable results:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
uvx pyclichecker@2.4.1 changed_file.py --format json
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
JSON output contains the package version, number of files checked, findings,
|
|
170
|
+
and operational errors. Each finding includes path, line, column, code,
|
|
171
|
+
severity, and message.
|
|
172
|
+
|
|
173
|
+
Treat exit `1` as work to review and exit `2` as a broken or incomplete scan.
|
|
174
|
+
Fix findings before adding suppressions, and keep every suppression scoped to
|
|
175
|
+
one explicit rule.
|
|
176
|
+
|
|
177
|
+
An agent should finish only after the same command returns `0`, or after it
|
|
178
|
+
records why each remaining finding is intentional. It should never treat exit
|
|
179
|
+
`2` as a clean result.
|
|
180
|
+
|
|
181
|
+
For agents that do not load skills, add this portable contract to the
|
|
182
|
+
project's `AGENTS.md`:
|
|
183
|
+
|
|
184
|
+
```markdown
|
|
185
|
+
## Python quality gate
|
|
186
|
+
|
|
187
|
+
After creating or changing Python code:
|
|
188
|
+
|
|
189
|
+
1. Run pyclichecker on every changed Python file:
|
|
190
|
+
`uvx pyclichecker@2.4.1 changed_file.py --format json`
|
|
191
|
+
2. Treat exit 1 as findings to fix and exit 2 as an incomplete scan.
|
|
192
|
+
3. Fix findings and rerun relevant tests. Do not add broad suppressions.
|
|
193
|
+
4. Run the final repository gate with the same command, replacing
|
|
194
|
+
`changed_file.py` with `.`, and finish only when it exits 0.
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Development
|
|
198
|
+
|
|
199
|
+
The locked development environment contains Ruff and uses the standard-library
|
|
200
|
+
`unittest` runner:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
uv sync --locked
|
|
204
|
+
uv run python -m unittest discover -v
|
|
205
|
+
uv run ruff check .
|
|
206
|
+
uv run ruff format --check .
|
|
207
|
+
uv run pyclichecker src tests
|
|
208
|
+
uv build
|
|
209
|
+
uvx --from . pyclichecker --version
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for rule and pull-request requirements.
|
|
213
|
+
|
|
214
|
+
The implementation has been exercised on macOS with CPython 3.14. The GitHub
|
|
215
|
+
Actions workflow is configured to run the complete validation suite on Linux.
|
|
216
|
+
|
|
217
|
+
## License
|
|
218
|
+
|
|
219
|
+
Released under the [MIT License](LICENSE).
|