Claude-initial-setup project-structure-python
install
source · Clone the upstream repo
git clone https://github.com/VersoXBT/claude-initial-setup
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/VersoXBT/claude-initial-setup "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/python/project-structure-python" ~/.claude/skills/versoxbt-claude-initial-setup-project-structure-python && rm -rf "$T"
manifest:
skills/python/project-structure-python/SKILL.mdsource content
Python Project Structure
Set up Python projects with modern tooling and clear layout conventions. A well-structured project prevents import headaches, simplifies packaging, and scales cleanly.
When to Use
- User creates a new Python project or package
- User asks about project layout or packaging
- User encounters import errors or circular imports
- User sets up dependency management
- User configures linting, testing, or build tools
Core Patterns
Standard src Layout
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.py
pyproject.toml (Single Source of Truth)
pyproject.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
Package Manager Setup
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 pytest
Virtual Environments
Always 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/python
Add to
.gitignore:
.venv/ __pycache__/ *.pyc dist/ *.egg-info/ .mypy_cache/ .pytest_cache/ .ruff_cache/ .coverage
init.py and Package Exports
Use
__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, Event
Namespace Packages (No init.py)
Use 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
Configuration and Settings
# 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.
Anti-Patterns
- Flat layout without src/: Causes the uninstalled package to shadow the installed one during development, leading to subtle import bugs.
- requirements.txt as primary dependency spec: Use
dependencies. Generatepyproject.toml
from lock files only for deployment if needed.requirements.txt - Wildcard imports in init.py:
pollutes the namespace and breaks static analysis. Always use explicit imports andfrom .models import *
.__all__ - Circular imports: If module A imports from B and B imports from A, restructure. Extract shared code into a third module, or use late imports inside functions.
- Global Python installs: Never
into the system Python. Always use a virtual environment or tool-managed environment.pip install
Quick Reference
| Tool | Create | Add Dep | Run | Lock |
|---|---|---|---|---|
| uv | | | | |
| rye | | | | |
| poetry | | | | |
| File | Purpose |
|---|---|
| Dependencies, build config, tool config |
| Package marker, public API exports |
| Shared pytest fixtures |
| Pin Python version for tools |
/ | Reproducible dependency resolution |