| name | python-adapters-success |
| description | Build the three boundary-crossing constructs — foreign model, contract model, ordered union — the only shapes for data crossing in or out of the program. Fires before touching a foreign payload, file format, message, or client reply; before declaring an API surface or response shape; or before writing a try/except, error handler, retry, fallback, or None-check around foreign input. |
Adapters
The boundary constructs: how data is trusted crossing in (foreign model, ordered union) and shaped crossing out (contract model). Distinct from python-values-success, whose constructs represent truth already inside the program.
Foreign Model
Definition
A frozen BaseModel of another system's data shape, named for the other system's thing: its aliases hold that system's keys, and its fields are this program's domain meanings. It exists only when the foreign shape differs from the domain shape, and it carries every field the program uses from the foreign data.
Required Form
class VenueFill(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
order_id: OrderId = Field(alias="ordId")
account_id: AccountId = Field(alias="acct")
fill_price: Price = Field(alias="px")
filled_quantity: Quantity = Field(alias="qty")
class VenueFillMessage(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
fill: VenueFill = Field(validation_alias="data")
class VenueStreamMessage(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
fill: VenueFill = Field(validation_alias=AliasPath("data", "payload"))
The declarative inventory: Field(alias=...) for a key rename, validation_alias for data wrapped at one key, AliasPath for data under nested wrapper keys, nested foreign models for nested structure, from_attributes=True for objects, model_validate_json for serialized data.
Sorting Rules
If the foreign shape already matches the domain model, construct the domain model directly; the constructor does the entire job and no foreign model exists. This program's own API shape is a contract model, never a foreign model. Foreign data that carries no identity and is expected to fail constructs through the ordered union. A live client is held by the consistency model, never on a foreign model.
Replaced Forms
A mapper, adapter, translator, DTO, or field-copying function restates work the constructor owns: the foreign model lifts whole in one call. A json.loads dict carries unproven data through the program. A before-validator that indexes, renames, routes, or computes is an alias, a path, or a nested model not yet written.
Construct-Specific Doctrine
The crossing takes the foreign data whole: model_validate on an arrived object, model_validate_json on arrived bytes, or keyword construction lifting a result's attributes. The foreign model carries every field the program uses, and nothing reads the foreign object after a model has been constructed from it. An omitted foreign key resolves at lifting: a default naming what omission means, or a union variant when omission means a different fact; bare None never crosses in. No coalesce mints data the wire did not carry. A foreign key holding a raw primitive validates into its semantic-scalar field: model_validate wraps the value and applies the scalar's constraint. Type the field as the scalar, never the primitive; never pre-wrap.
A foreign object graph you must walk yourself is a reported gap, but a converter that renders the graph whole as a tagged dict tree (an ast tree dumped to dicts, each tagged by its node kind) is a crossing: model_validate lifts the dict into a discriminated union keyed on the foreign tag, one frozen variant per node kind, with extra="ignore" for keys outside the modeled set. The walk is the converter's, not the program's.
Allowed Patterns
- a frozen
BaseModel, extra="forbid", every field a declared type, named for the foreign thing
Field(alias=...) for every rename, the aliases holding the foreign keys
- a nested foreign model for every nested foreign structure
validation_alias and AliasPath for transport wrappers, the wrapper modeled, never indexed past
from_attributes=True for objects; model_validate_json for serialized data
- an omitted key resolved by a named default or a variant
Forbidden
- a mapper, adapter, translator, DTO, or field-copying function
- a
json.loads result carried as a dict
- a before-validator that indexes, renames, routes, or computes; any
mode="after" validator
- a model named for a pipeline stage instead of the foreign thing
- an attribute read on a foreign object after its model was constructed
- a live client or handle retained as a field
Halt Rule
Halt when the foreign shape cannot be lifted by the declarative inventory, when lifting it would need a walk or computation you must write yourself (a converter that renders the graph whole is not that walk), or when the shapes match and a foreign model is still demanded. Report the row and the shape: the crossing is either unnecessary or unmodelable as declared, and the table is not finished.
Contract Model
Definition
A frozen BaseModel of this program's own API request or reply, composed of declared types in this program's vocabulary. No alias to another system's key appears on it.
Required Form
class OrderRequest(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
product: ProductId
side: OrderSide
quantity: Quantity
price: Price
class OrderReceipt(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
order_id: OrderId
A request that does not conform fails construction at the surface; nothing behind the route sees it. The reply leaves whole: the route serializes the contract model itself.
Sorting Rules
Composition direction separates the edge's two models: the contract model is ours, built of our types and names, projecting outward; the foreign model is theirs, named for their thing, its aliases holding their keys, lifting inward. A composite that is a domain fact rather than a surface shape is a concept model (python-values-success).
Replaced Forms
One model serving as both our API shape and another system's shape fuses our surface with their shape; build one of each. A hand-built response dict is an unproven shape leaving the program; the reply is the contract model itself, serialized at the route.
Allowed Patterns
- a frozen
BaseModel, extra="forbid", every field a declared type from this context or its peers
- a request contract a route constructs from raw transport data
- a reply contract constructed from proven facts and serialized whole in the route
@computed_field derivations when a derived fact is part of the published shape
Forbidden
- an alias to another system's key
- a foreign shape modeled as a contract
include, exclude, or by_alias on the reply's serialization; a different wire shape is its own contract model
- a contract field whose type is not one of this program's declared types
Halt Rule
Halt when the published shape would need a foreign alias, an undeclared type, or include, exclude, or by_alias on its serialization. Report the row and the field: either the shape belongs to a foreign model or the type is not yet modeled, and the table is not finished.
Ordered Union
Definition
Annotated[A | B, Field(union_mode="left_to_right")] over variants in attempt order, for foreign data that carries no identity and is expected sometimes to fail or to say no. The stronger construction is attempted first, the failure variant is last and composes from the input itself, and construction selects the case.
Required Form
class TapeText(RootModel[str], frozen=True):
"""The venue tape's frame text as received. Unconstrained on purpose: an unparseable frame is any text."""
root: str
class FrameKind(StrEnum):
TICK = "tick"
UNPARSEABLE = "unparseable"
class Tick(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
kind: Literal[FrameKind.TICK] = FrameKind.TICK
price: Price
quantity: Quantity
class Unparseable(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
kind: Literal[FrameKind.UNPARSEABLE] = FrameKind.UNPARSEABLE
raw: TapeText
@model_validator(mode="before")
@classmethod
def _wrap(cls, data: object) -> object:
return {"raw": data}
Frame = Annotated[Json[Tick] | Unparseable, Field(union_mode="left_to_right")]
FrameConstructor = TypeAdapter(Frame)
class TapeEntry(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid")
frame: Frame
Fed bytes that parse, Json decodes and Tick constructs; fed garbage, Json refuses and Unparseable composes from the input itself. As a field, frame: Frame admits garbage as a declared value.
A raising client, modeled the same way:
class LedgerKind(StrEnum):
BOOKED = "booked"
UNBOOKED = "unbooked"
class Booked(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
kind: Literal[LedgerKind.BOOKED] = LedgerKind.BOOKED
exposure: Exposure
class Unbooked(BaseModel):
model_config = ConfigDict(frozen=True, extra="forbid", from_attributes=True)
kind: Literal[LedgerKind.UNBOOKED] = LedgerKind.UNBOOKED
LedgerReply = Annotated[Booked | Unbooked, Field(union_mode="left_to_right")]
LedgerReplyConstructor = TypeAdapter(LedgerReply)
Fed the reply object, Booked constructs from its attributes. Fed the exception, Booked refuses, and Unbooked constructs from its pinned kind.
Sorting Rules
One question routes every failure: did the domain say no, or did the proof fail? A construction refusal proves nothing, is never modeled, and propagates; catching ValidationError manufactures the unproven value. A domain no is a value: this construct. An omission that means a fact is the absence doctrine at a foreign lift, not a failure. A transport-setup signal nothing in the domain reacts to belongs to the binding. Identity-carrying data constructs through the plain union's discriminator (python-values-success), never by attempt order.
Replaced Forms
A try/except producing a default, flag, or partial object manufactures the unproven value construction refused to make. A reply parser inspecting a foreign reply to choose a case restates the selection construction performs. A coalesce (x or default) manufactures a value nothing proved.
Construct-Specific Doctrine
The wrap. The one mode="before" validator the architecture admits: a single wrapping line on the failure variant that places the bare input under its field name, legal only with a recorded substrate run showing the declarative inventory refuses the shape. One return, constant keys, the input as every value. A marker handed back where it means a fact constructs an identity-only variant the same way.
The constructor. The constant beside the alias is the alias's declared constructor, the substrate's TypeAdapter, named for the alias it constructs. It defines no structure and decides nothing. As a field on a foreign model, the alias needs no constant: frame: Frame admits garbage as a declared value, the TapeEntry form above.
The capture. Python raises must be captured before construction can receive them. The capture lives in a verb: one call assigned, each declared exception assigned to the same variable, then ordered-union construction from that variable. The capture does not choose a variant, does not construct in an except arm, and an undeclared raise propagates as the refusal it is.
Allowed Patterns
- the
Annotated alias with union_mode="left_to_right", variants in attempt order, failure last
- the failure variant composing from the input itself through the proven one-line wrap
Json[...] as the stronger construction when the input is serialized
- the
TypeAdapter constant named for the alias
- the alias as a field on a foreign model
- the capture in a verb feeding the constructor
Forbidden
except ValidationError, anywhere
- a
try/except producing a default, flag, partial object, or domain variant
- a broad
except or a second statement in an except arm
x or default
- a reply parser: a function,
match, or isinstance deciding a reply's case
- a
RootModel class created to construct the alias
Halt Rule
Halt when a client produces a signal or marker not named by its interface, when the failure variant cannot compose from the input through the wrap, or when ordered selection is demanded for data that carries identity. Report the row and the signal: the wire's shapes are not fully modeled, and the table is not finished.