Skip to main content

Trust, but verify. The quality gate for AI-generated Python code.

Project description

PyCodeGate

Trust, but verify. The quality gate for AI-generated Python code.

CI PyPI Python License: MIT


Why PyCodeGate?

AI coding agents ship code fast — but fast doesn't mean safe. Every eval() an LLM drops in, every mutable default it forgets, every circular import it creates is a landmine waiting to go off. PyCodeGate is the trust layer between AI-generated code and your production codebase: one command, one score, zero ambiguity.

How it works

PyCodeGate detects 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.

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 that finds unused functions, classes, imports, and variables.

Findings are filtered through your configuration and scored using a weighted category-budget system. Each category has a maximum deduction budget proportional to its weight. Within a category the top 3 findings apply at full cost; additional findings apply diminishing returns (10% each), so fixing the worst issues always moves the needle. The final result is a 0–100 health score with a label: Excellent (90+), Great (75–89), Needs work (50–74), or Critical (<50).

Install

Run instantly with uvx — no install needed:

uvx pycodegate .

Install globally with pipx or uv:

pipx install pycodegate
# or
uv tool install pycodegate
# or
pip install pycodegate

Quick Start

# Basic scan — score + summary
pycodegate .

# Show file paths and line numbers for every finding
pycodegate . --verbose

# Machine-readable output for AI agents and CI
pycodegate . --json

# Auto-fix ruff-fixable issues, then scan
pycodegate . --fix

# Output only the numeric score (useful in scripts)
pycodegate . --score

# Scan only files changed vs a base branch
pycodegate . --diff main

JSON Output

Pass --json to get structured output that AI agents and CI pipelines can parse:

pycodegate . --json
{
  "version": "0.1.0",
  "path": ".",
  "score": 87,
  "label": "Great",
  "errors": 1,
  "warnings": 4,
  "elapsed_ms": 212,
  "project": {
    "framework": "fastapi",
    "python_version": "3.12",
    "package_manager": "uv",
    "test_framework": "pytest"
  },
  "diagnostics": [
    {
      "rule": "no-mutable-default",
      "severity": "error",
      "category": "Correctness",
      "message": "Mutable default argument `[]` is shared across all calls",
      "file_path": "src/api/routes.py",
      "line": 34
    },
    {
      "rule": "high-complexity",
      "severity": "warning",
      "category": "Complexity",
      "message": "Function 'process_order' has cyclomatic complexity 17 (max 15)",
      "file_path": "src/api/orders.py",
      "line": 88
    }
  ]
}

Agent Integration

PyCodeGate is designed to be used by AI coding agents. Add it to your agent's context so it runs after every Python change.

Claude Code

Add the skill to your project:

mkdir -p .claude/skills
curl -fsSL https://raw.githubusercontent.com/themohitkhare/pycodegate/main/skills/pycodegate/SKILL.md \
  -o .claude/skills/pycodegate.md

Or copy AGENTS.md to your project root — Claude Code picks it up automatically.

Cursor

Add to .cursor/rules/pycodegate.mdc:

After modifying Python files, run `uvx pycodegate . --json` and fix all findings
with severity "error" before marking the task complete. Target score: 80+.

Windsurf

Add to .windsurfrules:

After modifying Python files, run: uvx pycodegate . --json
Fix all "error" severity findings. Re-run to verify the score improved.

Codex

Add to your system prompt:

After modifying Python files, run `uvx pycodegate . --json` to check code quality.
Fix errors first. Target score: 80+.

Aider

aider --read AGENTS.md

GitHub Actions

name: Quality Gate

on: [push, pull_request]

jobs:
  pycodegate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # required for --diff

      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"

      - name: Run PyCodeGate
        run: |
          pip install pycodegate
          pycodegate . --verbose --diff main --fail-on error

CLI Reference

Flag Default Description
[DIRECTORY] . Path to the Python project to scan
--lint / --no-lint on Enable or disable lint checks
--dead-code / --no-dead-code on Enable or disable dead code detection
--verbose off Show file path and line number per finding
--score off Print only the numeric score and exit
--json off Emit structured JSON (for agents and CI)
--fix off Run ruff --fix before scanning
--diff TEXT Scan only files changed vs this base branch
--fail-on [error|warning|none] none Exit code 1 when findings at this level exist
-v, --version Show version and exit
-h, --help Show help and exit

What It Checks

Category Weight Max Deduction What it catches
Security 5 ~24 pts eval, exec, pickle.load, unsafe YAML, hardcoded secrets, weak hashes
Correctness 4 ~19 pts Mutable defaults, bare/broad except, assert in production, bad __init__ return
Complexity 3 ~14 pts Cyclomatic complexity > 15 (warning) or > 25 (error)
Architecture 3 ~14 pts Giant modules (>500 lines), deep nesting (>5), god functions (>50 lines), too many args (>7)
Performance 2 ~10 pts String concat in loops, imports inside functions, star imports
Structure 2 ~10 pts Missing __init__.py, missing tests directory, no type hints
Imports 1 ~5 pts Circular imports, wildcard imports, import order issues
Dead Code 1 ~5 pts Unused functions, classes, variables, and imports via Vulture

Framework-specific rules (Django, FastAPI, Flask) are mapped into the Security or Correctness budget.

Scoring

PyCodeGate uses a weighted category-budget system:

  1. Each category has a weight (see table above). Weights are normalized to sum to 100 points of total deduction budget.
  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).
  3. A category's deduction is capped at its budget, so a single broken category can never zero out an otherwise healthy project.
  4. Final score = 100 - sum(capped category deductions), floored at 0.
Score Label Meaning
90–100 Excellent Production-ready
75–89 Great Minor issues to address
50–74 Needs work Significant issues present
0–49 Critical Blocking issues, do not ship

Configuration

Create pycodegate.toml in your project root:

[options]
lint = true
dead_code = true
verbose = false
fail_on = "none"

[ignore]
rules = ["dead-code", "no-import-in-function"]
files = ["tests/fixtures/**", "migrations/**", "scripts/**"]

[per-file-ignores]
"src/legacy/*.py" = ["high-complexity", "no-god-function"]

[scoring]
max-deduction.Security = 20
max-deduction.Dead Code = 0  # disable dead-code penalty entirely

Or use pyproject.toml:

[tool.pycodegate]
lint = true
dead_code = true
fail_on = "error"

[tool.pycodegate.ignore]
rules = ["no-import-in-function"]
files = ["tests/fixtures/**"]

[tool.pycodegate.per-file-ignores]
"src/legacy/*.py" = ["high-complexity"]

[tool.pycodegate.scoring]
"max-deduction" = { Security = 20, "Dead Code" = 0 }

If both files exist, pycodegate.toml takes precedence. CLI flags always override config values.

Profiles

PyCodeGate auto-detects a project profile and adjusts rule weights accordingly. You can also set a profile explicitly in config (profile = "library").

Profile Auto-detected when Adjustments
cli [project.scripts] in pyproject.toml Architecture rules weighted up; dead-code weighted down
web Django / FastAPI / Flask detected Security and correctness weighted up; framework rules active
library No scripts, no framework, has py.typed Public API checks active; dead-code weighted up
script Single-file project or scripts/ directory Architecture rules relaxed; complexity thresholds raised

Pre-commit Hook

Use --pre-commit to run PyCodeGate as a pre-commit hook. It automatically scans only the staged files:

pycodegate . --pre-commit --fail-on error

Add to .pre-commit-config.yaml:

repos:
  - repo: local
    hooks:
      - id: pycodegate
        name: PyCodeGate quality check
        entry: pycodegate . --pre-commit --fail-on error
        language: system
        types: [python]
        pass_filenames: false

Contributing

git clone https://github.com/themohitkhare/pycodegate
cd pycodegate
uv sync --all-extras
uv run pytest -q
uv run pycodegate . --verbose  # dogfood it

To add a new rule:

  1. Create a file in src/pycodegate/rules/ extending BaseRules
  2. Implement check(self, source: str, filename: str) -> list[Diagnostic]
  3. Register it in src/pycodegate/rules/__init__.py
  4. Add tests in tests/rules/

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pycodegate-0.3.0.tar.gz (98.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pycodegate-0.3.0-py3-none-any.whl (59.5 kB view details)

Uploaded Python 3

File details

Details for the file pycodegate-0.3.0.tar.gz.

File metadata

  • Download URL: pycodegate-0.3.0.tar.gz
  • Upload date:
  • Size: 98.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pycodegate-0.3.0.tar.gz
Algorithm Hash digest
SHA256 5e05ba013d86362f1833778229bdd74a073ad0f61860e50e33fad4e4f8354e56
MD5 fb966e0a46a4541f74ee5fa6d042fe42
BLAKE2b-256 ecdea84585343f9e6bc1b5a9919a118ca790c554c3600f163b989ff8eae9e153

See more details on using hashes here.

Provenance

The following attestation bundles were made for pycodegate-0.3.0.tar.gz:

Publisher: publish.yml on themohitkhare/pycodegate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pycodegate-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: pycodegate-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 59.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pycodegate-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2ad9916cd3fbbbd8d0f0b520537322de04d51bc5d4e476fee28ab451ff22da7f
MD5 97bb98011decb09e51fe107309ac84a5
BLAKE2b-256 1ddff08e68efe07f159ea0e9362d94e5a022b1977fcd003ff20d9f1ef51b9071

See more details on using hashes here.

Provenance

The following attestation bundles were made for pycodegate-0.3.0-py3-none-any.whl:

Publisher: publish.yml on themohitkhare/pycodegate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page