Welcome to Typedpy’s documentation!¶
typedpy is a library for type-safe, strict, validated Python data structures, with serialization,
JSON Schema and IDE stub generation built in. A structure can never hold an invalid value: validation runs
when it is created and on every change afterwards, including changes inside nested lists and dicts.
It is pure Python with zero dependencies, supports Python 3.9-3.15, and has run in production, including financial systems, since 2017.
Installation¶
pip install typedpy
or conda install -c conda-forge typedpy.
Quick Start¶
import enum
from typing import Optional
from typedpy import (
ImmutableStructure, String, PositiveInt, Float, Enum,
Serializer, Deserializer, mappers,
)
class Currency(enum.Enum):
USD = 1
EUR = 2
class Trader(ImmutableStructure):
lei: String(pattern="[0-9A-Z]{18}[0-9]{2}$")
alias: String(maxLength=32)
_serialization_mapper = mappers.TO_CAMELCASE
class Trade(ImmutableStructure):
order_id: String
symbol: String(pattern="[A-Z]+$", maxLength=6)
quantity: PositiveInt(multiplesOf=5)
price: Float(minimum=0)
currency: Enum[Currency]
buyer: Trader
tags: list[str]
comment: Optional[str]
_serialization_mapper = mappers.TO_CAMELCASE
# deserialize (and validate) JSON-like input; keys are camelCase on the wire
trade = Deserializer(Trade).deserialize({
"orderId": "T-1001",
"symbol": "AAPL",
"quantity": 100,
"price": 231.5,
"currency": "USD",
"buyer": {"lei": "5493001KJTIIGC8Y1R12", "alias": "desk-7"},
"tags": ["equity"],
})
assert trade.currency is Currency.USD
Trade(order_id="T-1", symbol="AAPL", quantity=-5, price=1.0,
currency=Currency.USD, buyer=trade.buyer, tags=[])
# ValueError: Trade.quantity: Got -5; Expected a positive number
trade.quantity = 200 # ValueError: Trade: Structure is immutable
trade.tags.append("bond") # ValueError: tags: Field is immutable (deep immutability)
Serializer(trade).serialize()
# {'orderId': 'T-1001', 'symbol': 'AAPL', 'quantity': 100, ..., 'buyer': {'lei': ..., 'alias': 'desk-7'}}
Fields can be typedpy field types with constraints (String(maxLength=32)), plain Python types
(int, str), standard typing/PEP 585 annotations (list[str], Optional[...]), other
structures, or any mix of them at any depth, e.g. Array[dict[String(minLength=5), int]].
A mutable Structure is still validated on every change:
from typedpy import Structure
class Order(Structure):
quantity: PositiveInt
tags: list[str]
order = Order(quantity=5, tags=["a"])
order.quantity = -1 # ValueError: quantity: Got -1; Expected a positive number
order.tags.append(3) # TypeError: ... Expected a string
The Tutorial - Getting Started walks through a fuller example step by step.
Features¶
Deep immutability -
ImmutableStructureblocks reassignment and changes to nested lists, dicts and sets, and reads return defensive copies (Immutability).A field system that is plain object-oriented Python - constraints are mixins that combine through inheritance (Inheritance and Mixins), and any class can be used as a field (Implicit Wrapping of Arbitrary Classes As Field). Optional hooks plug a custom field into serialization, JSON Schema and stubs.
Discriminated unions - Discriminated Union picks the variant from a tag, using a real field reference rather than a string, and discovers variants automatically from subclasses.
Deriving models from existing ones -
Partial,AllFieldsRequired,Omit,PickandExtend, like TypeScript’s utility types (Alternative Methods for Structure reuse).Enum-keyed structures -
@keys_ofguarantees at import time that a structure has a field for every member of an enum (Ensuring Field Names Include All Possible Enum Values).Undefined vs None - an opt-in
Undefinedvalue distinct fromNone, e.g. for PATCH-style APIs (Differentiating Between Undefined values and None Values).Serialization - key mappers (camelCase, lowercase, renames, functions) that can be chained and compose through inheritance (Custom mapping in the deserialization), custom serialization (Serialization/Deserialization of a Field), and schema versioning (Support for Versioning).
Performance where it counts - Deserialization From Trusted Data and Trusted Instantiation skip validation for data you already trust, and Fast Serialization is several times faster, still in pure Python.
JSON Schema in both directions - generate a schema from structures, or structure code from a schema (JSON Schema Mapping).
IDE and type-checker support - generated
.pyistubs turn typedpy fields back into ordinary type hints, so any IDE or type checker understands your structures (Creating Python Stubs).Structured errors - errors can be collected and returned as structured objects (Well Formed Errors).
Why Pure Python?¶
Typedpy is deliberately pure Python with no dependencies: you get full stack traces, it can be debugged and audited end to end, there are no compiled builds, and the supply-chain surface is just this package. That suits regulated and correctness-critical settings.
The trade-off is speed (see Limitations of Typedpy). If validation dominates your workload, or you need Pydantic’s ecosystem, Pydantic is the better fit. Typedpy’s strengths are elsewhere: deep immutability, validation on every change, a simple extension model, and the features above. It also offers trusted deserialization and fast serialization for the hot paths.
Contents:¶
- Tutorial - Getting Started
- Structures
- The Basics - Usage
- Required Fields and Optional Fields
- Defaults
- Immutability
- Structure As Field
- Inlined Structure
- Structure Inheritance
- Equality Checks
- String output
- “Final” Structure
- Ignore None Values
- Other Magic Methods Support
- Cloning
- Combining with “Regular” Classes
- Validating a Structure As a Whole
- Alternative Syntax
- Support for PEP-604 Style
- Alternative Methods for Structure reuse
- Ensuring Field Names Include All Possible Enum Values
- Abstract Structure
- Defining inherited fields as constants
- Discriminated Union
- Differentiating Between Undefined values and None Values
- Trusted Instantiation
- Global Defaults
- Structure Documentation
- Fields
- The Basics - Usage
- Support For The Types In The “Typing” Module, and PEP-585
- Combining Python and Typedpy types in field definition
- Implicit Wrapping of Arbitrary Classes As Field
- Defining a Field as Optional
- Immutability
- Custom Serialization or Deserialization of a Field
- Extension and Utilities
- Defining a Field Independently
- Predefined Types
- Serialization
- The Basics - Usage
- Serialization of a Structure to a JSON that is not an object
- Deserialization of a non-object JSON to a Structure
- Serialization/Deserialization of a Field
- Custom Serialization
- Custom mapping in the deserialization
- Custom mapping in the serialization
- Custom Mappers as Part of the Class Definitions
- Strict Serialization and Deserialization API
- Deserialization From Trusted Data
- Transformations
- Deserialization Using a Discriminator Field
- Predefined Mappers
- Serializing Additional Values Beyond Fields
- Adding Type Of Class To Serialization
- Force a Field To Be Set As A Constant During Deserialization
- Enums Serialization/Deserialization
- Fast Serialization
- Classes
- Functions
- Support for Versioning
- JSON Schema Mapping
- Well Formed Errors
- Why not just use Dataclasses?
- Limitations of Typedpy
- Common Utilities
- Creating Python Stubs
- Frequently Asked Questions