project-structure-python — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited project-structure-python (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Set up Python projects with modern tooling and clear layout conventions. A well-structured project prevents import headaches, simplifies packaging, and scales cleanly.
The src layout prevents accidental imports of the uninstalled package and is the recommended structure for libraries and applications.
my-project/
pyproject.toml
README.md
src/
my_project/
__init__.py
core/
__init__.py
models.py
services.py
api/
__init__.py
routes.py
middleware.py
utils/
__init__.py
helpers.py
tests/
__init__.py
conftest.py
test_models.py
test_services.py
test_routes.pypyproject.toml replaces setup.py, setup.cfg, requirements.txt, and tool-specific config files. Everything in one place.
[project]
name = "my-project"
version = "0.1.0"
description = "A well-structured Python project"
requires-python = ">=3.11"
dependencies = [
"httpx>=0.27",
"pydantic>=2.0",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"pytest-cov>=5.0",
"mypy>=1.10",
"ruff>=0.5",
]
[project.scripts]
my-cli = "my_project.cli:main"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
# Tool configuration -- all in one file
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra --strict-markers --cov=my_project"
[tool.ruff]
target-version = "py311"
line-length = 88
[tool.ruff.lint]
select = ["E", "F", "I", "N", "UP", "B", "SIM", "TCH"]
[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true
[tool.coverage.run]
source = ["src/my_project"]
branch = true
[tool.coverage.report]
fail_under = 80#### uv (Recommended -- fastest)
# Create new project
uv init my-project
cd my-project
# Add dependencies
uv add httpx pydantic
uv add --dev pytest ruff mypy
# Run commands in virtual env
uv run pytest
uv run python -m my_project
# Lock dependencies
uv lock
# Sync environment from lock file
uv sync#### rye
rye init my-project
cd my-project
rye add httpx pydantic
rye add --dev pytest ruff
rye sync
rye run pytest#### poetry
poetry new my-project --src
cd my-project
poetry add httpx pydantic
poetry add --group dev pytest ruff
poetry install
poetry run pytestAlways isolate project dependencies. Never install into the global Python.
# Standard venv (if not using uv/poetry/rye)
python -m venv .venv
source .venv/bin/activate # macOS/Linux
.venv\Scripts\activate # Windows
# Verify isolation
which python # Should point to .venv/bin/pythonAdd to .gitignore:
.venv/
__pycache__/
*.pyc
dist/
*.egg-info/
.mypy_cache/
.pytest_cache/
.ruff_cache/
.coverageUse __init__.py to define the public API of each package. Keep it minimal.
# src/my_project/__init__.py
"""My Project -- a well-structured Python package."""
from my_project.core.models import User, Event
from my_project.core.services import UserService
__all__ = ["User", "Event", "UserService"]# src/my_project/core/__init__.py
# Can be empty -- just marks directory as a package
# Or re-export key names for convenience
from my_project.core.models import User, EventUse implicit namespace packages when you need to split a package across multiple directories or distributions. Omit __init__.py entirely.
# Two separate distributions sharing the "mycompany" namespace
distribution-a/
src/
mycompany/ # No __init__.py
billing/
__init__.py
invoices.py
distribution-b/
src/
mycompany/ # No __init__.py
shipping/
__init__.py
tracking.py
# Both install into the same namespace
import mycompany.billing.invoices
import mycompany.shipping.tracking# src/my_project/config.py
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
"""Application settings loaded from environment variables."""
database_url: str
redis_url: str = "redis://localhost:6379"
debug: bool = False
log_level: str = "INFO"
model_config = {"env_prefix": "APP_", "env_file": ".env"}
# Usage
settings = Settings() # Reads APP_DATABASE_URL, APP_DEBUG, etc.one during development, leading to subtle import bugs.
pyproject.toml dependencies.Generate requirements.txt from lock files only for deployment if needed.
from .models import * pollutes the namespaceand breaks static analysis. Always use explicit imports and __all__.
Extract shared code into a third module, or use late imports inside functions.
pip install into the system Python. Always usea virtual environment or tool-managed environment.
| Tool | Create | Add Dep | Run | Lock |
|---|---|---|---|---|
| uv | uv init | uv add pkg | uv run cmd | uv lock |
| rye | rye init | rye add pkg | rye run cmd | rye lock |
| poetry | poetry new | poetry add pkg | poetry run cmd | poetry lock |
| File | Purpose |
|---|---|
pyproject.toml | Dependencies, build config, tool config |
src/pkg/__init__.py | Package marker, public API exports |
tests/conftest.py | Shared pytest fixtures |
.python-version | Pin Python version for tools |
uv.lock / poetry.lock | Reproducible dependency resolution |
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.