A Python project setup doctor that diagnoses broken imports, env files, pyproject config, and pytest setup issues.
Project description
Suture
Suture is a Python project setup doctor.
It diagnoses the boring setup mistakes that stop Python projects from running:
broken imports, unsafe .env handling, bad pyproject.toml entries, missing
pytest path config, and broken CLI script entry points.
Suture is not a linter, formatter, type checker, or security scanner. It
focuses on setup failures — the problems that usually show up as
ModuleNotFoundError, broken test discovery, missing environment variables, or
confusing packaging errors.
Why Suture exists
Python projects often fail for reasons that are not code-quality problems:
- pytest cannot find a
src/package .envexists but is not ignored by git- code uses env vars that are not documented in
.env.example pyproject.tomldefines a CLI command that points to missing code- both
requirements.txtandpyproject.tomlexist with unclear ownership
Suture checks those setup-level problems and explains them with severity and confidence levels.
Project status
Suture is currently alpha software. Issue codes and output format may change before 1.0.
Current focus:
- Python setup diagnostics
- Safe, explainable issue reports
- JSON output for AI assistants and CI tools
- Conservative auto-fixes only
Not yet supported:
- Full framework-specific checks
- Dependency vulnerability scanning
- Circular import graph analysis
- Docker / CI runtime debugging
- Source-code rewriting
Quick demo
uv run suture doctor examples/broken-projects/src-layout-no-pytest-path
Actual output:
╭──────────────────────────────╮
│ Suture Project Health Report │
╰──────────────────────────────╯
Score: 85/100
Possible Issues:
! IMP001 src layout may not be configured for pytest
This project uses src/ layout, but [tool.pytest.ini_options] pythonpath
does not include "src".
If tests fail with ModuleNotFoundError, add pythonpath = ["src"] under
[tool.pytest.ini_options] in pyproject.toml, or install the package
using pip install -e .
Passed:
✓ pyproject.toml found
✓ [project] name and version present
✓ [tool.pytest.ini_options] present
✓ src layout detected
Not checked:
- Docker config
- CI environment
- runtime errors
- async test setup
- circular imports
- dynamic imports
- framework-specific settings
Try locally
git clone <your-repo-url>
cd suture
uv sync --extra dev
uv run suture doctor
uv run suture doctor examples/broken-projects/missing-env-gitignore
Install
Once published:
pipx install suture
suture doctor
Suture is not yet published to PyPI. Use the local setup above.
Commands
All commands accept an optional PATH argument. When omitted, the current
working directory is used.
doctor, imports, and pyproject require a project directory. env also
accepts a single .py file.
Full health report
suture doctor # scan current directory
suture doctor /path/to/project # scan a specific project
Environment variables
suture env # scan current directory
suture env /path/to/project # scan a specific project
suture env path/to/file.py # scan a single Python file
Scans Python files for os.getenv, os.environ["X"], and
os.environ.get("X"). Compares against .env, .env.example, and
.gitignore.
Import layout
suture imports # scan current directory
suture imports /path/to/project # scan a specific project
Detects src/ vs flat layout, checks pytest pythonpath config, looks for
missing __init__.py files.
pyproject.toml
suture pyproject # scan current directory
suture pyproject /path/to/project # scan a specific project
Parses and validates pyproject.toml: metadata, pytest config, script entry
points, dual-file conflicts.
Apply fixes
suture apply --dry-run # show what would change, touch nothing
suture apply --safe # apply only safe auto-fixes
suture apply --interactive # ask before each fix
JSON output
Suture can output clean JSON for AI assistants, CI tools, and editor integrations:
suture doctor --json
suture doctor /path/to/project --json
Example:
{
"project_root": "/path/to/project",
"layout": "src",
"python_version": "3.12.0",
"package_manager": "uv",
"score": 72,
"issues": [
{
"code": "IMP001",
"title": "src layout may not be configured for pytest",
"severity": "high",
"confidence": "medium",
"reason": "This project uses src/ layout, but pythonpath does not include \"src\".",
"suggestion": "Add pythonpath = [\"src\"] under [tool.pytest.ini_options].",
"fix_id": "add-pytest-pythonpath"
}
],
"passed": ["pyproject.toml found"],
"skipped": [],
"not_checked": [
"Docker config",
"CI environment",
"runtime errors"
]
}
Paste the output directly into an AI assistant conversation to get targeted
advice. The not_checked field tells the assistant what Suture cannot see.
suture doctor --json | pbcopy # macOS
suture doctor --json | xclip # Linux
What Suture checks
| Area | Checks |
|---|---|
| pyproject.toml | File exists, valid TOML, [project] has name and version, pytest config present, script entry points resolve on disk, no dual requirements.txt conflict |
| Import layout | src/ vs flat layout detected, pytest pythonpath configured, __init__.py present, relative imports inside package context |
| Environment variables | .env in .gitignore, .env.example exists, all statically-referenced env vars documented, .env vars not silently undocumented |
What Suture does NOT check
- Docker config
- CI environment
- Runtime errors
- Async test setup
- Circular imports
- Dynamic imports (
importlib,__import__) - Framework-specific settings (Django
INSTALLED_APPS, FastAPI lifespan, etc.) - Type correctness
- Code style or formatting
Suture always shows a "Not checked" section so you know its limits.
Safety model
Suture is intentionally conservative.
It separates:
- Confirmed issues — high-confidence findings
- Possible issues — lower-confidence findings worth reviewing
- Passed checks — things that look correct
- Skipped / not checked — things Suture cannot assess
Each issue includes severity, confidence, reason, and suggestion. Suture will never automatically delete files, remove dependencies, rewrite imports, move packages, or modify source code.
The only safe auto-fixes (suture apply --safe) are additive and reversible:
- Adding
.envto.gitignore - Creating or updating
.env.example - Adding
pythonpath = ["src"]topyproject.toml
Everything else requires --interactive mode, which asks before each change.
Confidence levels
Every issue has a confidence level: high, medium, or low.
Suture uses these because static analysis cannot always be certain:
- A
src/layout withoutpythonpath = ["src"]in pytest config might cause test failures — but the package could also be installed withpip install -e .. Suture flags it asHIGHseverity,MEDIUMconfidence. - A variable in
.envthat Suture couldn't find in Python code might be unused — or used dynamically or by a framework.LOWseverity,MEDIUMconfidence.
Confirmed issues (high confidence) appear under Confirmed Issues. Lower- confidence findings appear under Possible Issues. Suture never mixes them.
Score
Suture computes a 0–100 score based on issues found:
| Severity | Deduction |
|---|---|
| CRITICAL | −25 |
| HIGH | −15 |
| MEDIUM | −8 |
| LOW | −3 |
| INFO | 0 |
Score is displayed in green (≥80), yellow (≥50), or red (<50). A score below 50 exits with code 1, which is useful in CI.
Development validation examples
Suture includes intentionally broken sample projects under examples/broken-projects/.
| Project | Expected issue |
|---|---|
missing-env-gitignore |
ENV001 |
src-layout-no-pytest-path |
IMP001 |
broken-script-entrypoint |
PRJ003 |
malformed-pyproject |
PRJ000 |
missing-env-example |
ENV004 |
mixed-requirements-pyproject |
PRJ005 |
uv run python scripts/validate_examples.py
These act as regression fixtures — if a check stops firing, the script catches it before a release does.
Package verification
Before publishing, build the wheel and run all pre-publish verification:
uv build
uv run python scripts/verify_package.py
uv run python scripts/check_release_ready.py
scripts/verify_package.py— installs the wheel into a clean venv and runs each CLI command against the broken-project examples. Catches packaging mistakes that only appear after installation.scripts/check_release_ready.py— checks required files, pyproject metadata completeness, changelog version match, and dist artifact names.
For maintainers, run the full preflight in one command:
uv run python scripts/preflight.py
This runs all of the above in sequence and stops on the first failure. It does not upload anything.
Contributing
See CONTRIBUTING.md.
For the first public release workflow, see docs/first-release.md.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file suture_py-0.1.5.tar.gz.
File metadata
- Download URL: suture_py-0.1.5.tar.gz
- Upload date:
- Size: 18.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd26bfacbc13fcd2ae01a4ec45c395937052b9b9a3e0c01583b5c44b130a6129
|
|
| MD5 |
068759577fc6462f74ba02a7645e0cb4
|
|
| BLAKE2b-256 |
98456636b1875bae0ffa63deac51fb3ba296e7739ae8ef9c69f84a2004e0c6ff
|
File details
Details for the file suture_py-0.1.5-py3-none-any.whl.
File metadata
- Download URL: suture_py-0.1.5-py3-none-any.whl
- Upload date:
- Size: 19.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.28 {"installer":{"name":"uv","version":"0.9.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fa1a028aa0422e020e8e134f5f9c95db16e1c0956a71080034820582ed44edda
|
|
| MD5 |
bee77239d4751ff9944595f537747fdb
|
|
| BLAKE2b-256 |
3d570f5a6cab73471dcbf4de62c8776ad0ccd646baed3436bee884d41090e611
|