### Install Adaptix Source: https://adaptix.readthedocs.io/en/latest/_sources/overview.rst.txt Install a specific beta version of Adaptix using pip. ```text pip install adaptix==3.0.0b12 ``` -------------------------------- ### Model Loading and Dumping Example Source: https://adaptix.readthedocs.io/en/latest/_sources/overview.rst.txt Demonstrates how to load and dump models using Adaptix. This example is useful for basic data serialization and deserialization tasks. ```python from dataclasses import dataclass from adaptix import Retort @dataclass class Point: x: int y: int retort = Retort() point = retort.load(Point, {"x": 1, "y": 2}) print(point) print(retort.dump(point)) ``` -------------------------------- ### Comprehensive Conversion Example Source: https://adaptix.readthedocs.io/en/latest/_sources/conversion/tutorial.rst.txt An example that integrates multiple features of the conversion library, showcasing complex data handling and transformations. ```python from datetime import datetime from typing import List, Optional from adaptix import ConversionError, Converter, Retort from adaptix.conversion import Link class Event: def __init__(self, name: str, timestamp: datetime, participants: List[str], details: Optional[dict] = None): self.name = name self.timestamp = timestamp self.participants = participants self.details = details def process_events(converter: Converter, events_data: List[dict]) -> List[Event]: return converter.convert(events_data) converter = Converter() retort = Retort() # Add a link to convert string to datetime converter.add_link(Link(str, datetime, coercer=datetime.fromisoformat)) # Add a link to convert list of strings to list of uppercase strings converter.add_link(Link(List[str], List[str], coercer=lambda x: [s.upper() for s in x])) # Add a link to convert dict to Event object converter.add_link(Link(dict, Event, retort=retort)) events_data = [ { "name": "Meeting", "timestamp": "2023-10-27T10:00:00", "participants": ["Alice", "Bob"], "details": {"location": "Room 1"} }, { "name": "Conference", "timestamp": "2023-10-28T09:30:00", "participants": ["Charlie", "David", "Eve"] } ] try: processed_events = process_events(converter, events_data) for event in processed_events: print(f"Event: {event.name}") print(f" Timestamp: {event.timestamp}") print(f" Participants: {event.participants}") if event.details: print(f" Details: {event.details}") except ConversionError as e: print(f"Conversion error: {e}") ``` -------------------------------- ### Install Adaptix with extras Source: https://adaptix.readthedocs.io/en/latest/conversion/tutorial.html Install Adaptix with specific extras for integrations like attrs, sqlalchemy, pydantic, or msgspec. Strict versions can also be specified. ```bash pip install adaptix[attrs-strict]==3.0.0b12 pip install adaptix[attrs, sqlalchemy-strict]==3.0.0b12 ``` -------------------------------- ### Install adaptix with extras Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/tutorial.html Install adaptix with specific extras for integrations with libraries like attrs, sqlalchemy, pydantic, and msgspec. Use '-strict' variants for version compatibility checks. ```bash pip install adaptix[attrs-strict]==3.0.0b12 pip install adaptix[attrs, sqlalchemy-strict]==3.0.0b12 ``` -------------------------------- ### Simple Generic Class Example Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/extended-usage.rst.txt Demonstrates the basic usage of generic classes with Adaptix. Ensure the generic class is correctly defined and used. ```python from typing import TypeVar, Generic T = TypeVar('T') class SimpleGeneric(Generic[T]): def __init__(self, value: T) -> None: self.value = value class ExtendedUsage(BaseModel): generic_field: SimpleGeneric[int] # Example usage: instance = ExtendedUsage(generic_field=SimpleGeneric(value=123)) ``` -------------------------------- ### New Error Rendering System Example Source: https://adaptix.readthedocs.io/en/latest/reference/changelog.html Demonstrates the new compact and clear error rendering for loader generation in adaptix. ```text Traceback (most recent call last): ... adaptix.ProviderNotFoundError: Cannot produce loader for type × Cannot create loader for model. Loaders for some fields cannot be created │ Location: ‹Book› ╰──▷ Cannot create loader for model. Cannot fetch `InputNameLayout` │ Location: ‹Book.author: Person› ╰──▷ Required fields ['last_name'] are skipped ``` -------------------------------- ### Old Error Rendering System Example Source: https://adaptix.readthedocs.io/en/latest/reference/changelog.html Shows the previous verbose error rendering for loader generation issues in adaptix. ```text | adaptix.AggregateCannotProvide: Cannot create loader for model. Loaders for some fields cannot be created (1 sub-exception) | Location: `Book` +-+---------------- 1 ---------------- | adaptix.AggregateCannotProvide: Cannot create loader for model. Cannot fetch InputNameLayout (1 sub-exception) | Location: `Book.author: Person` +-+---------------- 1 ---------------- | adaptix.CannotProvide: Required fields ['last_name'] are skipped | Location: `Book.author: Person` +------------------------------------ The above exception was the direct cause of the following exception: Traceback (most recent call last): ... adaptix.ProviderNotFoundError: Cannot produce loader for type Note: The attached exception above contains verbose description of the problem ``` -------------------------------- ### Basic Loading and Dumping with Retort Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/tutorial.rst.txt Demonstrates the fundamental usage of the Retort object for loading data into Python objects and dumping objects back into dictionaries. This is a quick start example. ```python from dataclasses import dataclass from adaptix import Retort @dataclass class Point: x: int y: int retort = Retort() point = retort.load(Point, {"x": 1, "y": 2}) print(point) data = retort.dump(point) print(data) ``` -------------------------------- ### Retort Recipe Introduction Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/tutorial.rst.txt Introduces the Retort recipe system for advanced configuration. This example shows how to override default behaviors, like the datetime loader, using a lambda function. ```python from datetime import datetime, timezone from adaptix import Retort retort = Retort( recipe=[loader(datetime, lambda x: datetime.fromtimestamp(x, tz=timezone.utc))] ) data = 1678886400 # Unix timestamp datetime_obj = retort.load(datetime, data) print(datetime_obj) ``` -------------------------------- ### New Error Formatting Example Source: https://adaptix.readthedocs.io/en/latest/reference/changelog.html Demonstrates the improved and more compact error message format introduced in adaptix. ```text adaptix.ProviderNotFoundError: Cannot produce dumper for type × Cannot create dumper for model. Dumpers for some fields cannot be created │ Location: ‹Foo› ╰──▷ Cannot create dumper for model. Dumpers for some fields cannot be created │ Location: ‹Foo.limit: MinMax[Bar]› ├──▷ Cannot create dumper for union. Dumpers for some union cases cannot be created │ │ Location: ‹MinMax[Bar].min: Optional[Bar]› │ ╰──▷ Cannot find dumper │ Location: ‹Bar› ╰──▷ Cannot create dumper for union. Dumpers for some union cases cannot be created │ Location: ‹MinMax[Bar].max: Optional[Bar]› ╰──▷ Cannot find dumper Location: ‹Bar› ``` -------------------------------- ### Provider Priority with Multiple Loaders Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/tutorial.html Illustrates how providers at the beginning of the recipe have higher priority. This example shows two loaders for `int`, where the first one (`add_one`) is applied because it appears earlier in the recipe list. ```python from dataclasses import dataclass from adaptix import Retort, loader @dataclass class Foo: value: int def add_one(data): return data + 1 def add_two(data): return data + 2 retort = Retort( recipe=[ loader(int, add_one), loader(int, add_two), ], ) assert retort.load({"value": 10}, Foo) == Foo(11) ``` -------------------------------- ### Model Conversion Example Source: https://adaptix.readthedocs.io/en/latest/_sources/overview.rst.txt Illustrates how to convert one model to another using Adaptix. This is useful for transforming data between different structures, such as between DTOs and internal models. ```python from dataclasses import dataclass from adaptix import Retort @dataclass class Point: x: int y: int @dataclass class PointDTO: x: int y: int retort = Retort() point_dto = retort.load(PointDTO, {"x": 1, "y": 2}) point = retort.convert(Point, point_dto) print(point) print(retort.dump(point)) ``` -------------------------------- ### Old Error Formatting Example Source: https://adaptix.readthedocs.io/en/latest/reference/changelog.html Illustrates the previous error message format for type-related issues in adaptix. ```text adaptix.ProviderNotFoundError: Cannot produce dumper for type × Cannot create dumper for model. Dumpers for some fields cannot be created │ Location: ‹Foo› ╰──▷ Cannot create dumper for model. Dumpers for some fields cannot be created │ Location: ‹Foo.limit: __main__.MinMax[__main__.Bar]› ├──▷ Cannot create dumper for union. Dumpers for some union cases cannot be created │ │ Location: ‹MinMax[Bar].min: Optional[Bar]› │ ╰──▷ Cannot find dumper │ Location: ‹Bar› ╰──▷ Cannot create dumper for union. Dumpers for some union cases cannot be created │ Location: ‹MinMax[Bar].max: Optional[Bar]› ╰──▷ Cannot find dumper Location: ‹Bar› ``` -------------------------------- ### Run Linting and Testing Source: https://adaptix.readthedocs.io/en/latest/_sources/reference/contributing.rst.txt Execute the main commands to verify the project setup. 'just lint' runs code linters, and 'just test-all' runs all tests across all Python versions. ```bash just lint just test-all ``` -------------------------------- ### Custom Sentinel Type Example Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/extended-usage.rst.txt Shows how to create and use a custom sentinel type using `as_sentinel` for detecting omitted fields. ```python from adaptix.type_checking import as_sentinel from adaptix.models import BaseModel MY_SENTINEL = as_sentinel('MY_SENTINEL') class ModelWithCustomSentinel(BaseModel): custom_field: int = MY_SENTINEL class Config: omit_default = True # Example usage: instance = ModelWithCustomSentinel() # instance.custom_field will be MY_SENTINEL ``` -------------------------------- ### Retort Recipe Introduction - Dumper Example Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/tutorial.rst.txt Demonstrates overriding the default datetime dumper using a lambda function within a Retort recipe. This allows custom serialization formats. ```python from datetime import datetime, timezone from adaptix import Retort retort = Retort( recipe=[dumper(datetime, lambda x: x.timestamp())] ) datetime_obj = datetime(2023, 3, 15, 12, 0, 0, tzinfo=timezone.utc) data = retort.dump(datetime_obj) print(data) ``` -------------------------------- ### Pydantic Model Mapping Example Source: https://adaptix.readthedocs.io/en/latest/_sources/why-not-pydantic.rst.txt Shows a basic example of model mapping in Pydantic using 'from_attributes=True', where Pydantic accesses object attributes instead of dictionary keys for validation. ```python from pydantic import BaseModel, ConfigDict class TargetModel(BaseModel): model_config = ConfigDict(from_attributes=True) field_a: str field_b: int class SourceModel: def __init__(self, field_a: str, field_b: int): self.field_a = field_a self.field_b = field_b source = SourceModel(field_a="hello", field_b=123) target = TargetModel.model_validate(source) print(target.field_a, target.field_b) ``` -------------------------------- ### Using Predicate System with P for Data Loading Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/tutorial.html Demonstrates how to use the P (pattern) object to specify predicates for loading data, particularly for handling datetime fields with different formats. This example shows loading a Bookshop object from a dictionary. ```python from dataclasses import dataclass from datetime import datetime, timezone from adaptix import P, Retort, loader @dataclass class Person: id: int name: str created_at: datetime @dataclass class Book: name: str price: int created_at: datetime @dataclass class Bookshop: workers: list[Person] books: list[Book] data = { "workers": [ { "id": 193, "name": "Kate", "created_at": "2023-01-29T21:26:28.026860+00:00", }, ], "books": [ { "name": "Fahrenheit 451", "price": 100, "created_at": 1674938508.599962, }, ], } retort = Retort( recipe=[ loader(P[Book].created_at, lambda x: datetime.fromtimestamp(x, tz=timezone.utc)), ], ) bookshop = retort.load(data, Bookshop) assert bookshop == Bookshop( workers=[ Person( id=193, name="Kate", created_at=datetime(2023, 1, 29, 21, 26, 28, 26860, tzinfo=timezone.utc), ), ], books=[ Book( name="Fahrenheit 451", price=100, created_at=datetime(2023, 1, 28, 20, 41, 48, 599962, tzinfo=timezone.utc), ), ], ) ``` -------------------------------- ### Complex Data Conversion with Multiple Coercions and Links Source: https://adaptix.readthedocs.io/en/latest/conversion/tutorial.html An advanced example demonstrating how to combine various conversion features: linking parameters to fields, renaming fields, coercing types (UUID to str, date to str), and ignoring fields. ```python # mypy: disable-error-code="empty-body" from dataclasses import dataclass from datetime import date from uuid import UUID from adaptix import P from adaptix.conversion import coercer, from_param, impl_converter, link @dataclass class Author: name: str surname: str birthday: date # is converted to str @dataclass class Book: id: UUID # is converted to str title: str author: Author # is renamed to `writer` isbn: str # this field is ignored @dataclass class AuthorDTO: name: str surname: str birthday: str @dataclass class BookDTO: id: str title: str writer: AuthorDTO page_count: int # is taken from `pages_len` param rating: float # is taken from param with the same name @impl_converter( recipe=[ link(from_param("pages_len"), P[BookDTO].page_count), link(P[Book].author, P[BookDTO].writer), coercer(UUID, str, func=str), coercer(P[Author].birthday, P[AuthorDTO].birthday, date.isoformat), ], ) def convert_book_to_dto(book: Book, pages_len: int, rating: float) -> BookDTO: ... assert ( convert_book_to_dto( book=Book( id=UUID("87000388-94e6-49a4-b51b-320e38577bd9"), isbn="978-0-7432-4722-1", title="Fahrenheit 451", author=Author(name="Ray", surname="Bradbury", birthday=date(1920, 7, 22)), ), pages_len=158, rating=4.8, ) == BookDTO( id="87000388-94e6-49a4-b51b-320e38577bd9", title="Fahrenheit 451", writer=AuthorDTO(name="Ray", surname="Bradbury", birthday="1920-07-22"), page_count=158, rating=4.8, ) ) ``` -------------------------------- ### Ignoring Extra Data During Dumping with ExtraSkip Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/extended-usage.html By default, extra data is ignored during dumping (`ExtraSkip`). This example shows how to load data with extra fields and then dump it, demonstrating that the extra fields are not included in the output. ```python from collections.abc import Mapping from dataclasses import dataclass from typing import Any from adaptix import Retort, name_mapping @dataclass class Book: title: str price: int extra: Mapping[str, Any] data = { "title": "Fahrenheit 451", "price": 100, "unknown1": 1, "unknown2": 2, } retort = Retort( recipe=[ name_mapping(Book, extra_in="extra"), ], ) book = retort.load(data, Book) assert book == Book( title="Fahrenheit 451", price=100, extra={ "unknown1": 1, "unknown2": 2, }, ) assert retort.dump(book) == { "title": "Fahrenheit 451", "price": 100, "extra": { "unknown1": 1, "unknown2": 2, }, } ``` -------------------------------- ### Bootstrap Development Environment Source: https://adaptix.readthedocs.io/en/latest/_sources/reference/contributing.rst.txt Initialize the virtual environment and repository for development. This command sets up all necessary dependencies and configurations. ```bash just bootstrap ``` -------------------------------- ### Build Documentation Source: https://adaptix.readthedocs.io/en/latest/_sources/reference/contributing.rst.txt Generate HTML files for the project documentation. The output will be placed in the 'docs-build/html' directory. ```bash just doc ``` -------------------------------- ### Provider Chaining with Chain.FIRST Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/tutorial.rst.txt Demonstrates how to use provider chaining with Chain.FIRST to process data before the next matched loader/dumper. The result of the current function is passed to the subsequent provider. ```python from adaptix.load_dump import loader, Chain @loader(int, Chain.FIRST) def process_int(data: int) -> int: return data + 1 ``` -------------------------------- ### Default Private Field Dumping Behavior Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/extended-usage.html Shows that Adaptix, by default, excludes private fields (those starting with an underscore) when dumping objects. ```python from adaptix import Retort from pydantic import BaseModel class Book(BaseModel): title: str price: int _private: int def __init__(self, **kwargs): super().__init__(**kwargs) self._private = 1 retort = Retort() book = Book(title="Fahrenheit 451", price=100) assert retort.dump(book) == { "title": "Fahrenheit 451", "price": 100, } ``` -------------------------------- ### Skipping Private Fields with Pydantic Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/extended-usage.rst.txt Shows how Adaptix skips private fields (starting with underscore) by default when dumping Pydantic models. ```python from adaptix.load_dump import Schema from pydantic import BaseModel class MyModel(BaseModel): a: int _b: int schema = Schema() print(schema.dump_to_dict(MyModel(a=1, _b=2))) # Output: {'a': 1} ``` -------------------------------- ### Runtime Type Hint Resolution Failure Source: https://adaptix.readthedocs.io/en/latest/conversion/extended-usage.html Demonstrates the `NameError` that occurs when trying to get type hints at runtime for models with `if TYPE_CHECKING` imports. ```python from typing import get_type_hints from .chat import Chat from .message import Message try: get_type_hints(Chat) except NameError as e: assert str(e) == "name 'Message' is not defined" try: get_type_hints(Message) except NameError as e: assert str(e) == "name 'Chat' is not defined" ``` -------------------------------- ### Retort Introduction - Adaptix Source: https://adaptix.readthedocs.io/en/latest/_sources/conversion/extended-usage.rst.txt Introduce `ConversionRetort` to manage conversion recipes. It exposes high-level converting functions as methods and allows reusing configuration. ```python from adaptix.conversion import ConversionRetort retort = ConversionRetort() ``` -------------------------------- ### Omit Specific Default Fields Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/extended-usage.html Serialize a dataclass instance, omitting only the fields specified in the `omit_default` parameter. This example omits the 'authors' field if it's empty. ```python from dataclasses import dataclass, field from adaptix import Retort, name_mapping @dataclass class Book: title: str sub_title: str | None = None authors: list[str] = field(default_factory=list) retort = Retort( recipe=[ name_mapping( Book, omit_default="authors", ), ], ) book = Book(title="Fahrenheit 451") assert retort.dump(book) == {"title": "Fahrenheit 451", "sub_title": None} ``` -------------------------------- ### Extending a Base ConversionRetort Recipe Source: https://adaptix.readthedocs.io/en/latest/conversion/extended-usage.html Demonstrates how to create a new `ConversionRetort` based on an existing one, adding new providers to the beginning of the recipe. This allows for overriding or extending existing conversion logic without modifying the base retort. ```python from dataclasses import dataclass from datetime import date from uuid import UUID from adaptix.conversion import ConversionRetort, coercer base_retort = ConversionRetort( recipe=[ coercer(UUID, str, str), coercer(date, str, lambda x: x.strftime("%d.%m.%Y")), ], ) ``` -------------------------------- ### Skipped error with default value Source: https://adaptix.readthedocs.io/en/latest/_sources/why-not-pydantic.rst.txt This example demonstrates how Adaptix handles cases where a field in the target model has a default value, potentially skipping errors during mapping. ```python from adaptix import Retort from dataclasses import dataclass, field @dataclass class Target: value: int = 10 retort = Retort() # This will not raise an error, as the default value is used # when the input data is missing the 'value' field. retort.load({}, Target) ``` -------------------------------- ### Using exec_type_checking for Runtime Type Hints Source: https://adaptix.readthedocs.io/en/latest/conversion/extended-usage.html Shows how to use `adaptix.type_tools.exec_type_checking` to make type hints defined under `if TYPE_CHECKING` available at runtime. ```python from typing import List, get_type_hints from adaptix.type_tools import exec_type_checking from . import chat, message # You pass the module object exec_type_checking(chat) exec_type_checking(message) # After these types can be extracted assert get_type_hints(chat.Chat) == { "id": int, "name": str, "messages": List[message.Message], } assert get_type_hints(chat.Message) == { "id": int, "text": str, "chat": chat.Chat, } ``` -------------------------------- ### Filtering fields by type using predicates Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/extended-usage.html Shows how to use predicates with `name_mapping.skip` to filter fields based on their type. This example skips fields of type `HiddenStr` during dumping. ```python from dataclasses import dataclass from adaptix import Retort, dumper, loader, name_mapping class HiddenStr(str): def __repr__(self): return "'"' @dataclass class User: id: int name: str password_hash: HiddenStr retort = Retort( recipe=[ loader(HiddenStr, HiddenStr), dumper(HiddenStr, str), ], ) skipping_retort = retort.extend( recipe=[ name_mapping( User, skip=HiddenStr, ), ], ) user = User( id=52, name="Ken Thompson", password_hash=HiddenStr("ZghOT0eRm4U9s"), ) data = { "id": 52, "name": "Ken Thompson", } data_with_password_hash = { **data, "password_hash": "ZghOT0eRm4U9s", } assert repr(user) == "User(id=52, name='Ken Thompson', password_hash='')" assert retort.dump(user) == data_with_password_hash assert retort.load(data_with_password_hash, User) == user assert skipping_retort.dump(user) == data ``` -------------------------------- ### Advanced Name Mapping with Adaptix Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/extended-usage.rst.txt Illustrates advanced name mapping techniques using `.name_mapping.map` with various data structures and predicates. ```python from adaptix.load_dump import Schema from adaptix.name_mapping import map, trim_trailing_underscore, name_style, as_list from adaptix.utils import Mapping class MyModel: def __init__(self, a: int, b_list: list[int], c: int): self.a = a self.b_list = b_list self.c = c schema = Schema() schema.add_mapping( map([ ('a', 'alpha'), ('b_list', lambda shape, field: trim_trailing_underscore(field.name)), ('c', lambda shape, field: name_style(field.name, 'camelCase')), ('d', ...), ('e', None), ]) ) print(schema.dump_to_dict(MyModel(a=1, b_list=[2], c=3))) # Output: {'alpha': 1, 'b': [2], 'c': 'c'} ``` -------------------------------- ### Initialize Submodules After Cloning Source: https://adaptix.readthedocs.io/en/latest/reference/contributing.html If the repository was cloned without the `--recurse-submodules` flag, use this command to initialize and update any missing submodules, particularly for directories like `benchmarks/release_data`. ```bash git submodule update --init --recursive ``` -------------------------------- ### Custom Type Coercion with ConversionRetort Source: https://adaptix.readthedocs.io/en/latest/conversion/extended-usage.html Utilize `ConversionRetort` to manage a collection of conversion recipes, enabling reuse and extension. This example shows coercing `UUID` to `str` and `date` to a formatted `str`. ```python from dataclasses import dataclass from datetime import date from uuid import UUID from adaptix.conversion import ConversionRetort, coercer @dataclass class Book: id: UUID title: str release_at: date @dataclass class BookDTO: id: str title: str release_at: str retort = ConversionRetort( recipe=[ coercer(UUID, str, str), coercer(date, str, lambda x: x.strftime("%d.%m.%Y")), ], ) convert_book_to_dto = retort.get_converter(Book, BookDTO) assert ( convert_book_to_dto( Book( id=UUID("87000388-94e6-49a4-b51b-320e38577bd9"), title="Fahrenheit 451", release_at=date(1953, 10, 19), ), ) == BookDTO( id="87000388-94e6-49a4-b51b-320e38577bd9", title="Fahrenheit 451", release_at="19.10.1953", ) ) ``` -------------------------------- ### Chaining Providers with Parameter Overriding Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/extended-usage.rst.txt Illustrates how the first provider in a chain can override parameters for the next provider. ```python from adaptix.load_dump import Provider, Schema class MyProvider(Provider): def dump_to_dict(self, obj: object) -> dict: return {'a': 1} class NextProvider(Provider): def dump_to_dict(self, obj: object) -> dict: return {'b': 2} schema = Schema() schema.add_provider(MyProvider(), override={'b': 3}) schema.add_provider(NextProvider()) print(schema.dump_to_dict(None)) # Output: {'a': 1, 'b': 3} ``` -------------------------------- ### Loading and Dumping Generic Classes with Adaptix Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/extended-usage.html Demonstrates how to load and dump data into a generic dataclass like MinMax[T] using Adaptix's Retort. Ensure concrete types are provided when working with generic classes. ```python from dataclasses import dataclass from typing import Generic, TypeVar from adaptix import Retort T = TypeVar("T") @dataclass class MinMax(Generic[T]): min: T | None = None max: T | None = None retort = Retort() data = {"min": 10, "max": 20} min_max = retort.load(data, MinMax[int]) assert min_max == MinMax(min=10, max=20) assert retort.dump(min_max, MinMax[int]) == data ``` -------------------------------- ### Load and Dump Model with Retort Source: https://adaptix.readthedocs.io/en/latest/overview.html Demonstrates loading data into a dataclass model and dumping the model back to data using Adaptix's Retort. ```python from dataclasses import dataclass from adaptix import Retort @dataclass class Book: title: str price: int data = { "title": "Fahrenheit 451", "price": 100, } # Retort is meant to be global constant or just one-time created retort = Retort() book = retort.load(data, Book) assert book == Book(title="Fahrenheit 451", price=100) assert retort.dump(book) == data ``` -------------------------------- ### Recursive Data Types Example Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/extended-usage.rst.txt Illustrates how Adaptix handles recursive data types, allowing for nested structures without explicit configuration. Note that cyclic references require special handling. ```python from typing import List class Node: def __init__(self, name: str, children: List['Node'] = None) -> None: self.name = name self.children = children if children is not None else [] class ExtendedUsage(BaseModel): node: Node # Example usage: node = Node('root', [Node('child1'), Node('child2')]) instance = ExtendedUsage(node=node) ``` -------------------------------- ### Handling Colliding Field Names with ExtraKwargs Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/extended-usage.html When using `ExtraKwargs`, if an unknown field name collides with an original field name, a `TypeError` is raised. This example demonstrates how to handle such collisions by mapping fields first. ```python from adaptix import ExtraKwargs, Retort, name_mapping class Book: def __init__(self, title: str, price: int, **kwargs): self.title = title self.price = price self.kwargs = kwargs def __eq__(self, other): return ( self.title == other.title and self.price == other.price and self.kwargs == other.kwargs ) data = { "name": "Fahrenheit 451", "price": 100, "title": "Celsius 232.778", } retort = Retort( recipe=[ name_mapping(Book, map={"title": "name"}), name_mapping(Book, extra_in=ExtraKwargs()), ], ) try: retort.load(data, Book) except TypeError as e: assert str(e).endswith("__init__() got multiple values for argument 'title'") ``` -------------------------------- ### Filter Fields Using 'skip' Parameter Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/extended-usage.rst.txt Control which fields are loaded or dumped by using the `skip` parameter. This example demonstrates skipping a required field during loading, which will raise an error, but dumping will still work. ```python from retort import Retort class MyData: def __init__(self, required_field, optional_field=None): self.required_field = required_field self.optional_field = optional_field loader = MyData.load(skip=['required_field']) try: loader.load({"required_field": "value"}) except Exception as e: print(e) dumper = MyData.dump() data = MyData(required_field="value") data.dump() == {"required_field": "value"} ``` -------------------------------- ### Chaining Providers in Adaptix Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/extended-usage.rst.txt Demonstrates how one provider can override parameters for subsequent providers in a chain. ```python from adaptix.load_dump import Provider, Schema class MyProvider(Provider): def dump_to_dict(self, obj: object) -> dict: return {'a': 1} class NextProvider(Provider): def dump_to_dict(self, obj: object) -> dict: return {'b': 2} schema = Schema() schema.add_provider(MyProvider()) schema.add_provider(NextProvider()) print(schema.dump_to_dict(None)) # Output: {'a': 1, 'b': 2} ``` -------------------------------- ### Generate Converter for Nested Models Source: https://adaptix.readthedocs.io/en/latest/conversion/tutorial.html Demonstrates using `get_converter` to handle conversions between nested data structures. This example converts a `Book` model containing a nested `Person` model to a `BookDTO` with a nested `PersonDTO`. ```python from dataclasses import dataclass from adaptix.conversion import get_converter @dataclass class Person: name: str @dataclass class Book: title: str price: int author: Person @dataclass class PersonDTO: name: str @dataclass class BookDTO: title: str price: int author: PersonDTO convert_book_to_dto = get_converter(Book, BookDTO) assert ( convert_book_to_dto( Book(title="Fahrenheit 451", price=100, author=Person("Ray Bradbury")), ) == BookDTO(title="Fahrenheit 451", price=100, author=PersonDTO("Ray Bradbury")) ) ``` -------------------------------- ### Union Model Superset Handling Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/specific-types-behavior.rst.txt Shows how to handle situations where one model is a superset of another within a union. By default, unknown fields are skipped, which can prevent distinguishing between such models. This example demonstrates a potential issue. ```python from dataclasses import dataclass from adaptix import Retort @dataclass class Base: name: str @dataclass class Extended(Base): age: int retort = Retort() union_loader = retort.loader(Base | Extended) print(union_loader({'name': 'John'})) print(union_loader({'name': 'Jane', 'age': 25})) ``` -------------------------------- ### Load Error Handling with DebugTrail.ALL Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/tutorial.rst.txt Illustrates comprehensive error handling using LoadError and DebugTrail.ALL. This configuration captures detailed traceback information for debugging loading errors. ```python from adaptix import Retort from adaptix.load_error import LoadError, DebugTrail class MyError(LoadError): pass retort = Retort(debug_trail=DebugTrail.ALL) @retort.loader(int) def load_int(data: int) -> int: raise MyError('Some error') try: retort.load_by_type(int, 1) except MyError as e: print(e) ``` -------------------------------- ### Union Case Overlapping Example Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/specific-types-behavior.rst.txt Demonstrates a scenario where union case loaders might overlap, leading to undefined return values. This occurs when a single input value can be accepted by multiple loaders within a union. ```python from dataclasses import dataclass from adaptix import Retort @dataclass class Cat: name: str @dataclass class Dog: name: str retort = Retort() cat_loader = retort.loader(Cat) print(cat_loader({'name': 'Barsik'})) print(cat_loader({'name': 'Sharick'})) ``` -------------------------------- ### Loading with DebugTrail.FIRST Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/tutorial.html Illustrates how setting debug_trail=DebugTrail.FIRST on Retort raises only the first encountered error during loading, simplifying the traceback. ```python Traceback (most recent call last): ... adaptix.load_error.TypeLoadError: expected_type=, input_value='Fahrenheit 451' Exception was caused at ['price'] ``` -------------------------------- ### Retort Extension with New Items Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/tutorial.rst.txt Shows how to use the .extend() method to add new items to the beginning of a Retort's recipe. This promotes the DRY principle by allowing reuse of existing retort configurations. ```python from adaptix import Retort retort = Retort() new_retort = retort.extend(debug_trail=True, strict_coercion=True) ``` -------------------------------- ### append_trail Source: https://adaptix.readthedocs.io/en/latest/reference/api/adaptix.struct_trail.html Appends a trail element to an object. The trail is stored in a special attribute. If the object does not allow adding third-party attributes, this operation does nothing. The element is inserted at the start of the path, as trails are built in reverse order. ```APIDOC ## append_trail ### Description Appends a trail element to an object. Trail stores in special attribute, if an object does not allow adding 3rd-party attributes, do nothing. Element inserting to start of the path (it is built in reverse order) ### Parameters - **_obj** (T) - The object to which the trail element will be appended. - **_trail_element** (str | int | Any | TrailElementMarker) - The trail element to append. ### Returns - **T** - The object with the appended trail element. ``` -------------------------------- ### extend_trail Source: https://adaptix.readthedocs.io/en/latest/reference/api/adaptix.struct_trail.html Extends an object's trail with a sub-trail. The trail is stored in a special attribute. If the object does not allow adding third-party attributes, this operation does nothing. The sub-trail is inserted at the start of the path, as trails are built in reverse order. ```APIDOC ## extend_trail ### Description Extend a trail with a sub trail. Trail stores in special attribute, if an object does not allow adding 3rd-party attributes, do nothing. Sub path inserting to start (it is built in reverse order) ### Parameters - **_obj** (T) - The object whose trail will be extended. - **_sub_trail** (Reversible[str | int | Any | TrailElementMarker]) - The sub-trail to extend with. ### Returns - **T** - The object with the extended trail. ``` -------------------------------- ### adaptix.constructor Source: https://adaptix.readthedocs.io/en/latest/reference/api/adaptix.html Creates a provider that uses a specified function to construct objects. ```APIDOC ## adaptix.constructor ### Parameters #### Parameters - **pred** (str | Pattern | type | Any | LocStackChecker | LocStackPattern) - Predicate specifying where the provider should be used. - **func** (Callable) - The function to use for construction. ### Returns - Provider: The constructor provider. ``` -------------------------------- ### Extending Retort Recipe with New Loaders/Dumpers Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/tutorial.html Demonstrates how to extend an existing Retort with new loaders and dumpers using the `extend` method. This allows for code reuse and modularity by adding specific type handling to a base retort. ```python from datetime import datetime from adaptix import Retort, dumper, loader base_retort = Retort( recipe=[ loader(datetime, datetime.fromtimestamp), dumper(datetime, datetime.timestamp), ], ) specific_retort1 = base_retort.extend( recipe=[ loader(bytes, bytes.hex), loader(bytes, bytes.fromhex), ], ) # same as specific_retort2 = Retort( recipe=[ loader(bytes, bytes.hex), loader(bytes, bytes.fromhex), loader(datetime, datetime.fromtimestamp), dumper(datetime, datetime.timestamp), ], ) ``` -------------------------------- ### Union with Type Designator Example Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/specific-types-behavior.rst.txt Illustrates how to add a type designator (tag) to a model to uniquely determine the type within a union, resolving ambiguity issues. Note that this may not work if :paramref:`.name_mapping.omit_default` is applied to the tag field. ```python from dataclasses import dataclass, field from adaptix import Retort @dataclass class Cat: name: str type: str = field(default='cat') @dataclass class Dog: name: str type: str = field(default='dog') retort = Retort() union_loader = retort.loader(Cat | Dog) print(union_loader({'name': 'Barsik', 'type': 'cat'})) print(union_loader({'name': 'Sharick', 'type': 'dog'})) ``` -------------------------------- ### Using TypedDict with NotRequired Fields Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/extended-usage.html Shows how to use `TypedDict` with `NotRequired` fields to achieve similar behavior to sentinels for optional fields. `adaptix` handles loading and dumping of these fields. ```python from typing import NotRequired, TypedDict from adaptix import Retort class PatchBook(TypedDict): id: int title: NotRequired[str] sub_title: NotRequired[str] retort = Retort() data = {"id": 435} patch_book = retort.load(data, PatchBook) assert patch_book == PatchBook(id=435) assert retort.dump(patch_book, PatchBook) == data ``` -------------------------------- ### Benchmark Pydantic vs. Dataclass Instantiation Source: https://adaptix.readthedocs.io/en/latest/why-not-pydantic.html Measures the time taken to create instances of Pydantic and dataclass User models. Requires the model definitions from the previous snippet. ```python from datetime import datetime from timeit import timeit from .instantiating_penalty_models import UserDataclass, UserPydantic stmt = """ User( id=123, signup_ts=datetime(year=2019, month=6, day=1, hour=12, minute=22), tastes={'wine': 9, 'cheese': 7, 'cabbage': '1'}, ) """ print("pydantic ", timeit(stmt, globals={"User": UserPydantic, "datetime": datetime})) print("dataclass", timeit(stmt, globals={"User": UserDataclass, "datetime": datetime})) ``` -------------------------------- ### Loading and Dumping Pydantic Model with adaptix Source: https://adaptix.readthedocs.io/en/latest/why-not-pydantic.html Demonstrates how to load and dump Pydantic models using adaptix's Retort. This method bypasses Pydantic's alias settings, with all transformation logic handled by the retort. ```python from adaptix import Retort from pydantic import BaseModel class Book(BaseModel): title: str price: int data = { "title": "Fahrenheit 451", "price": 100, } retort = Retort() book = retort.load(data, Book) assert book == Book(title="Fahrenheit 451", price=100) assert retort.dump(book) == data ``` -------------------------------- ### Chaining Name Mappings for Partial Overriding Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/extended-usage.html Demonstrates how to chain multiple `name_mapping` directives to override parameters progressively. The first provider affects subsequent ones. ```python from dataclasses import dataclass from typing import Any from adaptix import NameStyle, Retort, name_mapping @dataclass class Person: first_name: str last_name: str extra: dict[str, Any] @dataclass class Book: title: str author: Person retort = Retort( recipe=[ name_mapping(Person, name_style=NameStyle.CAMEL), name_mapping("author", extra_in="extra", extra_out="extra"), ], ) data = { "title": "Lord of Light", "author": { "firstName": "Roger", "lastName": "Zelazny", "unknown_field": 1995, }, } book = retort.load(data, Book) assert book == Book( title="Lord of Light", author=Person( first_name="Roger", last_name="Zelazny", extra={"unknown_field": 1995}, ), ) assert retort.dump(book) == data ``` -------------------------------- ### Provider Chaining with Chain.LAST Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/tutorial.rst.txt Illustrates provider chaining using Chain.LAST, which applies the function after the one generated by the next provider. This is useful for post-processing data. ```python from adaptix.load_dump import loader, Chain @loader(int, Chain.LAST) def process_int(data: int) -> int: return data * 2 ``` -------------------------------- ### Combining Retorts for Layered Data Handling Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/tutorial.html Illustrates how to include one retort within another to create layered configurations. This approach is beneficial for separating the creation of loaders and dumpers for specific types into isolated, manageable layers. ```python from dataclasses import dataclass from datetime import datetime, timezone from enum import Enum from adaptix import Retort, bound, dumper, enum_by_name, loader class LiteraryGenre(Enum): DRAMA = 1 FOLKLORE = 2 POETRY = 3 PROSE = 4 @dataclass class LiteraryWork: id: int name: str genre: LiteraryGenre uploaded_at: datetime literature_retort = Retort( recipe=[ loader(datetime, lambda x: datetime.fromtimestamp(x, tz=timezone.utc)), dumper(datetime, lambda x: x.timestamp()), enum_by_name(LiteraryGenre), ], ) ``` -------------------------------- ### Upgrade and Compile Dependencies Source: https://adaptix.readthedocs.io/en/latest/_sources/reference/contributing.rst.txt Upgrade and compile dependencies, ensuring locked versions are updated. Use this command when you need to update to the latest dependency versions. ```bash just deps-compile-upgrade ``` -------------------------------- ### Run Basic Tests Sequentially Source: https://adaptix.readthedocs.io/en/latest/_sources/reference/contributing.rst.txt Execute basic tests sequentially across all supported Python versions. This is useful for quick checks during development. ```bash just test ``` -------------------------------- ### Predicate System with P Helper Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/tutorial.rst.txt Uses the P helper to define patterns for matching fields within specific model structures. This is more convenient than using plain strings for field matching across all models. ```python from adaptix.load_dump import P @dataclass class Book: created_at: datetime retort.register_provider(Book, P[Book].created_at, lambda _, __: datetime.now()) ``` -------------------------------- ### Loading and Dumping Collections of Dataclasses Source: https://adaptix.readthedocs.io/en/latest/loading-and-dumping/tutorial.html Demonstrates loading and dumping lists of dataclasses by specifying the target type as `list[YourDataclass]`. Adaptix supports various collection types. ```python from dataclasses import dataclass from adaptix import Retort @dataclass class Book: title: str price: int data = [ { "title": "Fahrenheit 451", "price": 100, }, { "title": "1984", "price": 100, }, ] retort = Retort() books = retort.load(data, list[Book]) assert books == [Book(title="Fahrenheit 451", price=100), Book(title="1984", price=100)] assert retort.dump(books, list[Book]) == data ``` -------------------------------- ### adaptix.conversion.from_param Source: https://adaptix.readthedocs.io/en/latest/reference/api/adaptix.conversion.html A special predicate form that matches only top-level parameters by name. ```APIDOC ## adaptix.conversion.from_param ### Description The special predicate form matching only top-level parameters by name ### Method from_param ### Parameters #### Path Parameters - **param_name** (str) - ### Response #### Success Response LocStackChecker ``` -------------------------------- ### Loading with Unknown Fields Renaming Source: https://adaptix.readthedocs.io/en/latest/_sources/loading-and-dumping/extended-usage.rst.txt Demonstrates how to handle unknown fields during loading by renaming them. If not handled, a TypeError will be raised. ```python from adaptix.load_options import OnLoadingUnknownFields class MyModel(BaseModel): x: int class Config(BaseConfig): on_loading_unknown_fields = OnLoadingUnknownFields.RENAMING MyModel.load({'x': 1, 'y': 2}) # MyModel(x=1) ```