Skip to content
Back to skills

94 Python Project Review

ASecurity

Python: uv envs, packaging/PyPI, style, anti-patterns, asyncio, jobs, config, errors, resilience, FastAPI.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 23, 2026
ai-agentsjavascriptpythonrustgojavashellbashsqlnodefastapi

Works with

  • cli
  • api

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 20 files and shows the line behind each finding

Scanned September 23, 2026

npx -y skills add FluxonLab/Skillry --skill 94-python-project-review --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of 94 Python Project Review?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for 94 Python Project Review
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/fluxonlab-94-python-project-review/badge)](https://www.skillsdirectory.com/skills/fluxonlab-94-python-project-review)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: python-project-review
description: "Python: uv envs, packaging/PyPI, style, anti-patterns, asyncio, jobs, config, errors, resilience, FastAPI."
---

# Python Project Review

## Purpose

Conduct a structured review of a Python project — covering virtual environment and dependency management (venv/uv/poetry), requirements pinning and lockfile hygiene, test structure (pytest fixtures, coverage targets), static analysis (ruff, mypy), common security anti-patterns, and packaging/entry-point correctness. Applies directly to Python-based projects such as ExampleApp and any adjacent Python tooling in the monorepo. Surface concrete findings from actual files, never from assumptions.

## Merged skills

Skillry 3.0.0 merged the skills below into this one. A request that names a former skill routes here; open only the reference that matches the task. Paths are relative to this skill's directory.

| Former skill | Reference | Covers |
|---|---|---|
| `python-anti-patterns` | [references/python-anti-patterns.md](references/python-anti-patterns.md) | Anti-pattern checklist for Python code review: infrastructure, architecture, errors, resources, typing, tests |
| `python-code-style` | [references/python-code-style.md](references/python-code-style.md) | Ruff and formatter configuration, naming, imports, docstrings, documentation standards |
| `python-design-patterns` | [references/python-design-patterns.md](references/python-design-patterns.md) | KISS, separation of concerns, single responsibility, composition over inheritance, when to abstract |
| `python-resource-management` | [references/python-resource-management.md](references/python-resource-management.md) | Context managers, cleanup, connection and file lifecycles, streaming with accumulated state |
| `python-packaging` | [references/python-packaging.md](references/python-packaging.md) | pyproject.toml, src layout, CLI entry points, building and publishing to PyPI |
| `python-pypi-package-builder` | [references/python-pypi-package-builder.md](references/python-pypi-package-builder.md) | End-to-end PyPI library: build backend choice, versioning, typing, CI, Trusted Publishing; scaffold script `scripts/python-pypi-package-builder/scaffold.py` |
| `uv-package-manager` | [references/uv-package-manager.md](references/uv-package-manager.md) | uv projects, virtual environments, lockfiles, Python versions, Docker and CI workflows |
| `async-python-patterns` | [references/async-python-patterns.md](references/async-python-patterns.md) | asyncio: tasks, gather, semaphores, timeouts, async context managers, event-loop pitfalls |
| `python-background-jobs` | [references/python-background-jobs.md](references/python-background-jobs.md) | Task queues and workers (Celery and alternatives): retries, idempotency, job state, chains and chords |
| `python-error-handling` | [references/python-error-handling.md](references/python-error-handling.md) | Input validation, exception hierarchies, partial-failure handling in batches |
| `python-resilience` | [references/python-resilience.md](references/python-resilience.md) | Retries with exponential backoff, timeouts, fault-tolerant decorators |
| `python-observability` | [references/python-observability.md](references/python-observability.md) | Structured logging, correlation IDs, metrics and OpenTelemetry tracing in Python |
| `python-configuration` | [references/python-configuration.md](references/python-configuration.md) | pydantic-settings, environment variables, secrets, per-environment configuration |
| `fastapi-templates` | [references/fastapi-templates.md](references/fastapi-templates.md) | FastAPI project scaffold: layered structure, repositories, services, auth, tests |
| `fastapi` | [references/fastapi.md](references/fastapi.md) | Official FastAPI conventions: fastapi CLI, Annotated parameters and dependencies, return types, routers, streaming and SSE, serving frontends, related tooling |

## When to use

- A PR adds or modifies Python source files, `pyproject.toml`, `requirements*.txt`, or `uv.lock` / `poetry.lock`.
- A security audit is requested for a Python CLI, scraper, or automation tool.
- `pip-audit` has not been run recently and CVE exposure is unknown.
- Tests are failing, coverage is dropping, or new code has no tests.
- You are onboarding into a Python project and need to establish its health baseline.
- Ruff or mypy is not integrated in CI and linting drift has accumulated.

## When not to use

- The project is a Jupyter notebook exploration with no production intent — use a lighter notebook review.
- The PR is documentation-only (`.md`, `.rst`) with no code changes.
- The Python file is a one-line build helper inside a JavaScript monorepo — a full review is disproportionate.

## Procedure

### 1. Orient to the project layout and tooling

```bash
# Identify project root and tooling
ls pyproject.toml setup.py setup.cfg requirements*.txt uv.lock poetry.lock Pipfile.lock 2>/dev/null

# Show Python version constraint and build backend
python3 - <<'EOF'
import tomllib
from pathlib import Path
try:
 data = tomllib.loads(Path("pyproject.toml").read_text())
 proj = data.get("project", data.get("tool", {}).get("poetry", {}))
 print("name:", proj.get("name"))
 print("python:", proj.get("requires-python"))
 build = data.get("build-system", {}).get("build-backend", "not set")
 print("build-backend:", build)
except Exception as e:
 print("pyproject.toml error:", e)
EOF

# Check active venv / uv / poetry
which python3 && python3 --version
which uv && uv --version 2>/dev/null || echo "uv not found"
which poetry && poetry --version 2>/dev/null || echo "poetry not found"
```

### 2. Audit dependency pinning and lockfile hygiene

```bash
# Check requirements files for unpinned dependencies
grep -n "^[a-zA-Z]" requirements*.txt 2>/dev/null | grep -v "==" | head -30

# List direct dependencies from pyproject.toml
python3 -c "
import tomllib; from pathlib import Path
d = tomllib.loads(Path('pyproject.toml').read_text())
deps = d.get('project',{}).get('dependencies', d.get('tool',{}).get('poetry',{}).get('dependencies',{}))
print(deps)
" 2>/dev/null

# Confirm lockfile exists and is not gitignored
git ls-files uv.lock poetry.lock requirements.txt requirements-lock.txt 2>/dev/null || echo "WARNING: lockfile may not be tracked"

# Check for packages pinned with >= only (no upper bound — unpredictable upgrades)
grep -n ">=[0-9]\|~=" requirements*.txt pyproject.toml 2>/dev/null | head -20
```

### 3. Run dependency CVE audit

```bash
# pip-audit against current environment
pip-audit --desc --fix-dry-run 2>/dev/null || \
 pip install pip-audit -q && pip-audit --desc 2>/dev/null

# uv audit (if using uv)
uv pip audit 2>/dev/null

# Safety (alternative)
safety check --full-report 2>/dev/null
```

Triage each CVE by severity (Critical/High/Medium/Low). Flag any Critical or High CVEs affecting direct dependencies for immediate patching.

### 4. Run ruff linting and auto-fix check

```bash
# Show ruff config
grep -A 20 "\[tool\.ruff\]" pyproject.toml 2>/dev/null || cat .ruff.toml 2>/dev/null

# Run ruff — count violations by category
ruff check . --statistics 2>/dev/null | head -30

# Run ruff with auto-fix (dry-run) to see what can be auto-fixed
ruff check . --diff 2>/dev/null | head -60

# Check if ruff is in dev dependencies
grep "ruff" pyproject.toml requirements*.txt 2>/dev/null
```

Key rule categories to enable if missing: `E`, `W` (pycodestyle), `F` (pyflakes), `I` (isort), `S` (bandit security), `B` (bugbear), `UP` (pyupgrade).

### 5. Run mypy type checking

```bash
# Show mypy config
grep -A 20 "\[tool\.mypy\]" pyproject.toml 2>/dev/null || cat mypy.ini .mypy.ini setup.cfg 2>/dev/null | grep -A 20 "\[mypy\]"

# Run mypy
mypy . --ignore-missing-imports 2>/dev/null | tail -20

# Count Any usage (too many → type coverage is low)
grep -rn ": Any\|-> Any\|cast(Any" src/ --include="*.py" | wc -l

# Check strict mode is enabled
grep -n "strict\|disallow_untyped" pyproject.toml mypy.ini 2>/dev/null
```

### 6. Audit security anti-patterns

```bash
# subprocess shell=True (command injection risk)
grep -rn "subprocess\.\(run\|call\|Popen\|check_output\).*shell=True" \
 . --include="*.py" | grep -v "test_\|#.*shell=True"

# eval / exec with non-literal arguments
grep -rn "\beval(\|\bexec(" . --include="*.py" | grep -v "^.*#\|test_\|evaluat"

# pickle load/loads (arbitrary code execution on untrusted data)
grep -rn "pickle\.load\|pickle\.loads\|cPickle\.load" . --include="*.py"

# SQL string concatenation (SQLi risk)
grep -rn 'execute.*%\s*\|execute.*\.format(\|execute.*f".*{' . --include="*.py" | head -20

# requests without timeout (hangs / DoS)
grep -rn "requests\.\(get\|post\|put\|delete\|patch\)(" . --include="*.py" \
 | grep -v "timeout=" | head -20

# requests with verify=False (TLS disabled)
grep -rn "verify=False" . --include="*.py"

# yaml.load without Loader (arbitrary code execution)
grep -rn "yaml\.load(" . --include="*.py" | grep -v "Loader="

# tempfile.mktemp (race condition — use mkstemp/NamedTemporaryFile)
grep -rn "tempfile\.mktemp(" . --include="*.py"

# Hardcoded credentials in source
grep -rn "password\s*=\s*['\"][^${\.\(]" . --include="*.py" | grep -v "test_\|example\|#" | head -20
```

### 7. Review pytest structure and coverage

```bash
# Test discovery
find . -type f -name "test_*.py" -o -name "*_test.py" | grep -v __pycache__ | head -30

# pytest config
grep -A 15 "\[tool\.pytest\|pytest\.ini_options\]" pyproject.toml 2>/dev/null \
 || cat pytest.ini setup.cfg 2>/dev/null | grep -A 15 "\[pytest\]"

# Run tests with coverage
pytest --tb=short -q 2>/dev/null | tail -20
pytest --cov=. --cov-report=term-missing --tb=no -q 2>/dev/null | tail -30

# Check for missing test files for key modules
for f in $(find src/ . -maxdepth 2 -name "*.py" | grep -v test | grep -v __); do
 base=$(basename "$f" .py)
 find . -name "test_${base}.py" -o -name "${base}_test.py" 2>/dev/null | grep -q . \
 || echo "NO TEST: $f"
done
```

### 8. Check packaging and entry points

```bash
# Confirm __main__.py or entry_points are defined
find . -name "__main__.py" | grep -v node_modules | head -10

python3 - <<'EOF'
import tomllib; from pathlib import Path
try:
 d = tomllib.loads(Path("pyproject.toml").read_text())
 scripts = d.get("project", {}).get("scripts", d.get("tool", {}).get("poetry", {}).get("scripts", {}))
 print("entry points:", scripts or "NONE DEFINED")
except: pass
EOF

# Check import structure (relative vs absolute)
grep -rn "^from \.\|^import \." . --include="*.py" | head -20

# Check for circular imports (run import in clean env)
python3 -c "import src" 2>&1 | head -10
```

## Concrete checks

- [ ] `pyproject.toml` exists and defines `requires-python` with a specific version constraint.
- [ ] A lockfile (`uv.lock`, `poetry.lock`, or `requirements.txt` with pinned `==` versions) is committed and up to date.
- [ ] No direct dependency is unpinned (bare package name or `>=` only without upper bound in requirements files).
- [ ] `pip-audit` / `uv pip audit` reports no Critical or High CVEs.
- [ ] `ruff` is in dev dependencies and runs clean (zero errors) in CI.
- [ ] `mypy` is in dev dependencies with `disallow_untyped_defs = true` or `strict = true`.
- [ ] No `subprocess.run(..., shell=True)` with user-controlled input.
- [ ] No `eval(` or `exec(` on non-literal strings.
- [ ] No `pickle.loads` on data from untrusted sources (network, user upload).
- [ ] All `execute()` DB calls use parameterised queries (`?` / `%s` placeholders), not string formatting.
- [ ] Every `requests.*` call has an explicit `timeout=` argument.
- [ ] `requests` calls never use `verify=False` in production paths.
- [ ] `yaml.safe_load` is used instead of `yaml.load` without an explicit Loader.
- [ ] `tempfile.mkstemp` or `NamedTemporaryFile` is used instead of `mktemp`.
- [ ] No hardcoded passwords, API keys, or secrets in `.py` files.
- [ ] pytest runs with a coverage threshold (e.g. `--cov-fail-under=80`) in CI.
- [ ] A `__main__.py` or `[project.scripts]` entry point is defined for CLI tools.

## Commands

```bash
# Full security scan (bandit)
pip install bandit -q && bandit -r . -f txt -ll 2>/dev/null | head -50

# Dependency vulnerability scan
pip install pip-audit -q && pip-audit 2>/dev/null

# Lint + type check in one pass
ruff check . && mypy . --ignore-missing-imports

# Coverage report
pytest --cov=. --cov-report=term-missing -q 2>/dev/null | tail -30

# List all subprocess shell=True usages
grep -rn "shell=True" . --include="*.py" | grep "subprocess"

# List all eval/exec usages
grep -rn "\beval(\|\bexec(" . --include="*.py" | grep -v "^.*#"

# Check uv lockfile is up to date
uv lock --check 2>/dev/null && echo "lockfile up to date" || echo "lockfile OUTDATED"
```

## Required output

Produce a structured report with:
1. **Environment & tooling summary** — Python version, venv/uv/poetry, build backend, lockfile status.
2. **Dependency audit** — unpinned packages table; CVE findings from pip-audit (severity, package, version, fix).
3. **Lint findings** — ruff violation count by category; top 10 violations with file:line.
4. **Type coverage** — mypy error count; any `Any` overuse; strict mode status.
5. **Security findings** — severity-ranked table: `file:line | pattern | risk | concrete fix`. Each of the 8 anti-patterns above confirmed absent or listed.
6. **Test coverage** — overall coverage %; modules below threshold; missing test files for key modules.
7. **Packaging** — entry point status; import structure issues.
8. **Next safe action** — single highest-priority remediation.

## Safety checks

- Never execute untrusted Python files or `eval` found in the codebase as part of the review.
- Do not run `pip install` of unvetted packages to test them.
- Do not modify `pyproject.toml` or `requirements.txt` without explicit user approval.
- Redact any hardcoded credentials found to `****`; flag for rotation and removal.
- Do not commit changes during the review.

## Completion criteria

Done means: dependency pinning and CVE status are documented, ruff and mypy have been run (or their absence is flagged), all 8 security anti-pattern categories have been checked and reported, pytest coverage is measured and compared against the threshold, packaging is verified, and every finding has file:line + severity + concrete fix. The project is safe to ship when CVE audit is clean, ruff/mypy pass, security patterns are absent, and coverage meets the threshold.

Files in this skill

  • SKILL.md14.2 KB
  • references/async-python-patterns.md19.9 KB
  • references/async-python-patterns/LICENSE1 KB
  • references/fastapi-templates.md15.4 KB
  • references/fastapi-templates/LICENSE1 KB
  • references/fastapi.md10.5 KB
  • references/fastapi/LICENSE1.1 KB
  • references/fastapi/references/dependencies.md3.2 KB
  • references/fastapi/references/other-tools.md1.5 KB
  • references/fastapi/references/path-operations.md1.5 KB
  • references/fastapi/references/pydantic.md1.8 KB
  • references/fastapi/references/responses.md1.8 KB
  • references/fastapi/references/streaming.md2.5 KB
  • references/python-anti-patterns.md8.6 KB
  • references/python-anti-patterns/LICENSE1 KB
  • references/python-background-jobs.md10.8 KB
  • references/python-background-jobs/LICENSE1 KB
  • references/python-code-style.md8.4 KB
  • references/python-code-style/LICENSE1 KB
  • references/python-configuration.md10.3 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…