Adaptix Documentation

repository·main·Indexed 20 days ago

https://github.com/reagento/adaptix

A high-performance Python library for data model conversion and serialization. Adaptix supports transformations between dataclasses, Pydantic models, SQLAlchemy entities, TypedDict, NamedTuple, attrs, and msgspec. It features the Retort class for loading and dumping data, get_converter for model-to-model transformation, and a flexible predicate system for precise behavior overrides.

Tokens
20.4K
Snippets
69
Records
107
Agent score
70%

What's inside Adaptix

  1. Introduction to Adaptix Conversion

    main

    Adaptix is designed to automate the generation of converter functions between different data models (e.g., transforming a Domain Model into a DTO). This avoids writing boilerplate code when passing data between application layers.

    Core Mechanics:

    • Adaptix scans each field of the destination model and attempts to match it with a field in the source model.
    • By default, matching is based on exact name equivalence.
    • It supports nested models automatically.
    • Converter signatures are automatically derived by IDEs and type checkers.
    # Example of a generated converter
    def convert_book_to_dto(book: Book) -> BookDTO:
        ...
  2. Overview of Adaptix capabilities

    main

    Adaptix is a high-performance data model conversion library. Key features include:

    • Broad Type Support: Works with @dataclass, TypedDict, NamedTuple, attrs, sqlalchemy, pydantic, and msgspec.
    • Performance: Optimized for speed in parsing and serialization.
    • Flexibility: Supports automatic name style conversion (e.g., snake_case to camelCase), self-referenced data types (trees, linked lists), and user-defined generic models.
    • Robustness: Provides machine-readable errors and tracks the path where exceptions occur during loading.
    • Control: Features a predicate system for precise behavior overrides and allows disabling checks for trusted data sources to increase speed.
    • Strictness: No auto-casting by default; the loader does not guess types from input formats unless configured.
  3. Key advantages of using Adaptix

    main

    Adaptix is a high-performance data parsing and serialization library designed for flexibility and speed. Key features include:

    • High Performance: One of the fastest libraries for data parsing and serialization.
    • Flexible Model Support: Supports various model kinds including @dataclass, TypedDict, NamedTuple, attrs, sqlalchemy, pydantic, and msgspec.
    • Decoupled Logic: Separates model definitions from conversion rules, adhering to the Single Responsibility Principle (SRP) and allowing multiple representations for a single model.
    • Advanced Type Handling: Supports self-referenced data types (e.g., trees, linked lists) and user-defined generic models.
    • Robust Error Handling: Provides machine-readable errors that can be dumped and ensures paths are saved where exceptions occur.
    • Customization: Features a predicate system for precise behavior overrides and automatic name style conversion (e.g., snake_case to camelCase).
    • Safety and Control: No auto-casting by default (prevents guessing input formats) and allows disabling additional checks to maximize speed when loading from trusted sources.
  4. Real-world integration scenarios for Adaptix

    main

    The examples/real_world_app directory demonstrates how to integrate adaptix into production-grade Python applications. Key patterns covered include:

    • Environment-based Configuration: Loading application settings from environment variables using config.py.
    • Caching with External Storage: Implementing cache storage using Redis-like systems in user_gateway.py.
    • Model Transformation: Converting SQLAlchemy database models to Pydantic models in routes.py.
    • Direct Serialization: Serializing SQLAlchemy models directly in routes.py.
    • Transparent JSON Handling: Managing JSON storage in a database with automatic parsing and dumping in db_models.py.
  5. Common use cases for Adaptix

    main

    Adaptix is a flexible data model conversion library suitable for several patterns:

    • API Data Handling: Validation and transformation of received data for APIs.
    • Model Mapping: Conversion between internal data models and Data Transfer Objects (DTOs).
    • Configuration: Loading and dumping configuration via codecs that produce or consume dictionaries.
    • Database Integration: Representing JSON stored in databases as application models, or implementing fast, primitive ORMs.
    • Client/Server Communication: Creating API clients that convert models to JSON for transmission.
    • Caching: Persisting entities in cache storage.
  6. Supported model kinds in Adaptix

    main

    Adaptix provides out-of-the-box support for several Python model types. You do not need to enable support manually; it works automatically via introspection. To ensure version compatibility, you can install Adaptix with specific extras.

    Supported models:

    • dataclass
    • NamedTuple (Note: standard collections.namedtuple is supported, but all field types will be treated as Any)
    • TypedDict
    • attrs (requires version >=21.3.0)
    • sqlalchemy (requires version >=2.0.0)
    • pydantic (requires version >=2.0.0)
    • msgspec (requires version >=0.14.0)

    Note on Arbitrary Types: Arbitrary types can be loaded via __init__ method introspection, but they cannot be dumped.

  7. Use msgspec integration for high-performance models

    main

    As of version 3.0.0b10, Adaptix supports msgspec models. You can treat msgspec models like any other model (constructing from a dict, serializing to a dict, or converting to other models).

    To achieve maximum performance, use integrations.msgspec.native_msgspec to delegate loading and dumping operations directly to msgspec itself, combining Adaptix's flexibility with msgspec's speed.

    # Example concept (requires msgspec installed)
    from adaptix.integrations.msgspec import native_msgspec
    
    # Use native_msgspec to delegate loading/dumping to msgspec
    # for high-performance workflows.
  8. Chain and override name_mappings

    main

    Multiple name_mapping configurations can be chained. The resulting configuration is computed by merging all parameters of matched name_mapping calls.

    Precedence: The first provider (the one defined earliest in the chain) overrides parameters of subsequent providers.

    Note on map: Unlike other parameters, a new map does not replace the previous one; instead, the new iterable is concatenated to the previous one.

  9. Handle recursive data types

    main

    Recursive data types (where a class contains an instance of itself) can be loaded and dumped by Adaptix without additional configuration.

    Limitation: This does not support cyclic-referenced objects (e.g., an object that contains a reference to itself in a list or attribute).

    from typing import List
    
    class Node:
        def __init__(self, name: str, children: List['Node'] = None):
            self.name = name
            self.children = children or []
    
    # This works fine
  10. Implement API division with outer and inner retorts

    main

    To optimize performance and security, you can implement different representations of a single model using separate Retort instances. This pattern is known as API division:

    1. Outer Retort (outer_receipt_retort): Use this for loading data from untrusted sources (e.g., external API users). It includes comprehensive validations to ensure data integrity.
    2. Inner Retort (inner_receipt_retort): Use this for internal service-to-service communication. It contains fewer validations, which speeds up data loading and dumping processes.

    In production, these retorts should ideally reside in your Interface Adapters layer to maintain clean architecture boundaries.

  11. How NewType and Metadata types are processed

    main

    NewType

    All NewType definitions are treated as their origin types. For example, MyNewModel = NewType('MyNewModel', MyModel) will inherit the loader, dumper, and name_mapping of MyModel. You can only override providers for a NewType if you pass the NewType itself directly as a predicate.

    Metadata Types

    Types used for type hinting metadata, such as Final, Annotated, ClassVar, and InitVar, are processed identically to the wrapped types they contain.

  12. Handle loading errors with AggregateLoadError and Struct trails

    main

    When loading data, Adaptix loaders signal invalid input data by throwing a LoadError. By default, all errors encountered during the loading process are collected into an AggregateLoadError.

    Each error includes a Struct trail (similar to JSONPath) that points to the exact location in the input data where the error occurred. You can access this trail using get_trail(exception).

    Note: For Python versions < 3.11, the exceptiongroup package is used to provide ExceptionGroup functionality.

    from adaptix.struct_trail import get_trail
    
    try:
        # loading logic here
        ...
    except LoadError as e:
        trail = get_trail(e)
        print(f"Error at {trail}")