Skip to content

Talea

Talea

Data contracts, built for modern Python

Test Suite Package version Supported Python versions


Documentation: https://talea.tarsild.io πŸ“š

Source Code: https://github.com/tarsil/talea

The official supported version is always the latest released.


Talea is a 2026+ Python data-contract library built for strict Python semantics, explicit external boundaries, immutable records, and standards-aware schemas. It targets Python 3.14+, is implemented in pure Python, and declares zero required runtime dependencies.

An annotation becomes one canonical contract. Talea then compiles separate, specialized operations for trusted construction, external Mapping input, JSON input, Python output, JSON output, and standards projection. The result is a boundary model that remains visible in application code and inspectable by frameworks.

See the model in five minutes

"""Five-minute Talea quickstart used by the documentation."""

from typing import Annotated, cast

from talea import MinLength, Spec, ValidationError


class User(Spec):
    id: int
    name: Annotated[str, MinLength(1)]
    active: bool = True


user = User(id=1, name="Ada")
assert user.to_dict() == {"id": 1, "name": "Ada", "active": True}

decoded = User.from_json('{"id":2,"name":"Grace","active":false}')
assert decoded.to_dict() == {"id": 2, "name": "Grace", "active": False}

try:
    User(id=cast(int, "1"), name="Ada")
except ValidationError as error:
    assert error.errors()[0]["code"] == "type"
    assert error.errors()[0]["location"] == ["id"]
else:
    raise AssertionError("strict construction must reject a string identifier")

schema = User.json_schema()
assert schema["$schema"] == "https://json-schema.org/draft/2020-12/schema"

The direct constructor is strict: a field declared as int accepts an exact Python integer, not "1", 1.0, or True. from_json() is deliberately a different operation. It understands the documented JSON representations of values such as UUID, datetime, Decimal, IP addresses, paths, and bytes, then validates the resulting contract.

That distinction prevents conversion from becoming an invisible property of every Python assignment. Application code can see where external data entered, where finite resource limits applied, and where an already-valid object took a short trusted path.

The problem Talea addresses

Typed storage is only one part of a production data boundary. An API request, event message, third-party payload, or persistence document also needs:

  • predictable input conversion rather than ambient coercion;
  • nested locations and stable machine-readable errors;
  • safe handling for rejected sensitive values;
  • finite work on oversized, deep, broad, or highly invalid external input;
  • serialization that agrees with validation and aliases;
  • JSON Schema and OpenAPI projection that describe implemented behavior;
  • introspection that frameworks can consume without repeating annotation resolution;
  • a performance model that does declaration work once and keeps unused features off simple hot paths.

Talea treats those as related projections of one contract, not as independent subsystems free to reinterpret the fields.

flowchart LR
    A[Python annotations] --> B[Canonical schema graph]
    B --> C[Strict construction]
    B --> D[Mapping input]
    B --> E[JSON input]
    B --> F[Python and JSON output]
    B --> G[JSON Schema and OpenAPI]
    B --> H[Introspection]

A realistic boundary

A framework-neutral service can place Talea at the exact transport seam:

raw request bytes
    -> ResourcePolicy
    -> UserCreate.from_json(...)
    -> ValidationError or ResourceLimitError
    -> application/domain operation
    -> UserResponse
    -> to_json()

The production service tutorial makes that complete flow executable with nested account data, aliases, constraints, credentials, redaction, invalid payloads, resource rejection, response serialization, and input/output OpenAPI fragments. It does not require or pretend to be a web framework: route registration and HTTP policy remain with FastAPI, Lilya, Django, Starlette, Flask, or the embedding system.

For partial updates, Talea keeps presence as first-class truth:

UserPatch = derive_spec(User, partial=True)
patch = UserPatch.from_json('{"displayName":"Grace"}')

assert patch.present_fields == frozenset({"display_name"})
updated = apply_patch(existing_user, patch)

An absent field is not rewritten as None, and a value equal to a default is still present when explicitly supplied. The PATCH guide covers aliases, defaults, sensitive fields, empty patches, failed whole-object invariants, serialization, and schemas.

Two declaration surfaces, one contract system

Use Spec for a named, immutable record with attributes, defaults, methods, inheritance, and validation hooks. Use Contract when the useful root already has another shape:

from decimal import Decimal
from uuid import UUID

from talea import Contract


identifiers = Contract[list[UUID]](list[UUID])
balances = Contract[dict[str, Decimal]](dict[str, Decimal])

ids = identifiers.from_json(
    '["12345678-1234-5678-1234-567812345678"]'
)
amounts = balances.from_json('{"CHF":"42.50"}')

Contract also covers stdlib dataclasses, TypedDicts, tagged unions, PEP 695 aliases, recursive graphs, and concrete generic specializations. It exposes validate, from_python, from_json, to_python, to_json, json_schema, and openapi_schema without requiring a wrapper class. See Arbitrary contracts for the full boundary matrix and production examples.

What β€œ2026+” means

Talea asks what a Python data-contract library should look like if it were designed today, for modern Python, without inherited compatibility constraints. Starting with Python 3.14 allows PEP 695 generic syntax, deferred annotations, current typing/runtime behavior, and recursive type graphs to be architectural assumptions from the beginning rather than optional compatibility layers.

It does not mean guaranteed future superiority, a prediction that the ecosystem will replace mature libraries, that other libraries are β€œlegacy,” or that Talea will always be faster. Mature projects serve public contracts and version ranges that Talea never had to preserve. That is a design circumstance, not a criticism. Why Talea? develops the technical consequences in detail.

What is included

Area Implemented surface
Records strict keyword-only immutable slotted Specs, defaults/factories, inheritance, generics, recursion
Validation exact Python types, constraints, transforms, field and whole-Spec checks, structured errors
Boundaries trusted Python, external Mapping, strict JSON, arbitrary Contract roots
Composition nested Specs, stdlib dataclasses, TypedDict, aliases, represented custom domain types, unions, canonical tagged dispatch, recursive named graphs
Updates copy.replace, derive_spec, presence-aware partials, apply_patch
Output detached Python projection, JSON representations, nested selection, field serializers with optional declared output truth, per-call codec boundaries
Security Sensitive-aware failure redaction; finite transport, depth, work, and error budgets
Standards JSON Schema Draft 2020-12 and OpenAPI 3.1-compatible Schema Objects
Tooling immutable field/schema introspection and dynamic create_spec declarations
Execution compile-once specialized pure-Python operations and permanent benchmark canaries

Not a competition

Talea is not trying to replace Pydantic, msgspec, dataclasses, attrs, or manually written validation. Different projects make different tradeoffs.

  • Pydantic brings broad adoption, extensive integrations, a mature ecosystem, and coercive/parsing workflows many applications actively want.
  • msgspec brings an extremely fast native implementation, mature serialization, and representation/performance choices that may already fit a workload.
  • dataclasses and attrs remain excellent for internal records; a stdlib dataclass can also retain that role behind a Talea Contract boundary.
  • direct Python remains the clearest answer for a sufficiently small or specialized contract.

Talea offers another design point: strict, dependency-light, Python-native, compile-once, explicit-boundary, introspectable, standards-aware, and security-conscious. The comparison and adoption guide use concrete scenarios rather than a winner/loser ranking.

When Talea is a fit

Evaluate Talea when a Python 3.14+ system needs predictable request or event boundaries, wants conversion to be explicit, values an empty required runtime dependency graph, and can use structured failures, security budgets, schemas, or introspection. Its compile-once model is especially relevant when the same contract executes repeatedly after application startup.

Do not choose Talea only because it is newer. It is probably not a good fit when an application depends deeply on Pydantic-specific integrations, must run on older Python, wants broad coercion, needs settings/ORM ecosystems in one package, already has an ideal msgspec workflow, needs only a tiny internal record, or can express a specialized check more clearly by hand.

Talea deliberately remains in the 0.x series and has a much smaller ecosystem than mature alternatives. Its compatibility and support policy may continue to evolve across 0.x releases. The limitations and maturity/support page make that cost explicit.

Choose a path

Goal Start here Then continue to
Build a first contract Quickstart Progressive tutorial
Integrate an API boundary Production service Input, errors, security
Model partial updates PATCH and presence derive_spec / apply_patch API
Consume event messages Tagged unions Recursion/generics, schemas
Validate arbitrary roots Contract Supported types
Represent custom domain types Custom representations Serialization, schemas
Build framework tooling Introspection OpenAPI, architecture
Perform security review Resource and security model Security summary
Evaluate adoption Why Talea? Comparison, enterprise questions
Reproduce evidence Performance Contributing

Every substantial flow is owned by an executable docs_src program. The docs gate runs those examples with assertions and also checks navigation, links, public API inventory, headings, and documentation policy.