496303471d
---ci--- project: atelier phase: 7 milestone: v0.1 status: complete phase_role: final milestone_complete: true requirements: covered: [ATELIER-01, ATELIER-02, ATELIER-03, ATELIER-04, ATELIER-05, ATELIER-06, ATELIER-07, ATELIER-08, ATELIER-09, ATELIER-10, ATELIER-11, ATELIER-12, ATELIER-13, ATELIER-14, ATELIER-15, ATELIER-16, ATELIER-17, ATELIER-18, ATELIER-19, ATELIER-20, ATELIER-21, ATELIER-22, ATELIER-23, ATELIER-24, ATELIER-25, ATELIER-26, ATELIER-27, ATELIER-28, ATELIER-29, ATELIER-30, ATELIER-31, ATELIER-32, ATELIER-33, ATELIER-34, ATELIER-35] partial: [] ship: milestone: v0.1 type: NFR tag: v0.0.7 merge: milestone/v0.1-atelier -> main release: https://git.cloudinit.dev/cloudinit-bot/atelier/releases/tag/v0.0.7 ---/ci--- Milestone v0.1 — Initial Framework (NFR, complete). 8 core principles (C1-C8), 11 domains, 110 domain principles, 27 derived docs, 4 good + 3 bad examples, 4 language docs, full matrix, 3 review docs. All 35 requirements covered. 7 patches (v0.0.0 pre-execution through v0.0.7 final). v0.0.7 IS the v0.1.0 milestone release.
2.9 KiB
2.9 KiB
Python — Language Application
How Atelier's domain principles apply in Python specifically. Derives from
domains/docs.
Type System (C1 Correctness, Data P7 Type Fidelity)
- Type hints on every function:
def get_user(id: UUID) -> User | None:. mypy --strictorpyrightin CI: type check is not optional.- No
Anywithout justification:Anydisables the type checker. Useobject+ narrowing. - Pydantic for runtime validation: schemas validate and type at the boundary.
from pydantic import BaseModel
from uuid import UUID
class UserCreate(BaseModel):
email: str
name: str
# additionalProperties: false by default (extra='forbid')
Error Handling (Errors P1 Errors are Data)
- Exceptions for exceptional cases, not control flow.
raisenotreturn Nonefor errors. - Custom exception hierarchy:
class AppError(Exception): pass
class ValidationError(AppError): pass
class NotFoundError(AppError): pass
- Never bare
except::except Exception as e:(catch specific, not everything). - Never
except: pass: log and re-raise or handle, never swallow (Errors P2).
Async (Concurrency P7, P8)
asynciofor I/O-bound:async def,await. Not threads for I/O.anyiofor portability if you may switch runtimes (trio compatibility).- Timeout on every
await:asyncio.wait_for(coro, timeout=5), not bareawait. - Cancellation propagated:
asyncio.CancelledErroris not caught; it propagates.
Immutability (Concurrency P1)
frozen=Truedataclasses for value objects:
from dataclasses import dataclass
@dataclass(frozen=True)
class UserId:
value: str
- Tuples over lists for fixed-length, immutable sequences.
- No in-place mutation of shared state: return new objects.
Nullability (C1)
Optional[T]isT | None: explicit, must be checked.Noneis not "not found": raiseNotFoundErroror returnResult, notNone.assertis for invariants, not for runtime checks (stripped with-O).
Testing (Testing)
- pytest with fixtures (factories, not shared state).
pytest --randomlyto catch order-dependent tests (P2 Independence).freezegunfor time: nodatetime.now()in tests; inject the clock.factory_boyorpytest-factoryboyfor realistic factories.
Observability (Observability P1)
structlogorpython-json-logger: JSON logs, notprint.loggingwith structured formatter: every log hasrequest_id,user_id,event.- No secrets in logs:
mask_secret()helper, orstructlogprocessors.
Tooling (DevOps P2)
rufffor lint + format: replaces flake8 + black + isort.mypy --strictin CI: type check.pip-toolsorpoetryfor lockfile: pinned dependencies.pip install --no-deps -r requirements.txt: reproducible install.