Python Project Setup
When to Use
Use this skill when:
- User asks to set up a new Python project from scratch
- User wants to know Python project structure conventions
- User asks about pyproject.toml, setup.py, or Python packaging
- User mentions virtual environments, dependency management, or lock files
- User asks about configuring mypy, ruff, or Python tooling for a new project
- User wants to create a distributable Python package or library
- User asks about src layout vs flat layout for Python
Do NOT use this skill when:
- The user is asking about Python language features or syntax → use
python-idioms
- The user already has a project and wants to add testing → use
python-testing-patterns
- The user wants to set up async patterns → use
python-async-patterns
- The user is asking about data validation and modeling → use
python-data-modeling
- The user wants to improve performance of existing code → use
python-performance
- The user is asking about type annotations and generics → use
python-type-system
- The user wants to handle errors and exceptions → use
python-error-handling
Process
Assess the project context. Before generating any files, determine:
- Is this a library/package (distributed via PyPI) or an application (deployed directly)?
- If library: src layout is mandatory. Editable installs and packaging are critical.
- If application: flat layout is acceptable. Focus on reproducibility and deployment.
- Is this a solo project or team project?
- If team: enforce pre-commit hooks, stricter linting, and type checking from day one.
- If solo: still configure tooling but relax some rules for velocity.
- What is the minimum Python version?
- If 3.12+: use the new
type statement syntax awareness. Enable latest mypy features.
- If 3.10-3.11: structural pattern matching is available. Standard generics from
__future__.
- If 3.9 or below: avoid unless legacy constraint. Document why in pyproject.toml.
Choose the dependency management tool. Apply this decision tree:
- If the team values fast installs and modern standards: use uv (Rust-based, 10-100x faster than pip, resolves dependencies deterministically, lockfile built-in).
- If the project needs compatibility with existing CI/CD that only supports the standard Python installer: use pip-tools with
requirements.in compiled to requirements.txt.
- If the project is a library that needs flexible dependency ranges: still use uv or pip-tools, but define loose ranges in
pyproject.toml [project.dependencies] and pin exact versions in lock files.
- NEVER use
requirements.txt alone without a lock mechanism. Version drift between developers is the number one Python project reliability problem.
Choose the project layout. Apply this decision tree:
- If building a redistributable package: use src layout (
src/package_name/). This prevents accidental imports from the working directory during testing.
- If building a deployed application with no distribution: flat layout (
package_name/ at root) is acceptable and simpler.
- If building a monorepo with multiple packages: use src layout per package with a workspace definition.
Generate the project structure. Create the directory tree and all configuration files per the Output Format below. Every file must be generated - do not leave any configuration for the user to fill in manually.
Configure the type checker (mypy). Apply this decision tree:
- Default:
strict = true in [tool.mypy]. This enables all strict flags.
- If integrating with third-party packages that lack type stubs: add per-module overrides with
ignore_missing_imports = true for those specific packages only.
- If the project uses Pydantic: add
plugins = ["pydantic.mypy"] for model validation support.
- ALWAYS include a
py.typed marker file in the package directory for PEP 561 compliance.
Configure the linter and formatter (ruff). Apply this decision tree:
- Use ruff for both linting AND formatting (replaces flake8, black, isort, pyflakes, and more).
- Default rule set:
select = ["E", "F", "W", "I", "N", "UP", "S", "B", "A", "C4", "DTZ", "T10", "ISC", "ICN", "PIE", "PT", "RSE", "RET", "SLF", "SIM", "TID", "TCH", "ARG", "PLC", "PLE", "PLW", "TRY", "FLY", "PERF", "RUF"]
- If team is migrating from flake8/black: start with
select = ["E", "F", "W", "I"] and expand incrementally.
- Line length: 88 (ruff default, matches black) or 120 (if team prefers wider lines on modern monitors).
Configure pre-commit hooks (team projects only). Set up:
- ruff check (lint)
- ruff format (format)
- mypy (type check)
- pytest (optional, for fast test suites only - do not gate on slow integration tests)
Verify the setup. Confirm the following checks pass:
uv sync (or editable dev install) succeeds without errors
ruff check . passes with no violations
ruff format --check . reports no formatting changes needed
mypy . passes with zero errors
pytest executes successfully (even with minimal tests)
Output Format
{project-name}/
├── src/ # Only for src layout
│ └── {package_name}/
│ ├── __init__.py
│ ├── py.typed # PEP 561 marker
│ └── main.py # Entry point (applications only)
├── tests/
│ ├── __init__.py
│ ├── conftest.py # Shared fixtures
│ └── test_placeholder.py # Initial test to verify setup
├── pyproject.toml # Complete project configuration
├── .python-version # Pin Python version (e.g., 3.12)
├── .gitignore # Python-specific gitignore
├── README.md # Project documentation
└── .pre-commit-config.yaml # Pre-commit hooks (team projects)
pyproject.toml template:
[project]
name = "{project-name}"
version = "0.1.0"
description = "{Project description}"
requires-python = ">={min-python-version}"
license = "MIT"
authors = [
{ name = "{Author Name}", email = "{email}" },
]
dependencies = []
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"pytest-cov>=5.0",
"mypy>=1.10",
"ruff>=0.5",
"pre-commit>=3.7",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/{package_name}"]
[tool.ruff]
target-version = "py{min-version-digits}"
line-length = 88
[tool.ruff.lint]
select = [
"E", "F", "W", "I", "N", "UP", "S", "B", "A", "C4",
"DTZ", "T10", "ISC", "ICN", "PIE", "PT", "RSE", "RET",
"SLF", "SIM", "TID", "TCH", "ARG", "PLC", "PLE", "PLW",
"TRY", "FLY", "PERF", "RUF",
]
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101"] # Allow assert in tests
[tool.mypy]
strict = true
warn_return_any = true
warn_unused_configs = true
plugins = []
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra -q --strict-markers"
conftest.py template:
"""Shared test fixtures for {project-name}."""
import pytest
@pytest.fixture
def sample_data() -> dict[str, str]:
"""Provide sample data for tests. Customize per project."""
return {"key": "value"}
.pre-commit-config.yaml template:
repos:
- repo: local
hooks:
- id: ruff-check
name: ruff-check
entry: ruff check --fix
language: system
types: [python]
- id: ruff-format
name: ruff-format
entry: ruff format
language: system
types: [python]
- id: mypy
name: mypy
entry: mypy
language: system
types: [python]
pass_filenames: false
Rules
- NEVER use
setup.py or setup.cfg for new projects. pyproject.toml is the standard since PEP 621.
- NEVER use
requirements.txt as the sole dependency specification. Always use pyproject.toml for dependency declaration with a lockfile mechanism for reproducibility.
- ALWAYS include a
py.typed marker file in the package directory for PEP 561 compliance.
- ALWAYS configure mypy in strict mode by default. Relax per-module only with documented justification.
- ALWAYS use ruff for both linting and formatting. Do not configure flake8, black, and isort separately - ruff replaces all three.
- NEVER hardcode Python version requirements below 3.10 without explicit legacy justification from the user.
- ALWAYS include a
.gitignore with Python-specific entries (.venv/, __pycache__/, *.pyc, .mypy_cache/, .ruff_cache/, dist/, *.egg-info/).
- ALWAYS create an initial test file that imports the package to verify the project structure works end-to-end.
- For library projects: ALWAYS use src layout. For application projects: document the choice between src and flat layout with rationale.
- NEVER leave placeholder or TODO comments in generated configuration files. Every value must be filled in based on the project context.
Edge Cases
Legacy codebase migration: When the user has an existing project with setup.py and requirements.txt, do not rewrite from scratch. Create a pyproject.toml that preserves all existing dependencies, add a migration note, and configure ruff with a minimal rule set that the existing code passes. Expand rules incrementally. Keep setup.py as a shim if the build system requires it during transition.
Monorepo with multiple Python packages: Each package gets its own pyproject.toml with src layout. Use uv workspaces (if available) or a top-level Makefile that orchestrates builds. Shared dependencies go in a constraints.txt or workspace-level configuration. Each package must be independently installable.
CI environment without uv: If the CI system only supports the standard Python package installer, generate a requirements-lock.txt from uv and reference it in CI. Document the two-step workflow: developers use uv locally, CI uses the lock file for deterministic installs.
Corporate proxy or air-gapped environment: Configure uv or pip to use a private index with --index-url or --extra-index-url in pyproject.toml under [tool.uv] or in a pip.conf. Document the configuration so new developers do not default to public PyPI.
Data science projects with heavy native dependencies: When the project depends on NumPy, SciPy, pandas, or similar packages with C extensions, verify the build toolchain is available. Consider using conda/mamba for native dependency management alongside pyproject.toml for Python-level dependencies. Document the dual dependency management strategy.
Example
Input: "I need to set up a new Python web API project. It will be a FastAPI service deployed to AWS Lambda via container. Team of 4 developers, Python 3.12. We use GitHub Actions for CI."
Output:
order-service/
├── src/
│ └── order_service/
│ ├── __init__.py
│ ├── py.typed
│ ├── main.py
│ ├── config.py
│ ├── models/
│ │ └── __init__.py
│ ├── routes/
│ │ └── __init__.py
│ └── services/
│ └── __init__.py
├── tests/
│ ├── __init__.py
│ ├── conftest.py
│ ├── test_main.py
│ └── test_health.py
├── pyproject.toml
├── .python-version
├── Dockerfile
├── .gitignore
├── .pre-commit-config.yaml
└── README.md
pyproject.toml:
[project]
name = "order-service"
version = "0.1.0"
description = "Order management API service"
requires-python = ">=3.12"
license = "MIT"
authors = [
{ name = "Order Team", email = "team@company.com" },
]
dependencies = [
"fastapi>=0.115",
"uvicorn[standard]>=0.30",
"pydantic>=2.8",
"mangum>=0.19",
]
[project.optional-dependencies]
dev = [
"pytest>=8.0",
"pytest-cov>=5.0",
"pytest-asyncio>=0.24",
"httpx>=0.27",
"mypy>=1.10",
"ruff>=0.5",
"pre-commit>=3.7",
]
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.build.targets.wheel]
packages = ["src/order_service"]
[tool.ruff]
target-version = "py312"
line-length = 88
[tool.ruff.lint]
select = [
"E", "F", "W", "I", "N", "UP", "S", "B", "A", "C4",
"DTZ", "T10", "ISC", "ICN", "PIE", "PT", "RSE", "RET",
"SLF", "SIM", "TID", "TCH", "ARG", "PLC", "PLE", "PLW",
"TRY", "FLY", "PERF", "RUF",
]
[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S101"]
[tool.mypy]
strict = true
warn_return_any = true
warn_unused_configs = true
plugins = ["pydantic.mypy"]
[[tool.mypy.overrides]]
module = ["mangum.*"]
ignore_missing_imports = true
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra -q --strict-markers"
asyncio_mode = "auto"
tests/conftest.py:
"""Shared test fixtures for order-service."""
import pytest
from fastapi.testclient import TestClient
from order_service.main import app
@pytest.fixture
def client() -> TestClient:
"""Provide a test client for the FastAPI application."""
return TestClient(app)
tests/test_health.py:
"""Health check endpoint tests."""
from fastapi.testclient import TestClient
def test_health_returns_ok(client: TestClient) -> None:
"""Verify the health check endpoint returns 200 with status ok."""
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
This setup provides: src layout for packaging integrity, mypy strict mode with Pydantic plugin, ruff with comprehensive rule set, pytest-asyncio for async endpoint testing, httpx for async client testing, mangum for AWS Lambda adapter, and pre-commit hooks for the 4-person team. The Dockerfile would use multi-stage builds targeting the Lambda Python 3.12 base image.
1---2name: python-project-setup3description: Guides expert-level Python project initialization with modern tooling: pyproject.toml configuration, uv for dependency management, src layout decisions, mypy strict mode, and ruff for linting/formatting. Use when the user asks about starting a new Python project, structuring a Python package, configuring pyproject.toml, choosing between src layout and flat layout, setting up Python dependency management, or configuring Python linting and type checking from scratch. Do NOT use when the user asks about Python language features or syntax (use `python-idioms`), Python testing setup (use `python-testing-patterns`), Python async programming (use `python-async-patterns`), or Python data modeling with Pydantic (use `python-data-modeling`).4license: Apache-2.05---67# Python Project Setup89## When to Use1011**Use this skill when:**12- User asks to set up a new Python project from scratch13- User wants to know Python project structure conventions14- User asks about pyproject.toml, setup.py, or Python packaging15- User mentions virtual environments, dependency management, or lock files16- User asks about configuring mypy, ruff, or Python tooling for a new project17- User wants to create a distributable Python package or library18- User asks about src layout vs flat layout for Python1920**Do NOT use this skill when:**21- The user is asking about Python language features or syntax → use `python-idioms`22- The user already has a project and wants to add testing → use `python-testing-patterns`23- The user wants to set up async patterns → use `python-async-patterns`24- The user is asking about data validation and modeling → use `python-data-modeling`25- The user wants to improve performance of existing code → use `python-performance`26- The user is asking about type annotations and generics → use `python-type-system`27- The user wants to handle errors and exceptions → use `python-error-handling`2829## Process30311. **Assess the project context.** Before generating any files, determine:32 - Is this a library/package (distributed via PyPI) or an application (deployed directly)?33 - If library: src layout is mandatory. Editable installs and packaging are critical.34 - If application: flat layout is acceptable. Focus on reproducibility and deployment.35 - Is this a solo project or team project?36 - If team: enforce pre-commit hooks, stricter linting, and type checking from day one.37 - If solo: still configure tooling but relax some rules for velocity.38 - What is the minimum Python version?39 - If 3.12+: use the new `type` statement syntax awareness. Enable latest mypy features.40 - If 3.10-3.11: structural pattern matching is available. Standard generics from `__future__`.41 - If 3.9 or below: avoid unless legacy constraint. Document why in pyproject.toml.42432. **Choose the dependency management tool.** Apply this decision tree:44 - If the team values fast installs and modern standards: use **uv** (Rust-based, 10-100x faster than pip, resolves dependencies deterministically, lockfile built-in).45 - If the project needs compatibility with existing CI/CD that only supports the standard Python installer: use **pip-tools** with `requirements.in` compiled to `requirements.txt`.46 - If the project is a library that needs flexible dependency ranges: still use uv or pip-tools, but define loose ranges in `pyproject.toml [project.dependencies]` and pin exact versions in lock files.47 - NEVER use `requirements.txt` alone without a lock mechanism. Version drift between developers is the number one Python project reliability problem.48493. **Choose the project layout.** Apply this decision tree:50 - If building a redistributable package: use **src layout** (`src/package_name/`). This prevents accidental imports from the working directory during testing.51 - If building a deployed application with no distribution: **flat layout** (`package_name/` at root) is acceptable and simpler.52 - If building a monorepo with multiple packages: use src layout per package with a workspace definition.53544. **Generate the project structure.** Create the directory tree and all configuration files per the Output Format below. Every file must be generated - do not leave any configuration for the user to fill in manually.55565. **Configure the type checker (mypy).** Apply this decision tree:57 - Default: `strict = true` in `[tool.mypy]`. This enables all strict flags.58 - If integrating with third-party packages that lack type stubs: add per-module overrides with `ignore_missing_imports = true` for those specific packages only.59 - If the project uses Pydantic: add `plugins = ["pydantic.mypy"]` for model validation support.60 - ALWAYS include a `py.typed` marker file in the package directory for PEP 561 compliance.61626. **Configure the linter and formatter (ruff).** Apply this decision tree:63 - Use ruff for both linting AND formatting (replaces flake8, black, isort, pyflakes, and more).64 - Default rule set: `select = ["E", "F", "W", "I", "N", "UP", "S", "B", "A", "C4", "DTZ", "T10", "ISC", "ICN", "PIE", "PT", "RSE", "RET", "SLF", "SIM", "TID", "TCH", "ARG", "PLC", "PLE", "PLW", "TRY", "FLY", "PERF", "RUF"]`65 - If team is migrating from flake8/black: start with `select = ["E", "F", "W", "I"]` and expand incrementally.66 - Line length: 88 (ruff default, matches black) or 120 (if team prefers wider lines on modern monitors).67687. **Configure pre-commit hooks** (team projects only). Set up:69 - ruff check (lint)70 - ruff format (format)71 - mypy (type check)72 - pytest (optional, for fast test suites only - do not gate on slow integration tests)73748. **Verify the setup.** Confirm the following checks pass:75 - `uv sync` (or editable dev install) succeeds without errors76 - `ruff check .` passes with no violations77 - `ruff format --check .` reports no formatting changes needed78 - `mypy .` passes with zero errors79 - `pytest` executes successfully (even with minimal tests)8081## Output Format8283```84{project-name}/85├── src/ # Only for src layout86│ └── {package_name}/87│ ├── __init__.py88│ ├── py.typed # PEP 561 marker89│ └── main.py # Entry point (applications only)90├── tests/91│ ├── __init__.py92│ ├── conftest.py # Shared fixtures93│ └── test_placeholder.py # Initial test to verify setup94├── pyproject.toml # Complete project configuration95├── .python-version # Pin Python version (e.g., 3.12)96├── .gitignore # Python-specific gitignore97├── README.md # Project documentation98└── .pre-commit-config.yaml # Pre-commit hooks (team projects)99```100101**pyproject.toml template:**102103```toml104[project]105name = "{project-name}"106version = "0.1.0"107description = "{Project description}"108requires-python = ">={min-python-version}"109license = "MIT"110authors = [111 { name = "{Author Name}", email = "{email}" },112]113dependencies = []114115[project.optional-dependencies]116dev = [117 "pytest>=8.0",118 "pytest-cov>=5.0",119 "mypy>=1.10",120 "ruff>=0.5",121 "pre-commit>=3.7",122]123124[build-system]125requires = ["hatchling"]126build-backend = "hatchling.build"127128[tool.hatch.build.targets.wheel]129packages = ["src/{package_name}"]130131[tool.ruff]132target-version = "py{min-version-digits}"133line-length = 88134135[tool.ruff.lint]136select = [137 "E", "F", "W", "I", "N", "UP", "S", "B", "A", "C4",138 "DTZ", "T10", "ISC", "ICN", "PIE", "PT", "RSE", "RET",139 "SLF", "SIM", "TID", "TCH", "ARG", "PLC", "PLE", "PLW",140 "TRY", "FLY", "PERF", "RUF",141]142143[tool.ruff.lint.per-file-ignores]144"tests/**" = ["S101"] # Allow assert in tests145146[tool.mypy]147strict = true148warn_return_any = true149warn_unused_configs = true150plugins = []151152[tool.pytest.ini_options]153testpaths = ["tests"]154addopts = "-ra -q --strict-markers"155```156157**conftest.py template:**158159```python160"""Shared test fixtures for {project-name}."""161162import pytest163164165@pytest.fixture166def sample_data() -> dict[str, str]:167 """Provide sample data for tests. Customize per project."""168 return {"key": "value"}169```170171**.pre-commit-config.yaml template:**172173```yaml174repos:175 - repo: local176 hooks:177 - id: ruff-check178 name: ruff-check179 entry: ruff check --fix180 language: system181 types: [python]182 - id: ruff-format183 name: ruff-format184 entry: ruff format185 language: system186 types: [python]187 - id: mypy188 name: mypy189 entry: mypy190 language: system191 types: [python]192 pass_filenames: false193```194195## Rules1961971. NEVER use `setup.py` or `setup.cfg` for new projects. `pyproject.toml` is the standard since PEP 621.1982. NEVER use `requirements.txt` as the sole dependency specification. Always use `pyproject.toml` for dependency declaration with a lockfile mechanism for reproducibility.1993. ALWAYS include a `py.typed` marker file in the package directory for PEP 561 compliance.2004. ALWAYS configure mypy in strict mode by default. Relax per-module only with documented justification.2015. ALWAYS use ruff for both linting and formatting. Do not configure flake8, black, and isort separately - ruff replaces all three.2026. NEVER hardcode Python version requirements below 3.10 without explicit legacy justification from the user.2037. ALWAYS include a `.gitignore` with Python-specific entries (`.venv/`, `__pycache__/`, `*.pyc`, `.mypy_cache/`, `.ruff_cache/`, `dist/`, `*.egg-info/`).2048. ALWAYS create an initial test file that imports the package to verify the project structure works end-to-end.2059. For library projects: ALWAYS use src layout. For application projects: document the choice between src and flat layout with rationale.20610. NEVER leave placeholder or TODO comments in generated configuration files. Every value must be filled in based on the project context.207208## Edge Cases209210- **Legacy codebase migration:** When the user has an existing project with `setup.py` and `requirements.txt`, do not rewrite from scratch. Create a `pyproject.toml` that preserves all existing dependencies, add a migration note, and configure ruff with a minimal rule set that the existing code passes. Expand rules incrementally. Keep `setup.py` as a shim if the build system requires it during transition.211212- **Monorepo with multiple Python packages:** Each package gets its own `pyproject.toml` with src layout. Use uv workspaces (if available) or a top-level `Makefile` that orchestrates builds. Shared dependencies go in a `constraints.txt` or workspace-level configuration. Each package must be independently installable.213214- **CI environment without uv:** If the CI system only supports the standard Python package installer, generate a `requirements-lock.txt` from uv and reference it in CI. Document the two-step workflow: developers use uv locally, CI uses the lock file for deterministic installs.215216- **Corporate proxy or air-gapped environment:** Configure uv or pip to use a private index with `--index-url` or `--extra-index-url` in `pyproject.toml` under `[tool.uv]` or in a `pip.conf`. Document the configuration so new developers do not default to public PyPI.217218- **Data science projects with heavy native dependencies:** When the project depends on NumPy, SciPy, pandas, or similar packages with C extensions, verify the build toolchain is available. Consider using conda/mamba for native dependency management alongside pyproject.toml for Python-level dependencies. Document the dual dependency management strategy.219220## Example221222**Input:** "I need to set up a new Python web API project. It will be a FastAPI service deployed to AWS Lambda via container. Team of 4 developers, Python 3.12. We use GitHub Actions for CI."223224**Output:**225226```227order-service/228├── src/229│ └── order_service/230│ ├── __init__.py231│ ├── py.typed232│ ├── main.py233│ ├── config.py234│ ├── models/235│ │ └── __init__.py236│ ├── routes/237│ │ └── __init__.py238│ └── services/239│ └── __init__.py240├── tests/241│ ├── __init__.py242│ ├── conftest.py243│ ├── test_main.py244│ └── test_health.py245├── pyproject.toml246├── .python-version247├── Dockerfile248├── .gitignore249├── .pre-commit-config.yaml250└── README.md251```252253**pyproject.toml:**254255```toml256[project]257name = "order-service"258version = "0.1.0"259description = "Order management API service"260requires-python = ">=3.12"261license = "MIT"262authors = [263 { name = "Order Team", email = "team@company.com" },264]265dependencies = [266 "fastapi>=0.115",267 "uvicorn[standard]>=0.30",268 "pydantic>=2.8",269 "mangum>=0.19",270]271272[project.optional-dependencies]273dev = [274 "pytest>=8.0",275 "pytest-cov>=5.0",276 "pytest-asyncio>=0.24",277 "httpx>=0.27",278 "mypy>=1.10",279 "ruff>=0.5",280 "pre-commit>=3.7",281]282283[build-system]284requires = ["hatchling"]285build-backend = "hatchling.build"286287[tool.hatch.build.targets.wheel]288packages = ["src/order_service"]289290[tool.ruff]291target-version = "py312"292line-length = 88293294[tool.ruff.lint]295select = [296 "E", "F", "W", "I", "N", "UP", "S", "B", "A", "C4",297 "DTZ", "T10", "ISC", "ICN", "PIE", "PT", "RSE", "RET",298 "SLF", "SIM", "TID", "TCH", "ARG", "PLC", "PLE", "PLW",299 "TRY", "FLY", "PERF", "RUF",300]301302[tool.ruff.lint.per-file-ignores]303"tests/**" = ["S101"]304305[tool.mypy]306strict = true307warn_return_any = true308warn_unused_configs = true309plugins = ["pydantic.mypy"]310311[[tool.mypy.overrides]]312module = ["mangum.*"]313ignore_missing_imports = true314315[tool.pytest.ini_options]316testpaths = ["tests"]317addopts = "-ra -q --strict-markers"318asyncio_mode = "auto"319```320321**tests/conftest.py:**322323```python324"""Shared test fixtures for order-service."""325326import pytest327from fastapi.testclient import TestClient328329from order_service.main import app330331332@pytest.fixture333def client() -> TestClient:334 """Provide a test client for the FastAPI application."""335 return TestClient(app)336```337338**tests/test_health.py:**339340```python341"""Health check endpoint tests."""342343from fastapi.testclient import TestClient344345346def test_health_returns_ok(client: TestClient) -> None:347 """Verify the health check endpoint returns 200 with status ok."""348 response = client.get("/health")349 assert response.status_code == 200350 assert response.json() == {"status": "ok"}351```352353This setup provides: src layout for packaging integrity, mypy strict mode with Pydantic plugin, ruff with comprehensive rule set, pytest-asyncio for async endpoint testing, httpx for async client testing, mangum for AWS Lambda adapter, and pre-commit hooks for the 4-person team. The Dockerfile would use multi-stage builds targeting the Lambda Python 3.12 base image.