pycodegate 0.1.0__tar.gz → 0.1.2__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.
- {pycodegate-0.1.0 → pycodegate-0.1.2}/AGENTS.md +5 -5
- {pycodegate-0.1.0 → pycodegate-0.1.2}/PKG-INFO +41 -41
- {pycodegate-0.1.0 → pycodegate-0.1.2}/README.md +40 -40
- {pycodegate-0.1.0 → pycodegate-0.1.2}/pyproject.toml +5 -4
- {pycodegate-0.1.0/skills/py-gate → pycodegate-0.1.2/skills/pycodegate}/SKILL.md +3 -3
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/__init__.py +1 -1
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/cli.py +3 -3
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/config.py +5 -5
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/output.py +3 -1
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/dependencies.py +1 -1
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/utils/badge.py +7 -7
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/utils/precommit.py +9 -9
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/test_cli.py +4 -3
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/test_config.py +9 -9
- {pycodegate-0.1.0 → pycodegate-0.1.2}/uv.lock +1 -1
- {pycodegate-0.1.0 → pycodegate-0.1.2}/.github/workflows/ci.yml +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/.github/workflows/publish.yml +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/.gitignore +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/LICENSE +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/docs/superpowers/plans/2026-03-22-python-doctor.md +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/_compat.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/api.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/constants.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/discover.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/profile.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/__init__.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/architecture.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/base.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/complexity.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/correctness.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/dead_code.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/django.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/fastapi.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/flask.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/imports.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/performance.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/security.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/rules/structure.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/scan.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/score.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/types.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/utils/__init__.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/utils/ast_helpers.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/utils/diff.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/utils/file_discovery.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/src/pycodegate/utils/fixer.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/conftest.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/fixtures/basic_python/app.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/fixtures/clean_project/app.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/__init__.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_architecture.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_complexity.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_correctness.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_dead_code.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_dependencies.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_django.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_fastapi.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_flask.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_imports.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_performance.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_security.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/rules/test_structure.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/test_discover.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/test_fixer.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/test_integration.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/test_output.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/test_profile.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/test_scan.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/test_score.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/tests/test_types.py +0 -0
- {pycodegate-0.1.0 → pycodegate-0.1.2}/whitelist.py +0 -0
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
pycodegate is a Python code quality gate that produces a 0–100 health score. It detects your framework, runs parallel lint and dead-code analysis across 40+ rules, and reports findings by severity so you always know what to fix first.
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
uvx
|
|
10
|
+
uvx pycodegate . --json
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## For AI Agents
|
|
14
14
|
|
|
15
|
-
Run `
|
|
15
|
+
Run `pycodegate . --json` after modifying Python files. Fix findings with severity `"error"` first (security, correctness). Re-run to verify the score improved. Target: **80+**.
|
|
16
16
|
|
|
17
17
|
The JSON output includes the score, label, per-diagnostic severity and file location, and detected project context (framework, Python version, package manager).
|
|
18
18
|
|
|
@@ -38,7 +38,7 @@ The JSON output includes the score, label, per-diagnostic severity and file loca
|
|
|
38
38
|
```bash
|
|
39
39
|
uv run pytest -q # run all tests
|
|
40
40
|
uv run ruff check . && uv run ruff format --check . # lint + format check
|
|
41
|
-
uv run
|
|
41
|
+
uv run pycodegate . --verbose # dogfood on ourselves
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
## Adding Rules
|
|
@@ -56,7 +56,7 @@ src/pycodegate/
|
|
|
56
56
|
api.py — Programmatic API (diagnose function)
|
|
57
57
|
scan.py — Orchestrator: parallel lint + dead code
|
|
58
58
|
score.py — Score calculation from diagnostics
|
|
59
|
-
config.py — Config loading (
|
|
59
|
+
config.py — Config loading (pycodegate.toml / pyproject.toml)
|
|
60
60
|
discover.py — Project detection (framework, package manager, etc.)
|
|
61
61
|
output.py — Rich terminal output
|
|
62
62
|
types.py — All data types (Diagnostic, Score, etc.)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pycodegate
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.2
|
|
4
4
|
Summary: One command. One score. Your Python quality gate.
|
|
5
5
|
Project-URL: Homepage, https://github.com/themohitkhare/pycodegate
|
|
6
6
|
Project-URL: Repository, https://github.com/themohitkhare/pycodegate
|
|
@@ -32,14 +32,14 @@ Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
|
32
32
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
34
|
|
|
35
|
-
<h1 align="center">
|
|
35
|
+
<h1 align="center">PyCodeGate</h1>
|
|
36
36
|
<p align="center">
|
|
37
37
|
<strong>One command. One score. Your Python quality gate.</strong>
|
|
38
38
|
</p>
|
|
39
39
|
<p align="center">
|
|
40
40
|
<a href="https://github.com/themohitkhare/pycodegate/actions/workflows/ci.yml"><img src="https://github.com/themohitkhare/pycodegate/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
41
|
-
<a href="https://pypi.org/project/
|
|
42
|
-
<a href="https://pypi.org/project/
|
|
41
|
+
<a href="https://pypi.org/project/pycodegate/"><img src="https://img.shields.io/pypi/v/pycodegate" alt="PyPI"></a>
|
|
42
|
+
<a href="https://pypi.org/project/pycodegate/"><img src="https://img.shields.io/pypi/pyversions/pycodegate" alt="Python"></a>
|
|
43
43
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"></a>
|
|
44
44
|
</p>
|
|
45
45
|
|
|
@@ -47,7 +47,7 @@ Description-Content-Type: text/markdown
|
|
|
47
47
|
|
|
48
48
|
## How it works
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
PyCodeGate starts by detecting your project's context: framework (Django, FastAPI, Flask), Python version, package manager (uv, poetry, pip), and test framework. That context drives which rules are active — Django projects get SQL injection checks, FastAPI projects get async-correctness checks, and so on.
|
|
51
51
|
|
|
52
52
|
It then runs two analysis passes **in parallel**: a lint pass that evaluates 40+ rules across 8 categories (Security, Correctness, Complexity, Architecture, Performance, Structure, Imports, Dead Code), and a dead-code pass powered by [Vulture](https://github.com/jendrikseipp/vulture) that finds unused functions, classes, imports, and variables.
|
|
53
53
|
|
|
@@ -58,39 +58,39 @@ Findings are filtered through your configuration and scored using a **weighted c
|
|
|
58
58
|
Run instantly with `uvx` — no install needed:
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
|
-
uvx
|
|
61
|
+
uvx pycodegate .
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
Install globally with pipx or uv:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
-
pipx install
|
|
67
|
+
pipx install pycodegate
|
|
68
68
|
# or
|
|
69
|
-
uv tool install
|
|
69
|
+
uv tool install pycodegate
|
|
70
70
|
# or
|
|
71
|
-
pip install
|
|
71
|
+
pip install pycodegate
|
|
72
72
|
```
|
|
73
73
|
|
|
74
74
|
## Quick Start
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
77
|
# Basic scan — score + summary
|
|
78
|
-
|
|
78
|
+
pycodegate .
|
|
79
79
|
|
|
80
80
|
# Show file paths and line numbers for every finding
|
|
81
|
-
|
|
81
|
+
pycodegate . --verbose
|
|
82
82
|
|
|
83
83
|
# Machine-readable output for AI agents and CI
|
|
84
|
-
|
|
84
|
+
pycodegate . --json
|
|
85
85
|
|
|
86
86
|
# Auto-fix ruff-fixable issues, then scan
|
|
87
|
-
|
|
87
|
+
pycodegate . --fix
|
|
88
88
|
|
|
89
89
|
# Output only the numeric score (useful in scripts)
|
|
90
|
-
|
|
90
|
+
pycodegate . --score
|
|
91
91
|
|
|
92
92
|
# Scan only files changed vs a base branch
|
|
93
|
-
|
|
93
|
+
pycodegate . --diff main
|
|
94
94
|
```
|
|
95
95
|
|
|
96
96
|
## JSON Output
|
|
@@ -98,7 +98,7 @@ py-gate . --diff main
|
|
|
98
98
|
Pass `--json` to get structured output that AI agents and CI pipelines can parse:
|
|
99
99
|
|
|
100
100
|
```bash
|
|
101
|
-
|
|
101
|
+
pycodegate . --json
|
|
102
102
|
```
|
|
103
103
|
|
|
104
104
|
```json
|
|
@@ -139,7 +139,7 @@ py-gate . --json
|
|
|
139
139
|
|
|
140
140
|
## Agent Integration
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
PyCodeGate is designed to be used by AI coding agents. Add it to your agent's context so it runs after every Python change.
|
|
143
143
|
|
|
144
144
|
### Claude Code
|
|
145
145
|
|
|
@@ -147,18 +147,18 @@ Add the skill to your project:
|
|
|
147
147
|
|
|
148
148
|
```bash
|
|
149
149
|
mkdir -p .claude/skills
|
|
150
|
-
curl -fsSL https://raw.githubusercontent.com/themohitkhare/pycodegate/main/skills/
|
|
151
|
-
-o .claude/skills/
|
|
150
|
+
curl -fsSL https://raw.githubusercontent.com/themohitkhare/pycodegate/main/skills/pycodegate/SKILL.md \
|
|
151
|
+
-o .claude/skills/pycodegate.md
|
|
152
152
|
```
|
|
153
153
|
|
|
154
154
|
Or copy `AGENTS.md` to your project root — Claude Code picks it up automatically.
|
|
155
155
|
|
|
156
156
|
### Cursor
|
|
157
157
|
|
|
158
|
-
Add to `.cursor/rules/
|
|
158
|
+
Add to `.cursor/rules/pycodegate.mdc`:
|
|
159
159
|
|
|
160
160
|
```markdown
|
|
161
|
-
After modifying Python files, run `uvx
|
|
161
|
+
After modifying Python files, run `uvx pycodegate . --json` and fix all findings
|
|
162
162
|
with severity "error" before marking the task complete. Target score: 80+.
|
|
163
163
|
```
|
|
164
164
|
|
|
@@ -167,7 +167,7 @@ with severity "error" before marking the task complete. Target score: 80+.
|
|
|
167
167
|
Add to `.windsurfrules`:
|
|
168
168
|
|
|
169
169
|
```
|
|
170
|
-
After modifying Python files, run: uvx
|
|
170
|
+
After modifying Python files, run: uvx pycodegate . --json
|
|
171
171
|
Fix all "error" severity findings. Re-run to verify the score improved.
|
|
172
172
|
```
|
|
173
173
|
|
|
@@ -176,7 +176,7 @@ Fix all "error" severity findings. Re-run to verify the score improved.
|
|
|
176
176
|
Add to your system prompt:
|
|
177
177
|
|
|
178
178
|
```
|
|
179
|
-
After modifying Python files, run `uvx
|
|
179
|
+
After modifying Python files, run `uvx pycodegate . --json` to check code quality.
|
|
180
180
|
Fix errors first. Target score: 80+.
|
|
181
181
|
```
|
|
182
182
|
|
|
@@ -194,7 +194,7 @@ name: Quality Gate
|
|
|
194
194
|
on: [push, pull_request]
|
|
195
195
|
|
|
196
196
|
jobs:
|
|
197
|
-
|
|
197
|
+
pycodegate:
|
|
198
198
|
runs-on: ubuntu-latest
|
|
199
199
|
steps:
|
|
200
200
|
- uses: actions/checkout@v4
|
|
@@ -205,10 +205,10 @@ jobs:
|
|
|
205
205
|
with:
|
|
206
206
|
python-version: "3.12"
|
|
207
207
|
|
|
208
|
-
- name: Run
|
|
208
|
+
- name: Run PyCodeGate
|
|
209
209
|
run: |
|
|
210
|
-
pip install
|
|
211
|
-
|
|
210
|
+
pip install pycodegate
|
|
211
|
+
pycodegate . --verbose --diff main --fail-on error
|
|
212
212
|
```
|
|
213
213
|
|
|
214
214
|
## CLI Reference
|
|
@@ -244,7 +244,7 @@ Framework-specific rules (Django, FastAPI, Flask) are mapped into the Security o
|
|
|
244
244
|
|
|
245
245
|
## Scoring
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
PyCodeGate uses a **weighted category-budget system**:
|
|
248
248
|
|
|
249
249
|
1. Each category has a weight (see table above). Weights are normalized to sum to 100 points of total deduction budget.
|
|
250
250
|
2. Within a category, findings are sorted by cost (errors cost more than warnings). The top 3 findings apply at full cost; every additional finding applies **diminishing returns** (10% of its cost).
|
|
@@ -260,7 +260,7 @@ Py Gate uses a **weighted category-budget system**:
|
|
|
260
260
|
|
|
261
261
|
## Configuration
|
|
262
262
|
|
|
263
|
-
Create `
|
|
263
|
+
Create `pycodegate.toml` in your project root:
|
|
264
264
|
|
|
265
265
|
```toml
|
|
266
266
|
[options]
|
|
@@ -284,27 +284,27 @@ max-deduction.Dead Code = 0 # disable dead-code penalty entirely
|
|
|
284
284
|
Or use `pyproject.toml`:
|
|
285
285
|
|
|
286
286
|
```toml
|
|
287
|
-
[tool.
|
|
287
|
+
[tool.pycodegate]
|
|
288
288
|
lint = true
|
|
289
289
|
dead_code = true
|
|
290
290
|
fail_on = "error"
|
|
291
291
|
|
|
292
|
-
[tool.
|
|
292
|
+
[tool.pycodegate.ignore]
|
|
293
293
|
rules = ["no-import-in-function"]
|
|
294
294
|
files = ["tests/fixtures/**"]
|
|
295
295
|
|
|
296
|
-
[tool.
|
|
296
|
+
[tool.pycodegate.per-file-ignores]
|
|
297
297
|
"src/legacy/*.py" = ["high-complexity"]
|
|
298
298
|
|
|
299
|
-
[tool.
|
|
299
|
+
[tool.pycodegate.scoring]
|
|
300
300
|
"max-deduction" = { Security = 20, "Dead Code" = 0 }
|
|
301
301
|
```
|
|
302
302
|
|
|
303
|
-
If both files exist, `
|
|
303
|
+
If both files exist, `pycodegate.toml` takes precedence. CLI flags always override config values.
|
|
304
304
|
|
|
305
305
|
## Profiles
|
|
306
306
|
|
|
307
|
-
|
|
307
|
+
PyCodeGate auto-detects a project profile and adjusts rule weights accordingly. You can also set a profile explicitly in config (`profile = "library"`).
|
|
308
308
|
|
|
309
309
|
| Profile | Auto-detected when | Adjustments |
|
|
310
310
|
|---------|--------------------|-------------|
|
|
@@ -315,10 +315,10 @@ Py Gate auto-detects a project profile and adjusts rule weights accordingly. You
|
|
|
315
315
|
|
|
316
316
|
## Pre-commit Hook
|
|
317
317
|
|
|
318
|
-
Use `--pre-commit` to run
|
|
318
|
+
Use `--pre-commit` to run PyCodeGate as a pre-commit hook. It automatically scans only the staged files:
|
|
319
319
|
|
|
320
320
|
```bash
|
|
321
|
-
|
|
321
|
+
pycodegate . --pre-commit --fail-on error
|
|
322
322
|
```
|
|
323
323
|
|
|
324
324
|
Add to `.pre-commit-config.yaml`:
|
|
@@ -327,9 +327,9 @@ Add to `.pre-commit-config.yaml`:
|
|
|
327
327
|
repos:
|
|
328
328
|
- repo: local
|
|
329
329
|
hooks:
|
|
330
|
-
- id:
|
|
331
|
-
name:
|
|
332
|
-
entry:
|
|
330
|
+
- id: pycodegate
|
|
331
|
+
name: PyCodeGate quality check
|
|
332
|
+
entry: pycodegate . --pre-commit --fail-on error
|
|
333
333
|
language: system
|
|
334
334
|
types: [python]
|
|
335
335
|
pass_filenames: false
|
|
@@ -342,7 +342,7 @@ git clone https://github.com/themohitkhare/pycodegate
|
|
|
342
342
|
cd pycodegate
|
|
343
343
|
uv sync --all-extras
|
|
344
344
|
uv run pytest -q
|
|
345
|
-
uv run
|
|
345
|
+
uv run pycodegate . --verbose # dogfood it
|
|
346
346
|
```
|
|
347
347
|
|
|
348
348
|
To add a new rule:
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
<h1 align="center">
|
|
1
|
+
<h1 align="center">PyCodeGate</h1>
|
|
2
2
|
<p align="center">
|
|
3
3
|
<strong>One command. One score. Your Python quality gate.</strong>
|
|
4
4
|
</p>
|
|
5
5
|
<p align="center">
|
|
6
6
|
<a href="https://github.com/themohitkhare/pycodegate/actions/workflows/ci.yml"><img src="https://github.com/themohitkhare/pycodegate/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
7
|
-
<a href="https://pypi.org/project/
|
|
8
|
-
<a href="https://pypi.org/project/
|
|
7
|
+
<a href="https://pypi.org/project/pycodegate/"><img src="https://img.shields.io/pypi/v/pycodegate" alt="PyPI"></a>
|
|
8
|
+
<a href="https://pypi.org/project/pycodegate/"><img src="https://img.shields.io/pypi/pyversions/pycodegate" alt="Python"></a>
|
|
9
9
|
<a href="LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="License: MIT"></a>
|
|
10
10
|
</p>
|
|
11
11
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
## How it works
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
PyCodeGate starts by detecting your project's context: framework (Django, FastAPI, Flask), Python version, package manager (uv, poetry, pip), and test framework. That context drives which rules are active — Django projects get SQL injection checks, FastAPI projects get async-correctness checks, and so on.
|
|
17
17
|
|
|
18
18
|
It then runs two analysis passes **in parallel**: a lint pass that evaluates 40+ rules across 8 categories (Security, Correctness, Complexity, Architecture, Performance, Structure, Imports, Dead Code), and a dead-code pass powered by [Vulture](https://github.com/jendrikseipp/vulture) that finds unused functions, classes, imports, and variables.
|
|
19
19
|
|
|
@@ -24,39 +24,39 @@ Findings are filtered through your configuration and scored using a **weighted c
|
|
|
24
24
|
Run instantly with `uvx` — no install needed:
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
|
-
uvx
|
|
27
|
+
uvx pycodegate .
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Install globally with pipx or uv:
|
|
31
31
|
|
|
32
32
|
```bash
|
|
33
|
-
pipx install
|
|
33
|
+
pipx install pycodegate
|
|
34
34
|
# or
|
|
35
|
-
uv tool install
|
|
35
|
+
uv tool install pycodegate
|
|
36
36
|
# or
|
|
37
|
-
pip install
|
|
37
|
+
pip install pycodegate
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
## Quick Start
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
43
|
# Basic scan — score + summary
|
|
44
|
-
|
|
44
|
+
pycodegate .
|
|
45
45
|
|
|
46
46
|
# Show file paths and line numbers for every finding
|
|
47
|
-
|
|
47
|
+
pycodegate . --verbose
|
|
48
48
|
|
|
49
49
|
# Machine-readable output for AI agents and CI
|
|
50
|
-
|
|
50
|
+
pycodegate . --json
|
|
51
51
|
|
|
52
52
|
# Auto-fix ruff-fixable issues, then scan
|
|
53
|
-
|
|
53
|
+
pycodegate . --fix
|
|
54
54
|
|
|
55
55
|
# Output only the numeric score (useful in scripts)
|
|
56
|
-
|
|
56
|
+
pycodegate . --score
|
|
57
57
|
|
|
58
58
|
# Scan only files changed vs a base branch
|
|
59
|
-
|
|
59
|
+
pycodegate . --diff main
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
## JSON Output
|
|
@@ -64,7 +64,7 @@ py-gate . --diff main
|
|
|
64
64
|
Pass `--json` to get structured output that AI agents and CI pipelines can parse:
|
|
65
65
|
|
|
66
66
|
```bash
|
|
67
|
-
|
|
67
|
+
pycodegate . --json
|
|
68
68
|
```
|
|
69
69
|
|
|
70
70
|
```json
|
|
@@ -105,7 +105,7 @@ py-gate . --json
|
|
|
105
105
|
|
|
106
106
|
## Agent Integration
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
PyCodeGate is designed to be used by AI coding agents. Add it to your agent's context so it runs after every Python change.
|
|
109
109
|
|
|
110
110
|
### Claude Code
|
|
111
111
|
|
|
@@ -113,18 +113,18 @@ Add the skill to your project:
|
|
|
113
113
|
|
|
114
114
|
```bash
|
|
115
115
|
mkdir -p .claude/skills
|
|
116
|
-
curl -fsSL https://raw.githubusercontent.com/themohitkhare/pycodegate/main/skills/
|
|
117
|
-
-o .claude/skills/
|
|
116
|
+
curl -fsSL https://raw.githubusercontent.com/themohitkhare/pycodegate/main/skills/pycodegate/SKILL.md \
|
|
117
|
+
-o .claude/skills/pycodegate.md
|
|
118
118
|
```
|
|
119
119
|
|
|
120
120
|
Or copy `AGENTS.md` to your project root — Claude Code picks it up automatically.
|
|
121
121
|
|
|
122
122
|
### Cursor
|
|
123
123
|
|
|
124
|
-
Add to `.cursor/rules/
|
|
124
|
+
Add to `.cursor/rules/pycodegate.mdc`:
|
|
125
125
|
|
|
126
126
|
```markdown
|
|
127
|
-
After modifying Python files, run `uvx
|
|
127
|
+
After modifying Python files, run `uvx pycodegate . --json` and fix all findings
|
|
128
128
|
with severity "error" before marking the task complete. Target score: 80+.
|
|
129
129
|
```
|
|
130
130
|
|
|
@@ -133,7 +133,7 @@ with severity "error" before marking the task complete. Target score: 80+.
|
|
|
133
133
|
Add to `.windsurfrules`:
|
|
134
134
|
|
|
135
135
|
```
|
|
136
|
-
After modifying Python files, run: uvx
|
|
136
|
+
After modifying Python files, run: uvx pycodegate . --json
|
|
137
137
|
Fix all "error" severity findings. Re-run to verify the score improved.
|
|
138
138
|
```
|
|
139
139
|
|
|
@@ -142,7 +142,7 @@ Fix all "error" severity findings. Re-run to verify the score improved.
|
|
|
142
142
|
Add to your system prompt:
|
|
143
143
|
|
|
144
144
|
```
|
|
145
|
-
After modifying Python files, run `uvx
|
|
145
|
+
After modifying Python files, run `uvx pycodegate . --json` to check code quality.
|
|
146
146
|
Fix errors first. Target score: 80+.
|
|
147
147
|
```
|
|
148
148
|
|
|
@@ -160,7 +160,7 @@ name: Quality Gate
|
|
|
160
160
|
on: [push, pull_request]
|
|
161
161
|
|
|
162
162
|
jobs:
|
|
163
|
-
|
|
163
|
+
pycodegate:
|
|
164
164
|
runs-on: ubuntu-latest
|
|
165
165
|
steps:
|
|
166
166
|
- uses: actions/checkout@v4
|
|
@@ -171,10 +171,10 @@ jobs:
|
|
|
171
171
|
with:
|
|
172
172
|
python-version: "3.12"
|
|
173
173
|
|
|
174
|
-
- name: Run
|
|
174
|
+
- name: Run PyCodeGate
|
|
175
175
|
run: |
|
|
176
|
-
pip install
|
|
177
|
-
|
|
176
|
+
pip install pycodegate
|
|
177
|
+
pycodegate . --verbose --diff main --fail-on error
|
|
178
178
|
```
|
|
179
179
|
|
|
180
180
|
## CLI Reference
|
|
@@ -210,7 +210,7 @@ Framework-specific rules (Django, FastAPI, Flask) are mapped into the Security o
|
|
|
210
210
|
|
|
211
211
|
## Scoring
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
PyCodeGate uses a **weighted category-budget system**:
|
|
214
214
|
|
|
215
215
|
1. Each category has a weight (see table above). Weights are normalized to sum to 100 points of total deduction budget.
|
|
216
216
|
2. Within a category, findings are sorted by cost (errors cost more than warnings). The top 3 findings apply at full cost; every additional finding applies **diminishing returns** (10% of its cost).
|
|
@@ -226,7 +226,7 @@ Py Gate uses a **weighted category-budget system**:
|
|
|
226
226
|
|
|
227
227
|
## Configuration
|
|
228
228
|
|
|
229
|
-
Create `
|
|
229
|
+
Create `pycodegate.toml` in your project root:
|
|
230
230
|
|
|
231
231
|
```toml
|
|
232
232
|
[options]
|
|
@@ -250,27 +250,27 @@ max-deduction.Dead Code = 0 # disable dead-code penalty entirely
|
|
|
250
250
|
Or use `pyproject.toml`:
|
|
251
251
|
|
|
252
252
|
```toml
|
|
253
|
-
[tool.
|
|
253
|
+
[tool.pycodegate]
|
|
254
254
|
lint = true
|
|
255
255
|
dead_code = true
|
|
256
256
|
fail_on = "error"
|
|
257
257
|
|
|
258
|
-
[tool.
|
|
258
|
+
[tool.pycodegate.ignore]
|
|
259
259
|
rules = ["no-import-in-function"]
|
|
260
260
|
files = ["tests/fixtures/**"]
|
|
261
261
|
|
|
262
|
-
[tool.
|
|
262
|
+
[tool.pycodegate.per-file-ignores]
|
|
263
263
|
"src/legacy/*.py" = ["high-complexity"]
|
|
264
264
|
|
|
265
|
-
[tool.
|
|
265
|
+
[tool.pycodegate.scoring]
|
|
266
266
|
"max-deduction" = { Security = 20, "Dead Code" = 0 }
|
|
267
267
|
```
|
|
268
268
|
|
|
269
|
-
If both files exist, `
|
|
269
|
+
If both files exist, `pycodegate.toml` takes precedence. CLI flags always override config values.
|
|
270
270
|
|
|
271
271
|
## Profiles
|
|
272
272
|
|
|
273
|
-
|
|
273
|
+
PyCodeGate auto-detects a project profile and adjusts rule weights accordingly. You can also set a profile explicitly in config (`profile = "library"`).
|
|
274
274
|
|
|
275
275
|
| Profile | Auto-detected when | Adjustments |
|
|
276
276
|
|---------|--------------------|-------------|
|
|
@@ -281,10 +281,10 @@ Py Gate auto-detects a project profile and adjusts rule weights accordingly. You
|
|
|
281
281
|
|
|
282
282
|
## Pre-commit Hook
|
|
283
283
|
|
|
284
|
-
Use `--pre-commit` to run
|
|
284
|
+
Use `--pre-commit` to run PyCodeGate as a pre-commit hook. It automatically scans only the staged files:
|
|
285
285
|
|
|
286
286
|
```bash
|
|
287
|
-
|
|
287
|
+
pycodegate . --pre-commit --fail-on error
|
|
288
288
|
```
|
|
289
289
|
|
|
290
290
|
Add to `.pre-commit-config.yaml`:
|
|
@@ -293,9 +293,9 @@ Add to `.pre-commit-config.yaml`:
|
|
|
293
293
|
repos:
|
|
294
294
|
- repo: local
|
|
295
295
|
hooks:
|
|
296
|
-
- id:
|
|
297
|
-
name:
|
|
298
|
-
entry:
|
|
296
|
+
- id: pycodegate
|
|
297
|
+
name: PyCodeGate quality check
|
|
298
|
+
entry: pycodegate . --pre-commit --fail-on error
|
|
299
299
|
language: system
|
|
300
300
|
types: [python]
|
|
301
301
|
pass_filenames: false
|
|
@@ -308,7 +308,7 @@ git clone https://github.com/themohitkhare/pycodegate
|
|
|
308
308
|
cd pycodegate
|
|
309
309
|
uv sync --all-extras
|
|
310
310
|
uv run pytest -q
|
|
311
|
-
uv run
|
|
311
|
+
uv run pycodegate . --verbose # dogfood it
|
|
312
312
|
```
|
|
313
313
|
|
|
314
314
|
To add a new rule:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pycodegate"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.2"
|
|
4
4
|
description = "One command. One score. Your Python quality gate."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
@@ -41,7 +41,8 @@ dev = [
|
|
|
41
41
|
]
|
|
42
42
|
|
|
43
43
|
[project.scripts]
|
|
44
|
-
|
|
44
|
+
pycodegate = "pycodegate.cli:main"
|
|
45
|
+
|
|
45
46
|
|
|
46
47
|
[build-system]
|
|
47
48
|
requires = ["hatchling"]
|
|
@@ -69,6 +70,6 @@ ignore = ["SIM102"] # nested ifs in AST walking are clearer than combined condi
|
|
|
69
70
|
"src/pycodegate/rules/base.py" = ["TC001"] # runtime import in abstract method
|
|
70
71
|
"src/pycodegate/utils/*.py" = ["E501"]
|
|
71
72
|
|
|
72
|
-
[tool.
|
|
73
|
-
[tool.
|
|
73
|
+
[tool.pycodegate]
|
|
74
|
+
[tool.pycodegate.ignore]
|
|
74
75
|
files = ["tests/fixtures/**"]
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
---
|
|
2
|
-
name:
|
|
2
|
+
name: pycodegate
|
|
3
3
|
description: Run after making Python changes to catch issues early. Use when reviewing code, finishing a feature, or fixing bugs.
|
|
4
4
|
version: 1.0.0
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
-
#
|
|
7
|
+
# PyCodeGate
|
|
8
8
|
|
|
9
9
|
Scans your Python codebase for security, performance, correctness, architecture, and structure issues. Outputs a 0-100 score with actionable diagnostics. Auto-detects Django, FastAPI, and Flask to activate framework-specific rules.
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
13
|
```bash
|
|
14
|
-
uvx
|
|
14
|
+
uvx pycodegate . --verbose --diff
|
|
15
15
|
```
|
|
16
16
|
|
|
17
17
|
## Workflow
|
|
@@ -17,7 +17,7 @@ from pycodegate.utils.precommit import install_precommit_hook
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
@click.command(context_settings={"help_option_names": ["-h", "--help"]})
|
|
20
|
-
@click.version_option(__version__, "-v", "--version", prog_name="
|
|
20
|
+
@click.version_option(__version__, "-v", "--version", prog_name="PyCodeGate")
|
|
21
21
|
@click.argument("directory", default=".", type=click.Path(exists=True))
|
|
22
22
|
@click.option("--lint/--no-lint", default=True, help="Enable/disable lint checks.")
|
|
23
23
|
@click.option(
|
|
@@ -79,7 +79,7 @@ def main(
|
|
|
79
79
|
precommit: bool,
|
|
80
80
|
min_score: int | None,
|
|
81
81
|
) -> None:
|
|
82
|
-
"""
|
|
82
|
+
"""PyCodeGate — Diagnose your Python project's health."""
|
|
83
83
|
if ci_workflow:
|
|
84
84
|
click.echo(generate_ci_workflow(), nl=False)
|
|
85
85
|
return
|
|
@@ -125,7 +125,7 @@ def main(
|
|
|
125
125
|
|
|
126
126
|
if min_score is not None and result.score.value < min_score:
|
|
127
127
|
click.echo(
|
|
128
|
-
f"
|
|
128
|
+
f"pycodegate: score {result.score.value} is below minimum {min_score}",
|
|
129
129
|
err=True,
|
|
130
130
|
)
|
|
131
131
|
sys.exit(1)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Configuration loading for
|
|
1
|
+
"""Configuration loading for pycodegate."""
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
@@ -22,11 +22,11 @@ class Config:
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def load_config(project_path: str) -> Config:
|
|
25
|
-
"""Load config from
|
|
25
|
+
"""Load config from pycodegate.toml or pyproject.toml [tool.pycodegate]."""
|
|
26
26
|
root = Path(project_path)
|
|
27
27
|
|
|
28
|
-
#
|
|
29
|
-
doctor_toml = root / "
|
|
28
|
+
# pycodegate.toml takes precedence
|
|
29
|
+
doctor_toml = root / "pycodegate.toml"
|
|
30
30
|
if doctor_toml.exists():
|
|
31
31
|
return _parse_doctor_toml(doctor_toml)
|
|
32
32
|
|
|
@@ -62,7 +62,7 @@ def _parse_pyproject_toml(path: Path) -> Config:
|
|
|
62
62
|
with open(path, "rb") as f:
|
|
63
63
|
data = tomllib.load(f)
|
|
64
64
|
|
|
65
|
-
section = data.get("tool", {}).get("
|
|
65
|
+
section = data.get("tool", {}).get("pycodegate", {})
|
|
66
66
|
if not section:
|
|
67
67
|
return Config()
|
|
68
68
|
|
|
@@ -138,7 +138,9 @@ def print_scan_result(result: ScanResult, verbose: bool = False) -> None:
|
|
|
138
138
|
# Project info header
|
|
139
139
|
p = result.project
|
|
140
140
|
console.print()
|
|
141
|
-
|
|
141
|
+
from pycodegate import __version__
|
|
142
|
+
|
|
143
|
+
console.print(f" [bold]PyCodeGate[/bold] — v{__version__}")
|
|
142
144
|
console.print()
|
|
143
145
|
console.print(f" [dim]Path:[/dim] {p.path}")
|
|
144
146
|
if p.python_version:
|
|
@@ -81,7 +81,7 @@ class DependencyRules:
|
|
|
81
81
|
# Try uv export
|
|
82
82
|
if shutil.which("uv") and (path / "pyproject.toml").exists():
|
|
83
83
|
try:
|
|
84
|
-
tmp = Path(tempfile.gettempdir()) / "
|
|
84
|
+
tmp = Path(tempfile.gettempdir()) / "pycodegate-reqs.txt"
|
|
85
85
|
result = subprocess.run(
|
|
86
86
|
[
|
|
87
87
|
"uv",
|
|
@@ -17,7 +17,7 @@ def generate_badge(score: int, label: str) -> str:
|
|
|
17
17
|
color = "red"
|
|
18
18
|
|
|
19
19
|
score_text = urllib.parse.quote(f"{score}/100")
|
|
20
|
-
return f""
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
def generate_ci_workflow() -> str:
|
|
@@ -36,16 +36,16 @@ jobs:
|
|
|
36
36
|
steps:
|
|
37
37
|
- uses: actions/checkout@v4
|
|
38
38
|
- uses: astral-sh/setup-uv@v5
|
|
39
|
-
- run: uv tool install
|
|
40
|
-
- name: Run
|
|
39
|
+
- run: uv tool install pycodegate
|
|
40
|
+
- name: Run pycodegate
|
|
41
41
|
run: |
|
|
42
|
-
SCORE=$(
|
|
43
|
-
BADGE=$(
|
|
42
|
+
SCORE=$(pycodegate . --score)
|
|
43
|
+
BADGE=$(pycodegate . --badge)
|
|
44
44
|
# Update badge in README
|
|
45
|
-
sed -i "s|!\\[
|
|
45
|
+
sed -i "s|!\\[pycodegate score\\](.*)|${BADGE}|" README.md
|
|
46
46
|
- name: Commit badge update
|
|
47
47
|
run: |
|
|
48
48
|
git config user.name "github-actions[bot]"
|
|
49
49
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
50
|
-
git diff --quiet README.md || (git add README.md && git commit -m "chore: update
|
|
50
|
+
git diff --quiet README.md || (git add README.md && git commit -m "chore: update pycodegate score badge" && git push)
|
|
51
51
|
"""
|
|
@@ -6,31 +6,31 @@ from pathlib import Path
|
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
def install_precommit_hook(project_path: str, min_score: int = 50) -> str:
|
|
9
|
-
"""Install a git pre-commit hook that runs
|
|
9
|
+
"""Install a git pre-commit hook that runs pycodegate."""
|
|
10
10
|
hooks_dir = Path(project_path) / ".git" / "hooks"
|
|
11
11
|
if not hooks_dir.exists():
|
|
12
12
|
return "Error: not a git repository"
|
|
13
13
|
|
|
14
14
|
hook_path = hooks_dir / "pre-commit"
|
|
15
15
|
hook_content = f"""#!/bin/sh
|
|
16
|
-
#
|
|
17
|
-
SCORE=$(
|
|
16
|
+
# pycodegate pre-commit hook
|
|
17
|
+
SCORE=$(pycodegate . --score 2>/dev/null)
|
|
18
18
|
if [ $? -ne 0 ]; then
|
|
19
|
-
echo "
|
|
19
|
+
echo "pycodegate not found. Install with: uvx pycodegate"
|
|
20
20
|
exit 0
|
|
21
21
|
fi
|
|
22
22
|
if [ "$SCORE" -lt {min_score} ]; then
|
|
23
|
-
echo "
|
|
24
|
-
|
|
23
|
+
echo "pycodegate: score $SCORE is below minimum {min_score}. Fix issues before committing."
|
|
24
|
+
pycodegate . 2>/dev/null
|
|
25
25
|
exit 1
|
|
26
26
|
fi
|
|
27
|
-
echo "
|
|
27
|
+
echo "pycodegate: score $SCORE/{min_score} — passed"
|
|
28
28
|
"""
|
|
29
29
|
|
|
30
30
|
# Don't overwrite existing hook — append or warn
|
|
31
31
|
if hook_path.exists():
|
|
32
32
|
existing = hook_path.read_text()
|
|
33
|
-
if "
|
|
33
|
+
if "pycodegate" in existing:
|
|
34
34
|
hook_path.write_text(hook_content)
|
|
35
35
|
hook_path.chmod(0o755)
|
|
36
36
|
return f"Updated pre-commit hook at {hook_path} (min-score: {min_score})"
|
|
@@ -41,7 +41,7 @@ echo "py-gate: score $SCORE/{min_score} — passed"
|
|
|
41
41
|
body = "".join(lines[1:]) if lines[0].startswith("#!/") else hook_content
|
|
42
42
|
with open(hook_path, "a") as f:
|
|
43
43
|
f.write("\n" + body)
|
|
44
|
-
return f"Appended
|
|
44
|
+
return f"Appended pycodegate to existing pre-commit hook at {hook_path}"
|
|
45
45
|
|
|
46
46
|
hook_path.write_text(hook_content)
|
|
47
47
|
hook_path.chmod(0o755)
|
|
@@ -2,6 +2,7 @@ import json
|
|
|
2
2
|
|
|
3
3
|
from click.testing import CliRunner
|
|
4
4
|
|
|
5
|
+
from pycodegate import __version__
|
|
5
6
|
from pycodegate.cli import main
|
|
6
7
|
|
|
7
8
|
|
|
@@ -9,14 +10,14 @@ def test_cli_help():
|
|
|
9
10
|
runner = CliRunner()
|
|
10
11
|
result = runner.invoke(main, ["--help"])
|
|
11
12
|
assert result.exit_code == 0
|
|
12
|
-
assert "
|
|
13
|
+
assert "PyCodeGate" in result.output or "pycodegate" in result.output
|
|
13
14
|
|
|
14
15
|
|
|
15
16
|
def test_cli_version():
|
|
16
17
|
runner = CliRunner()
|
|
17
18
|
result = runner.invoke(main, ["--version"])
|
|
18
19
|
assert result.exit_code == 0
|
|
19
|
-
assert
|
|
20
|
+
assert __version__ in result.output
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
def test_cli_scan_clean_project(tmp_path):
|
|
@@ -122,4 +123,4 @@ def test_ci_workflow(tmp_path):
|
|
|
122
123
|
result = runner.invoke(main, [str(tmp_path), "--ci"])
|
|
123
124
|
assert result.exit_code == 0
|
|
124
125
|
assert "name: Py Gate Score" in result.output
|
|
125
|
-
assert "
|
|
126
|
+
assert "pycodegate" in result.output
|
|
@@ -22,7 +22,7 @@ dead_code = false
|
|
|
22
22
|
verbose = true
|
|
23
23
|
fail_on = "error"
|
|
24
24
|
"""
|
|
25
|
-
(tmp_path / "
|
|
25
|
+
(tmp_path / "pycodegate.toml").write_text(toml_content)
|
|
26
26
|
cfg = load_config(str(tmp_path))
|
|
27
27
|
assert cfg.dead_code is False
|
|
28
28
|
assert cfg.verbose is True
|
|
@@ -33,13 +33,13 @@ fail_on = "error"
|
|
|
33
33
|
|
|
34
34
|
def test_load_from_pyproject_toml(tmp_path):
|
|
35
35
|
toml_content = """
|
|
36
|
-
[tool.
|
|
36
|
+
[tool.pycodegate]
|
|
37
37
|
lint = true
|
|
38
38
|
dead_code = true
|
|
39
39
|
verbose = false
|
|
40
40
|
fail_on = "none"
|
|
41
41
|
|
|
42
|
-
[tool.
|
|
42
|
+
[tool.pycodegate.ignore]
|
|
43
43
|
rules = ["no-pickle"]
|
|
44
44
|
files = []
|
|
45
45
|
"""
|
|
@@ -50,12 +50,12 @@ files = []
|
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
def test_pycodegate_toml_takes_precedence(tmp_path):
|
|
53
|
-
(tmp_path / "
|
|
53
|
+
(tmp_path / "pycodegate.toml").write_text("""
|
|
54
54
|
[options]
|
|
55
55
|
verbose = true
|
|
56
56
|
""")
|
|
57
57
|
(tmp_path / "pyproject.toml").write_text("""
|
|
58
|
-
[tool.
|
|
58
|
+
[tool.pycodegate]
|
|
59
59
|
verbose = false
|
|
60
60
|
""")
|
|
61
61
|
cfg = load_config(str(tmp_path))
|
|
@@ -69,7 +69,7 @@ lint = true
|
|
|
69
69
|
[per-file-suppress]
|
|
70
70
|
"tests/*" = ["no-bare-except"]
|
|
71
71
|
"""
|
|
72
|
-
(tmp_path / "
|
|
72
|
+
(tmp_path / "pycodegate.toml").write_text(toml_content)
|
|
73
73
|
config = load_config(str(tmp_path))
|
|
74
74
|
assert config.per_file_suppress == {"tests/*": ["no-bare-except"]}
|
|
75
75
|
|
|
@@ -82,16 +82,16 @@ lint = true
|
|
|
82
82
|
security = 15
|
|
83
83
|
structure = 5
|
|
84
84
|
"""
|
|
85
|
-
(tmp_path / "
|
|
85
|
+
(tmp_path / "pycodegate.toml").write_text(toml_content)
|
|
86
86
|
config = load_config(str(tmp_path))
|
|
87
87
|
assert config.max_deduction == {"security": 15, "structure": 5}
|
|
88
88
|
|
|
89
89
|
|
|
90
90
|
def test_per_file_suppress_from_pyproject(tmp_path):
|
|
91
|
-
pyproject = """[tool.
|
|
91
|
+
pyproject = """[tool.pycodegate.per-file-suppress]
|
|
92
92
|
"tests/*" = ["no-bare-except"]
|
|
93
93
|
|
|
94
|
-
[tool.
|
|
94
|
+
[tool.pycodegate.max-deduction]
|
|
95
95
|
security = 10
|
|
96
96
|
"""
|
|
97
97
|
(tmp_path / "pyproject.toml").write_text(pyproject)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|