Unit 4.1 · about 4 h
19 / 324Typed interfaces, runtime validation and configuration
Every later service in this section stands on this boundary. Untyped inputs and untyped configuration turn typos into silent corruption. Typed contracts with explicit validation move those failures to the edge where they are loud.
- explain
- implement
You can now
- Compare nominal and structural typing, generics with bounded TypeVars and variance, and justify where dynamic values are quarantined at the boundary.
- Define a typed public API with protocols and generics that keeps persistence and transport types out of domain logic.
- Build validated input models and typed settings with explicit unknown-field and precedence behavior, and diagnose one silent-corruption path.
Covers
- Nominal and structural typing, generics and protocols
- Runtime validation and trusted versus untrusted boundaries
- Typed configuration, precedence, secrets and unknown fields
Study, in this order
7 steps
- Pydantic Models: basic usage, data conversion and extra dataOfficial docs typed-service build path
Basic model usage including Model methods and properties (
model_validate,model_dump); Data conversion; Extra data (ignore,forbid,allow); Validating data, including Creating models without validation - Pydantic ValidatorsOfficial docs typed-service build path
Field validators (
after,before,plainandwrap, in the annotated and decorator patterns); Model validators; Raising validation errors - Pydantic Fields: constraints and strict fieldsOfficial docs typed-service build path
Field constraints; Strict fields
- Pydantic Strict ModeOfficial docs typed-service build path
As a validation parameter; At the field level, including the
Strict()metadata class; As a configuration value - Pydantic Settings Management: names, dotenv and priorityOfficial docs typed-service build path
Usage (
env_prefix,validation_alias); Environment variable names with Unknown environment variables; Parsing environment variable values; Dotenv (.env) support, including undefined dotenv keys raising under the defaultextraforbid; Field value priority - Interfaces and ProtocolsAlgoMaster Python - interfaces, generics and data files
Structural vs Nominal Typing; Defining a
Protocol;@runtime_checkable: AddingisinstanceSupport; Generic Protocols; Dependency Injection Contracts - Generic TypesAlgoMaster Python - interfaces, generics and data files
TypeVarBasics; Generic Classes withGeneric[T]; Bounded TypeVars; Variance: Whylist[Product]Isn'tlist[Item]; Putting It Together: A Generic Service Layer
Focus on
Protocoldefines a static duck-typing boundary. Nominal inheritance is checked by name, structural compatibility by shape. UseProtocolfor dependencies the domain calls but does not own.- Validate at the trust boundary with
model_validateandmodel_validate_json.model_constructskips validation and is only for already-trusted data. - The default
extrabehavior ignores unknown fields, which silently drops data. Boundary models that must reject drift useextraforbid, andstrictmode rejects coercions such as'3'to3. - Settings precedence is init kwargs, then environment, then dotenv, then defaults. Unknown environment names are ignored, so a misspelled variable silently keeps the default, but an undefined key in the dotenv file raises under the default
extraforbid.
Reproduce closed-book
Protocol plus a generic service, a boundary model with extra forbid and a StrictInt field, and settings with a prefix. Show that model_validate coerces '3' in lax mode but strict rejects it with int_type, that an extra field raises extra_forbidden, and that init beats env beats default.Traps
runtime_checkableisinstancechecks method presence only, not signatures. A sender with the wrong signature passesisinstanceand fails later; keep static checking as the real gate.parse_obj,.dict,.jsonand@validatorstill run, so old tutorials look current. In Pydantic 2.14 they are deprecated shims whoseDeprecationWarningstays hidden outside__main__, and some v1 calls now fail:.json(indent=2)raisesTypeError. Usemodel_validate,model_dump,model_dump_jsonandfield_validator.- Expecting
extraforbidto catch a misspelled environment variable. Unmatched environment names never reach validation (only undefined dotenv keys raise), so test precedence with a wrong-name variable and watch the default survive.
Practice
AlgoMaster Exercise: Interfaces and Protocols
Exercises 1 to 5 on the exercise page, with the lesson sections Structural vs Nominal Typing and Dependency Injection Contracts
Complete the set for recall, then add a self-built check: a strict boundary model with forbid-extra rejects extra fields and wrong types, and settings precedence init beats env beats default holds.
Self-test
-
A field
x: intreceives'123'in lax mode and understrictTrue. What happens in each?Lax coerces to123. Strict raises with typeint_type. Verified on Pydantic 2.14 with CPython 3.14. -
A payload carries an
extrakeyy. How do default andextraforbidmodels differ?Default ignoresyand dumps without it. Forbid raisesextra_forbiddenat('y',). -
What are the v2 replacements for
parse_objand.dict, and why do the old names matter?model_validateandmodel_dump. The old names still run as deprecated shims that forward to the v2 methods, so legacy code looks current until v3 removes them, and some v1 signatures already fail:.json(indent=2)raisesTypeError. Verified on Pydantic 2.14.1. -
Init passes port 9000, env sets 8080, default is 8000. Which wins, and what if the env name is misspelled?
Init wins with 9000. A misspelled name is ignored and the default 8000 applies. Verified with pydantic-settings on CPython 3.14.
Completion criterion
Stop when
You define a Protocol plus generic service without transport imports, validate good, malformed and extra-field payloads with v2 calls only, and demonstrate init beats env beats default with a misspelled variable keeping the default.
Leads to
- 4.2 JSON, JSONL, CSV, columnar formats and bounded ingestion Carry the validated record into bounded JSONL and CSV ingestion with quarantine.
- 4.7 Layer boundaries and command-line contracts Expose the same typed service through the CLI surface.
Comes after
- 3.7 Type hints, generics, protocols and narrowingProtocols and narrowing become service contracts.
Resources for this unit
- Official docs typed-service build path Build one record service in this order: structural protocols and generics; Pydantic models, validators, strict mode, serialization and settings; FastAPI request bodies, dependencies, errors and tests; then PostgreSQL transactions and EXPLAIN. Replay valid, malformed, duplicate and previous-schema requests through CLI and HTTP.
- AlgoMaster Python - interfaces, generics and data files Use Interfaces and Protocols plus Generic Types for structural typing, and Working with JSON plus Working with CSV for JSONL, error handling,
newlineandDictReaderbehavior. Lesson quizzes check recall; implementation is verified with your own scripts.
Also in this section
- AlgoMaster SQL - selected route Study SQL Foundations, Querying Data, Joins, Subqueries and CTEs, Window Functions, DDL, DML and Views, Performance Optimization, Transactions and Concurrency, and Data Modeling. Stop when the service queries and transaction tests pass.
- PostgreSQL transactions and EXPLAIN Use the transaction tutorial and EXPLAIN guide to demonstrate commit, rollback, one isolation anomaly or prevented anomaly, and a measured plan improvement on the database actually used.
- FastAPI tutorial Use Request Body, Dependencies, Handling Errors and Testing to expose the same service layer through HTTP. Keep framework models outside the domain core and preserve the same error semantics as the CLI.
- AlgoMaster Low-Level Design - one rehearsal Use one fitting design prompt to rehearse interface contracts, dependency direction and failure ownership. Treat the review as feedback, then verify behavior with your own tests.
- Stas' Python Cookbook - data and interfaces Use Serialization and Deserialization plus Environment and Arguments for focused implementation help. Compare JSON, JSONL, CSV and compressed data, and never use pickle for untrusted interchange.
- SQLAlchemy 2.1 documentation - select, Session and eager loading Use the
select()tutorial for 2.x query style,Sessionbasics for the transaction boundary and identity map, and relationship loading for lazy versusselectinloadversusjoinedloadplusraiseload. Useselect()style andSession.executeorSession.scalarsonly, never the legacyQueryAPI. - Alembic tutorial - migration scripts and downgrading Use Create a Migration Script, Running our First and Second Migration, Getting Information and Downgrading to run a paired
upgradeanddowngraderound trip on a scratch database. - PyArrow Parquet read and write docs Use Reading and Writing Single Files plus
Tablefrom_pydictandfrom_pylistto convert normalized records to Parquet and test types, nulls, column projection and schema. - Stripe idempotent requests Use the
Idempotency-Keyheader contract, stored-response replay, parameter-mismatch errors andPOST-only scope to implement keyed replay for one endpoint.