Skip to main 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 for applications that want strict Python semantics, explicit external boundaries, immutable records, and standards-aware schemas without a required runtime dependency graph.

It is built for Python 3.14 and newer. An annotation is resolved once into one canonical contract, then Talea compiles specialized pure-Python operations for construction, external Mapping input, JSON input, Python output, JSON output, and schema projection.

from typing import Annotated
from uuid import UUID

from talea import Alias, MinLength, Sensitive, Spec


class Credentials(Spec):
    token: Annotated[str, Sensitive(), MinLength(16)]


class UserCreate(Spec):
    user_id: Annotated[UUID, Alias("id")]
    display_name: Annotated[str, Alias("displayName"), MinLength(1)]
    credentials: Credentials


request = UserCreate.from_json(
    """{
      "id": "12345678-1234-5678-1234-567812345678",
      "displayName": "Ada Lovelace",
      "credentials": {"token": "correct-horse-battery-staple"}
    }"""
)

assert request.user_id == UUID("12345678-1234-5678-1234-567812345678")
assert request.display_name == "Ada Lovelace"
assert "correct-horse-battery-staple" not in repr(request)

The conversion above is deliberately attached to from_json(). Ordinary Python construction is strict:

UserCreate(
    user_id="12345678-1234-5678-1234-567812345678",  # ValidationError
    display_name="Ada Lovelace",
    credentials=Credentials(token="correct-horse-battery-staple"),
)

A UUID field accepts a Python UUID on the trusted path. JSON has no UUID value, so the JSON boundary owns its documented string representation. That separation is the core of Talea's mental model: conversion is explicit, and already-valid Python values do not pass through a general parsing pipeline.

Migration-safe field names

APIs evolve, but accepting an old input spelling should not make output or conflict handling ambiguous. Alias can retain a finite set of historical Mapping and JSON input names while keeping one current external name:

class User(Spec):
    user_id: Annotated[int, Alias("userId", legacy=("id", "user_id"))]


assert User.from_mapping({"id": 1}).to_dict() == {"userId": 1}

Exactly one accepted spelling may be supplied. Current-plus-historical, historical-plus-historical, and equal-valued conflicts all fail with alias_conflict; output always uses userId. The same declaration composes through Specs, stdlib dataclasses, tagged unions, derived and partial Specs, JSON Schema, and OpenAPI. See field semantics for the complete contract.

Application-owned types can declare the same explicit boundary truth at any annotation position with Representation(input=..., load=..., output=..., dump=...); Talea validates callback results and reuses the declared schemas for input, output, JSON Schema, OpenAPI, and nested projection.

Why Talea exists

Python applications often need more than a typed record. At an API, event, configuration, or third-party boundary they need to answer all of these questions consistently:

  • Which Python values are valid without conversion?
  • Which external representations are accepted from Mapping and JSON input?
  • Where does hostile input receive finite work and error budgets?
  • What locations and stable codes does invalid nested data produce?
  • Which names and representations appear in serialized output?
  • Can a framework project the same contract as Draft 2020-12 JSON Schema or an OpenAPI 3.1 Schema Object?
  • Can tooling inspect the contract without reconstructing annotations?

Talea answers them from one canonical schema graph, while retaining separate execution paths for operations that have different trust and performance requirements.

“2026+” describes that starting point. Talea began with Python 3.14+, PEP 695 generics, deferred annotations, recursive type graphs, current typing behavior, and modern JSON Schema as architectural assumptions. It did not need to carry compatibility requirements for historical Python releases or retrofit those assumptions into an older public contract. This is a design circumstance—not a claim that mature libraries are obsolete, a prediction of ecosystem replacement, or a guarantee of future superiority.

The boundary model

Operation Use it when What it does
User(...) application code already has Python values strict, keyword-only construction
Contract(T).validate(value) an arbitrary root is already Python-shaped strict validation without conversion
User.from_mapping(data) an external Python Mapping represents an object structural conversion with finite traversal policy
Contract(T).from_python(data) an external root may be a list, union, alias, or TypedDict structural conversion with finite traversal policy
from_json(data) text or bytes crosses a serialized boundary strict decoding, JSON representations, conversion, and resource policy
to_dict() / to_python() an application needs detached Python output schema-aware projection and current-state validation
to_json() an application needs JSON text schema-aware projection followed by encoding
json_schema() / openapi_schema() tooling needs a standards description projection from the same canonical graph
@validate_call an application function is itself a typed boundary native Python binding plus strict argument and return validation

JSON and Mapping boundaries are not aliases for the constructor. For example, Decimal, UUID, temporal values, paths, IP values, bytes, enums, nested Specs, and tagged unions each retain an explicit Python contract and an explicit JSON representation.

What is implemented

Talea currently provides:

  • strict, keyword-only, immutable, slotted Spec records;
  • defaults and factories, inheritance, safe narrowing, custom transforms, field checks, whole-Spec checks, and serializers;
  • built-in numeric, length, and pattern constraints carried by Annotated;
  • aliases, titles, descriptions, examples, deprecation, read/write metadata, and sensitive-value handling;
  • Contract for primitives, containers, unions, TypedDict, type aliases, stdlib dataclasses, recursive graphs, tagged unions, and concrete generic specializations;
  • first-class Mapping and JSON input with structured nested errors;
  • finite transport-size, depth, traversal-node, and error-aggregation policy;
  • presence-aware partial Specs, derive_spec(), and apply_patch() for PATCH semantics where absent is not confused with None;
  • explicit input/output Spec views derived from ReadOnly and WriteOnly;
  • canonical discriminator-based union dispatch and OpenAPI discriminator maps;
  • Python and JSON serialization with explicit per-call codec boundaries and finite nested include/exclude selection;
  • reusable annotation-scoped Representation contracts for explicit custom domain input/output across runtime, schemas, introspection, and selection;
  • strict compiled synchronous and asynchronous callable boundaries with native binding, complete parameter grammar, methods, return validation, typing, and immutable introspection;
  • optional declared output= contracts for field serializers, while legacy undeclared serializer results remain opaque;
  • JSON Schema Draft 2020-12 and OpenAPI 3.1-compatible Schema Objects;
  • public immutable introspection and runtime create_spec() declarations;
  • compile-once specialized pure-Python execution with permanent benchmark canaries for distinct workloads.

The documentation proves these features with executable account API, REST PATCH, event, financial, recursive AST, arbitrary Contract, error/security, schema/OpenAPI, dynamic declaration, and immutable replacement examples.

A complete service boundary

A framework-neutral request flow looks like this:

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

Talea does not choose routes, dependency injection, HTTP status codes, ORM behavior, or response envelopes. A FastAPI, Lilya, Django, Starlette, Flask, or other adapter can own those framework concerns while calling the explicit Talea boundary operations. The manual includes the entire executable flow, plus presence-aware PATCH and generated input/output OpenAPI fragments.

Installation

Talea requires Python 3.14+. Install the published release from PyPI:

python -m pip install talea

To install from a source checkout instead:

git clone https://github.com/tarsil/talea.git
cd talea
python -m pip install .

The core package declares dependencies = []. Development, test, benchmark, build, and documentation tools remain separate development dependencies.

Not a competition

Talea is not trying to replace Pydantic, msgspec, dataclasses, attrs, or manually written validation.

Pydantic has broad adoption, extensive integrations, a mature ecosystem, and coercive/parsing workflows many applications actively want. msgspec has an extremely fast native implementation, mature serialization, and a different set of representation and performance tradeoffs. Dataclasses and attrs remain excellent for internal records that do not need a full external-boundary contract. Direct Python is often clearest for three checks in one specialized function.

Talea is another design point: strict, dependency-light, Python-native, compile-once, explicit-boundary, introspectable, standards-aware, and security-conscious. Selection is a requirements decision, not a winner/loser ranking.

When Talea fits—and when it does not

Talea is worth evaluating when a project uses Python 3.14+, wants strict ordinary Python construction, needs Mapping or JSON boundaries, values an empty required dependency graph, and can benefit from structured errors, finite external-input policy, schemas, or framework introspection.

It is likely the wrong choice when:

  • the application depends heavily on Pydantic-specific integrations or wants broad coercion by default;
  • Python 3.13 or earlier must remain supported;
  • settings, ORM extraction, or a large plugin ecosystem must come from the same package;
  • msgspec already exactly matches a high-throughput native serialization workflow;
  • the only requirement is a small internal record, where a dataclass or attrs class is simpler;
  • specialized validation is shorter and clearer as manually written Python;
  • adopting a 0.x library with an evolving API and small ecosystem is unacceptable.

Documentation

For a local checkout, task docs_test executes all docs_src examples and checks navigation, links, API inventory, and documentation policy. task build builds the site; task build_with_checks verifies release artifacts.

Maturity and evidence

Talea deliberately remains in the 0.x release series. Compatibility, deprecation, support, and release governance are not yet frozen, and its ecosystem is necessarily much smaller than mature alternatives. This is an ongoing product stage, not a signal that a 1.0 freeze is imminent.

Repository gates include unit and integration tests, 100% line coverage, linting, formatting, static typing, package checks, executable documentation, standards-conformance tests, security/adversarial cases, and 19 permanent benchmark workloads. Performance comparisons require semantically equivalent operations; no claim is based on removing validation from one side.

See Contributing for exact commands, Maturity and support for current governance, and Security for the technical threat model and reporting status.

Talea is licensed under the MIT License.

Metadata

Release files for talea 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for talea 0.5.0
File Size Uploaded
talea-0.5.0.tar.gz 134.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for talea 0.5.0
File Interpreter ABI Platform
talea-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 306.5 kB

Release files / talea-0.5.0.tar.gz

Download URL talea-0.5.0.tar.gz
Size 134.9 kB
Tags Source
SHA-256 checksum
How to use checksums
15dbd74c58afffc6fde206b0fa860f0fea66f1a22019b275fb59fae81807e0b0
BLAKE2b-256 checksum
How to use checksums
cb5b08766f4d71045d06fb27e89cc29ad264a800ee1e665e02fba1e921a7cbff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via Hatch/1.18.0 {"ci":true,"cpu":"x86_64","distro":{"id":"noble","libc":{"lib":"glibc","version":"2.39"},"name":"Ubuntu","version":"24.04"},"implementation":{"name":"CPython","version":"3.14.7"},"installer":{"name":"hatch","version":"1.18.0"},"openssl_version":"OpenSSL 3.0.13 30 Jan 2024","python":"3.14.7","system":{"name":"Linux","release":"6.17.0-1022-azure"}} HTTPX2/2.12.0

Release files / talea-0.5.0-py3-none-any.whl

Download URL talea-0.5.0-py3-none-any.whl
Size 171.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a31f9efa1227785965e0d305638f445fb7a5b740873b6bf56daeb1339f52b7e2
BLAKE2b-256 checksum
How to use checksums
b818dec59cfb3fecdeb749f15341334bd1f8edb9d19e34a49d2863cbb8989065
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via Hatch/1.18.0 {"ci":true,"cpu":"x86_64","distro":{"id":"noble","libc":{"lib":"glibc","version":"2.39"},"name":"Ubuntu","version":"24.04"},"implementation":{"name":"CPython","version":"3.14.7"},"installer":{"name":"hatch","version":"1.18.0"},"openssl_version":"OpenSSL 3.0.13 30 Jan 2024","python":"3.14.7","system":{"name":"Linux","release":"6.17.0-1022-azure"}} HTTPX2/2.12.0

Release history Release notifications | RSS feed

0.6.0

2 release files

This release

0.5.0 This release

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page