### Install Adaptix via pip Source: https://github.com/reagento/adaptix/blob/main/docs/common/installation.rst Standard installation command for the library. ```text pip install adaptix==3.0.0b12 ``` -------------------------------- ### Install Adaptix with extras Source: https://github.com/reagento/adaptix/blob/main/docs/common/installation.rst Installation commands including optional extras for third-party library compatibility checks. ```text pip install adaptix[attrs-strict]==3.0.0b12 pip install adaptix[attrs, sqlalchemy-strict]==3.0.0b12 ``` -------------------------------- ### Run project checks Source: https://github.com/reagento/adaptix/blob/main/docs/reference/contributing.rst Commands to verify the environment setup. ```bash just lint just test-all ``` -------------------------------- ### Complex Example: Putting It All Together Source: https://github.com/reagento/adaptix/blob/main/docs/conversion/tutorial.rst A comprehensive example that integrates multiple Adaptix conversion features, including type coercion and linking, to showcase a complex scenario. ```python from adaptix.conversion import link, coercer from adaptix.conversion.type_coercion import TypeCoercion @coercer def int_to_str_coercer(value: int) -> str: return f"{value}" @link.coercer def link_coercer(value: int) -> str: return f"{value}" @link.link(coercer=link_coercer) def my_link(value: int) -> str: return f"{value}" @TypeCoercion.register def int_to_str_type_coercion(value: int) -> str: return f"{value}" @TypeCoercion.register def str_to_int_type_coercion(value: str) -> int: return int(value) class MyClass: def __init__(self, value: int) -> None: self.value = value @TypeCoercion.register def my_class_to_str_type_coercion(value: MyClass) -> str: return f"{value.value}" @TypeCoercion.register def str_to_my_class_type_coercion(value: str) -> MyClass: return MyClass(int(value)) ``` -------------------------------- ### Generate Output JSON Schema Example Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/json-schema.rst Example demonstrating how the JSON schema differs for output (dumping) compared to input (loading). Fields like 'tags' become required and 'additionalProperties' is removed. ```python from adaptix.load_dump import generate_json_schema from adaptix import Direction retort = ... json_schema = generate_json_schema(retort, MyType, direction=Direction.OUTPUT) ``` -------------------------------- ### Managing Provider Priority in Recipes Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Demonstrates how providers at the start of a recipe take precedence over subsequent ones. ```python .. literalinclude:: /examples/loading-and-dumping/tutorial/recipe_providers_priority.py ``` -------------------------------- ### New Error Example - Adaptix Loader Source: https://github.com/reagento/adaptix/blob/main/docs/changelog/changelog_body.rst This example shows the new error message format in Adaptix for loader generation failures, providing a more streamlined and readable output for issues with creating loaders. ```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` ``` -------------------------------- ### New Error Example - Adaptix Source: https://github.com/reagento/adaptix/blob/main/docs/changelog/changelog_body.rst This is an example of the new, improved error message format from Adaptix, also showing a 'ProviderNotFoundError' with a more compact and intuitive display. ```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› ``` -------------------------------- ### Advanced Name Mapping with Adaptix Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Illustrates advanced name mapping techniques using `adaptix.name_mapping.map`. This example shows how to handle complex renaming rules, including conditional mapping and structure flattening. ```python from adaptix import Retort from adaptix.name_mapping import map, only, trim_trailing_underscore, name_style, as_list from pydantic import BaseModel class Model(BaseModel): field_one: int field_two: str field_three_list: list[int] retort = Retort() retort.add_provider( map([ ("field_one", "id"), ("field_two", "name"), ("field_three_list", trim_trailing_underscore | name_style(camelcase) | as_list), ]) ) model = Model(field_one=1, field_two="test", field_three_list=[1, 2, 3]) print(retort.dump(model)) ``` -------------------------------- ### Old Error Example - Adaptix Source: https://github.com/reagento/adaptix/blob/main/docs/changelog/changelog_body.rst This is an example of an old error message format from Adaptix, showing a 'ProviderNotFoundError' when a dumper for a type cannot be produced. ```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: ‹__main__.MinMax[__main__.Bar].min: Optional[__main__.Bar]› │ ╰──▷ Cannot find dumper │ Location: ‹Bar› ╰──▷ Cannot create dumper for union. Dumpers for some union cases cannot be created │ Location: ‹__main__.MinMax[__main__.Bar].max: Optional[__main__.Bar]› ╰──▷ Cannot find dumper Location: ‹Bar› ``` -------------------------------- ### Process unknown fields on loading Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Examples for handling unknown fields during the loading process using field IDs or custom saturator functions. ```python from adaptix import loader, dumper, name_mapping, Retort from typing import Any class Book: def __init__(self, title: str, author: str, **extra): self.title = title self.author = author self.extra = extra retort = Retort( recipe=[ name_mapping(Book, extra_in="extra") ] ) # This will raise TypeError because 'extra' is not defined in __init__ # retort.load({"title": "1984", "author": "George Orwell", "year": 1949}, Book) ``` ```python from adaptix import loader, dumper, name_mapping, Retort from typing import Any class Book: def __init__(self, title: str, author: str, extra: dict = None): self.title = title self.author = author self.extra = extra or {} retort = Retort( recipe=[ name_mapping(Book, extra_in="extra") ] ) book = retort.load({"title": "1984", "author": "George Orwell", "year": 1949}, Book) assert book.extra == {"year": 1949} ``` ```python from adaptix import loader, dumper, name_mapping, Retort from typing import Any, Mapping class Book: def __init__(self, title: str, author: str): self.title = title self.author = author self.extra = {} def saturator(book: Book, extra: Mapping[str, Any]): book.extra.update(extra) retort = Retort( recipe=[ name_mapping(Book, extra_in=saturator) ] ) book = retort.load({"title": "1984", "author": "George Orwell", "year": 1949}, Book) assert book.extra == {"year": 1949} ``` -------------------------------- ### Old Error Example - Adaptix Loader Source: https://github.com/reagento/adaptix/blob/main/docs/changelog/changelog_body.rst An example of an older error message format from Adaptix, specifically for loader generation failures, indicating issues with creating loaders for model fields. ```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` +------------------------------------ ``` -------------------------------- ### Predicate System with P for Targeted Loading Source: https://context7.com/reagento/adaptix/llms.txt Use the `P` helper to create precise predicates that target specific fields in specific models, ensuring custom loaders are applied only where intended. This example applies a timestamp loader exclusively to `Book.created_at`. ```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=[ # Only apply timestamp loader to Book.created_at, not Person.created_at loader(P[Book].created_at, lambda x: datetime.fromtimestamp(x, tz=timezone.utc)), ], ) bookshop = retort.load(data, Bookshop) ``` -------------------------------- ### Pydantic Model Mapping Example Source: https://github.com/reagento/adaptix/blob/main/docs/why-not-pydantic.rst This example showcases Pydantic's approach to mapping one model to another using `from_attributes=True`. It highlights how Pydantic accesses object attributes instead of dictionary keys for mapping. ```python from pydantic import BaseModel class SourceModel(BaseModel): name: str age: int class TargetModel(BaseModel): name: str age: int model_config = { "from_attributes": True, } obj = SourceModel(name='John Doe', age=30) print(TargetModel.model_validate(obj).model_dump()) ``` -------------------------------- ### Chaining Loaders and Dumpers with Adaptix Source: https://context7.com/reagento/adaptix/llms.txt Use Chain.FIRST or Chain.LAST to add processing before or after existing converters, rather than replacing them entirely. This example demonstrates loading a JSON string within a message body. ```python import json from dataclasses import dataclass from datetime import datetime from adaptix import Chain, P, Retort, dumper, loader @dataclass class Book: title: str price: int author: str @dataclass class Message: id: str timestamp: datetime body: Book # Stored as JSON string in the input data = { "id": "ajsVre", "timestamp": "2023-01-29T21:26:28.026860", "body": '{"title": "Fahrenheit 451", "price": 100, "author": "Ray Bradbury"}', } retort = Retort( recipe=[ # Chain.FIRST: json.loads runs first, then result goes to Book loader loader(P[Message].body, json.loads, Chain.FIRST), # Chain.LAST: Book dumper runs first, then json.dumps on result dumper(P[Message].body, json.dumps, Chain.LAST), ], ) message = retort.load(data, Message) assert message == Message( id="ajsVre", timestamp=datetime(2023, 1, 29, 21, 26, 28, 26860), body=Book(title="Fahrenheit 451", price=100, author="Ray Bradbury"), ) ``` -------------------------------- ### Process unknown fields on dumping Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Examples for handling extra fields during the dumping process using default skipping, field IDs, or custom extractor functions. ```python from adaptix import Retort, name_mapping class Book: def __init__(self, title: str, author: str, extra: dict): self.title = title self.author = author self.extra = extra retort = Retort( recipe=[ name_mapping(Book, extra_out="extra") ] ) book = Book("1984", "George Orwell", {"year": 1949}) # Default behavior is ExtraSkip assert retort.dump(book) == {"title": "1984", "author": "George Orwell"} ``` ```python from adaptix import Retort, name_mapping class Book: def __init__(self, title: str, author: str, extra: dict): self.title = title self.author = author self.extra = extra retort = Retort( recipe=[ name_mapping(Book, extra_out="extra") ] ) book = Book("1984", "George Orwell", {"year": 1949}) assert retort.dump(book) == {"title": "1984", "author": "George Orwell", "year": 1949} ``` ```python from adaptix import Retort, name_mapping class Book: def __init__(self, title: str, author: str, extra: dict): self.title = title self.author = author self.extra = extra retort = Retort( recipe=[ name_mapping(Book, extra_out=["extra", "other"]) ] ) book = Book("1984", "George Orwell", {"year": 1949}) book.other = {"publisher": "Secker & Warburg"} assert retort.dump(book) == { "title": "1984", "author": "George Orwell", "year": 1949, "publisher": "Secker & Warburg" } ``` ```python from adaptix import Retort, name_mapping from typing import Mapping, Any class Book: def __init__(self, title: str, author: str, extra: dict): self.title = title self.author = author self.extra = extra def extractor(book: Book) -> Mapping[str, Any]: return book.extra retort = Retort( recipe=[ name_mapping(Book, extra_out=extractor) ] ) book = Book("1984", "George Orwell", {"year": 1949}) assert retort.dump(book) == {"title": "1984", "author": "George Orwell", "year": 1949} ``` -------------------------------- ### Name Style Conversion with Adaptix Source: https://context7.com/reagento/adaptix/llms.txt Automatically convert between naming conventions (e.g., snake_case to camelCase) using `name_mapping` with the `name_style` parameter. This example converts between snake_case and camelCase for person names. ```python from dataclasses import dataclass from adaptix import NameStyle, Retort, name_mapping @dataclass class Person: first_name: str last_name: str retort = Retort( recipe=[ name_mapping(Person, name_style=NameStyle.CAMEL), ], ) data = {"firstName": "Richard", "lastName": "Stallman"} person = retort.load(data, Person) assert person == Person(first_name="Richard", last_name="Stallman") assert retort.dump(person) == data ``` -------------------------------- ### Bootstrap environment Source: https://github.com/reagento/adaptix/blob/main/docs/reference/contributing.rst Initial preparation of the virtual environment and repository for development. ```bash just bootstrap ``` -------------------------------- ### Manage documentation Source: https://github.com/reagento/adaptix/blob/main/docs/reference/contributing.rst Commands to build or clean project documentation. ```bash just doc ``` ```bash just doc-clean ``` -------------------------------- ### Configuring Retort with Recipes Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Introduces the recipe system for customizing loader and dumper behavior using providers. ```python .. literalinclude:: /examples/loading-and-dumping/tutorial/retort_recipe_intro.py ``` ```python .. literalinclude:: /examples/loading-and-dumping/tutorial/retort_recipe_intro_dumper.py ``` -------------------------------- ### Create Custom Sentinel Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Shows how to define a custom sentinel type using as_sentinel. ```python from dataclasses import dataclass from adaptix import Retort, as_sentinel, omit_default NOT_PROVIDED = as_sentinel("NOT_PROVIDED") @dataclass class Book: title: str author: str = NOT_PROVIDED retort = Retort(recipe=[omit_default]) book = retort.load({"title": "The Great Gatsby"}, Book) assert book.author is NOT_PROVIDED ``` -------------------------------- ### Detect Cyclic References Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Example of a structure that causes issues with recursive loading/dumping. ```python item_category.sub_categories.append(item_category) ``` -------------------------------- ### Predicate System with P Helper Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Demonstrates the use of the 'P' helper for defining patterns in the predicate system. Use this to match fields based on their path and type within the structure. ```python from adaptix import P # Example usage of P for matching fields # P[Book].created_at matches 'created_at' field within 'Book' model # P.name matches any field named 'name' # P[Foo] + P.name is equivalent to P[Foo].name # P[Foo, Bar] matches either 'Foo' or 'Bar' class # P can be combined with bitwise operators: | (OR), & (AND), ^ (XOR), ~ (NOT) # P[Foo].name[Bar].age matches 'age' field in 'Bar' model, which is inside 'name' field of 'Foo' model. ``` -------------------------------- ### Run tests Source: https://github.com/reagento/adaptix/blob/main/docs/reference/contributing.rst Various commands for running test suites. ```bash just test ``` ```bash just test-all ``` ```bash just test-all-seq ``` -------------------------------- ### Clone repository with submodules Source: https://github.com/reagento/adaptix/blob/main/docs/reference/contributing.rst Initial repository cloning command including submodules. ```bash git clone --recurse-submodules https://github.com/reagento/adaptix ``` -------------------------------- ### Chat Model Definition Source: https://github.com/reagento/adaptix/blob/main/docs/common/dealing-with-if-type-checking.rst Defines the `Chat` model, which includes a forward reference to `Message`. This snippet is part of a larger example demonstrating runtime type checking resolution. ```python from typing import TYPE_CHECKING if TYPE_CHECKING: from .message import Message class Chat: def __init__(self, messages: list["Message"]): self.messages = messages def add_message(self, message: "Message") -> None: self.messages.append(message) ``` -------------------------------- ### Chaining Providers with Parameter Overrides Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Illustrates how the first provider in a chain can override parameters for the next providers. This allows for dynamic configuration of data processing steps. ```python from adaptix import Provider, Retort class OverrideProvider(Provider): def apply(self, data: dict, retort: Retort) -> dict: return {"a": "X", "b": "Y"} class EchoProvider(Provider): def apply(self, data: dict, retort: Retort) -> dict: return data retort = Retort() retort.add_provider(OverrideProvider()) retort.add_provider(EchoProvider()) data = {"a": "A", "b": "B"} print(retort.apply(data)) ``` -------------------------------- ### Conversion with Type Coercion Source: https://context7.com/reagento/adaptix/llms.txt Define how to convert between different types during model conversion using the `coercer` function. This example shows coercing a UUID to a string. Requires importing coercer and get_converter. ```python from dataclasses import dataclass from uuid import UUID from adaptix.conversion import coercer, get_converter @dataclass class Book: id: UUID title: str author: str @dataclass class BookDTO: id: str # UUID converted to string title: str author: str convert_book_to_dto = get_converter( src=Book, dst=BookDTO, recipe=[coercer(UUID, str, func=str)], ) book = Book(id=UUID("87000388-94e6-49a4-b51b-320e38577bd9"), title="Fahrenheit 451", author="Ray Bradbury") dto = convert_book_to_dto(book) assert dto == BookDTO(id="87000388-94e6-49a4-b51b-320e38577bd9", title="Fahrenheit 451", author="Ray Bradbury") ``` -------------------------------- ### Fix missing submodules Source: https://github.com/reagento/adaptix/blob/main/docs/reference/contributing.rst Command to initialize submodules if the repository was cloned without them. ```bash git submodule update --init --recursive ``` -------------------------------- ### Skipping Private Fields with Pydantic Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Shows how Adaptix, by default, skips private fields (starting with an underscore) when dumping data from Pydantic models. No special configuration is needed for this default behavior. ```python from adaptix import Retort from pydantic import BaseModel class Model(BaseModel): public: int _private: int retort = Retort() model = Model(public=1, _private=2) print(retort.dump(model)) ``` -------------------------------- ### Provider Chaining with Chain.FIRST and Chain.LAST Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Explains how to use provider chaining with '.loader' and '.dumper' by specifying the 'Chain.FIRST' or 'Chain.LAST' parameter. This allows additional data processing before or after an existing converter. ```python from adaptix import loader, dumper, Chain @loader(int, Chain.FIRST) def process_int(data): # Additional processing before int loading return data @dumper(str, Chain.LAST) def format_str(data): # Additional formatting after str dumping return data ``` -------------------------------- ### Basic Loading and Dumping with Retort Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Demonstrates the fundamental usage of Retort for converting between dataclasses and dictionaries. ```python .. literalinclude:: /examples/loading-and-dumping/tutorial/tldr.py ``` -------------------------------- ### Validator Creation with .validator Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Shows how to create a data verifier using the '.validator' wrapper. It takes a test function that returns False on invalid input, raising an exception. ```python from adaptix import validator @validator(int) def is_positive(value): return value > 0 ``` -------------------------------- ### Load Nested Objects with Retort Source: https://context7.com/reagento/adaptix/llms.txt Adaptix automatically handles nested objects when type annotations are correctly defined in your dataclasses. This example demonstrates loading a Book object with an embedded Person object. ```python from dataclasses import dataclass from adaptix import Retort @dataclass class Person: name: str @dataclass class Book: title: str price: int author: Person data = { "title": "Fahrenheit 451", "price": 100, "author": { "name": "Ray Bradbury", }, } retort = Retort() book = retort.load(data, Book) assert book == Book(title="Fahrenheit 451", price=100, author=Person("Ray Bradbury")) assert retort.dump(book) == data ``` -------------------------------- ### Avoid Implicit Float to Decimal Conversion in Pydantic Source: https://github.com/reagento/adaptix/blob/main/docs/why-not-pydantic.rst To prevent potential inaccuracies from implicit float to Decimal conversions, use Pydantic's strict mode. This example demonstrates the default behavior where conversion occurs. ```python from decimal import Decimal from pydantic import BaseModel class Model(BaseModel): value: Decimal print(Model(value=1.1).value == Decimal('1.1')) print(Model(value=1.1).value) ``` -------------------------------- ### Sync dependencies Source: https://github.com/reagento/adaptix/blob/main/docs/reference/contributing.rst Synchronize all project dependencies. ```bash just venv-sync ``` -------------------------------- ### Run linters Source: https://github.com/reagento/adaptix/blob/main/docs/reference/contributing.rst Execute all project linters. ```bash just lint ``` -------------------------------- ### Field Renaming with Adaptix Name Mapping Source: https://context7.com/reagento/adaptix/llms.txt Use `name_mapping` to rename fields between internal Python names and external representation (e.g., JSON keys). This example maps the internal 'timestamp' field to the external 'ts' key. ```python from dataclasses import dataclass from datetime import datetime, timezone from adaptix import Retort, name_mapping @dataclass class Event: name: str timestamp: datetime retort = Retort( recipe=[ name_mapping( Event, map={ "timestamp": "ts", # Internal field "timestamp" maps to external key "ts" }, ), ], ) data = {"name": "SystemStart", "ts": "2023-05-14T00:06:33+00:00"} event = retort.load(data, Event) assert event == Event(name="SystemStart", timestamp=datetime(2023, 5, 14, 0, 6, 33, tzinfo=timezone.utc)) assert retort.dump(event) == data ``` -------------------------------- ### Model Loading and Dumping Source: https://github.com/reagento/adaptix/blob/main/README.md Demonstrates basic serialization and deserialization using the Retort class. ```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 ``` -------------------------------- ### Omitting Default Values with Adaptix Name Mapping Source: https://context7.com/reagento/adaptix/llms.txt Skip fields with default values when dumping to reduce output size by using `name_mapping` with `omit_default=True`. This example omits the `sub_title` and `authors` fields when dumping a `Book` object that has default values for them. ```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=True), ], ) book = Book(title="Fahrenheit 451") # Fields with default values are omitted assert retort.dump(book) == {"title": "Fahrenheit 451"} ``` -------------------------------- ### Adding Validators with Adaptix Source: https://context7.com/reagento/adaptix/llms.txt Use `validator` to add validation rules that check input data during loading. Validation errors include the field path. This example shows a validator with a string error message and one with a custom exception factory. ```python from dataclasses import dataclass from adaptix import P, Retort, validator from adaptix.load_error import AggregateLoadError, LoadError, ValidationLoadError @dataclass class Book: title: str price: int data = {"title": "Fahrenheit 451", "price": -10} # Validator with string error message retort = Retort( recipe=[ validator(P[Book].price, lambda x: x >= 0, "value must be greater or equal 0"), ], ) try: retort.load(data, Book) except AggregateLoadError as e: assert len(e.exceptions) == 1 assert isinstance(e.exceptions[0], ValidationLoadError) assert e.exceptions[0].msg == "value must be greater or equal 0" # Validator with custom exception factory class BelowZeroError(LoadError): def __init__(self, actual_value: int): self.actual_value = actual_value retort = Retort( recipe=[ validator(P[Book].price, lambda x: x >= 0, lambda x: BelowZeroError(x)), ], ) try: retort.load(data, Book) except AggregateLoadError as e: assert isinstance(e.exceptions[0], BelowZeroError) assert e.exceptions[0].actual_value == -10 ``` -------------------------------- ### Introduction to `ConversionRetort` Source: https://github.com/reagento/adaptix/blob/main/docs/conversion/extended-usage.rst Utilize `ConversionRetort` to manage conversion recipes, providing convenient access to high-level converting functions like `convert`, `get_converter`, and `impl_converter` as methods. This promotes recipe reuse. ```python from adaptix.conversion import ConversionRetort retort = ConversionRetort() # Use retort methods like retort.convert(...), retort.get_converter(...) ``` -------------------------------- ### Pydantic Models from Tutorial Source: https://github.com/reagento/adaptix/blob/main/docs/why-not-pydantic.rst Defines Pydantic models as presented in the Pydantic tutorial. These models are used for performance benchmarking. ```python from datetime import datetime from pydantic import BaseModel class User(BaseModel): id: int name: str = 'John Doe' signup_ts: datetime | None = None friends: list[int] = [] external_data = { 'id': 123, 'signup_ts': '2017-06-01 12:00', 'friends': [1, '2', b'3'], } User(**external_data) ``` -------------------------------- ### Define Generic Classes Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Demonstrates the use of generic classes with Adaptix. ```python from typing import TypeVar, Generic from dataclasses import dataclass from adaptix import Retort T = TypeVar("T") @dataclass class Box(Generic[T]): content: T retort = Retort() # Loading box = retort.load({"content": 123}, Box[int]) assert box == Box(content=123) # Dumping assert retort.dump(box, Box[int]) == {"content": 123} ``` -------------------------------- ### Chaining Providers in Adaptix Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Demonstrates how one provider can override parameters of subsequent providers in a chain. Useful for setting up complex data processing pipelines. ```python from adaptix import Provider, Retort class UpperProvider(Provider): def apply(self, data: dict, retort: Retort) -> dict: return {k: v.upper() for k, v in data.items()} class LowerProvider(Provider): def apply(self, data: dict, retort: Retort) -> dict: return {k: v.lower() for k, v in data.items()} retort = Retort() retort.add_provider(UpperProvider()) retort.add_provider(LowerProvider()) data = {"a": "A", "b": "B"} print(retort.apply(data)) ``` -------------------------------- ### Retort Extension for Adding Recipe Items Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Illustrates using the '.extend' method to add new items to the beginning of a Retort's recipe. This promotes the DRY principle by reusing existing retort configurations. ```python from adaptix import Retort retort = Retort() new_retort = retort.extend(other_retort) ``` -------------------------------- ### Basic Model Conversion Source: https://github.com/reagento/adaptix/blob/main/docs/conversion/tutorial.rst Demonstrates the basic usage of Adaptix to generate a conversion function between two models. ```python from adaptix.conversion import impl_converter from dataclasses import dataclass @dataclass class Book: title: str author: str @dataclass class BookDTO: title: str author: str @impl_converter def convert_book_to_dto(data: Book) -> BookDTO: ... ``` -------------------------------- ### Loading and Dumping Pydantic Models Source: https://github.com/reagento/adaptix/blob/main/docs/why-not-pydantic.rst Shows how to use Adaptix to handle Pydantic models, bypassing internal Pydantic aliases in favor of Retort-defined logic. ```python from pydantic import BaseModel from adaptix import Retort class MyModel(BaseModel): field: int retort = Retort() # Loading data into a Pydantic model data = {"field": 1} model = retort.load(data, MyModel) # Dumping a Pydantic model to a dictionary dumped = retort.dump(model) print(dumped) ``` -------------------------------- ### Execute Type Checking with Adaptix Source: https://github.com/reagento/adaptix/blob/main/docs/common/dealing-with-if-type-checking.rst Demonstrates how to use `exec_type_checking` from Adaptix to resolve forward references and make type hints available at runtime. This should typically be called in the main module after all imports. ```python from adaptix.type_tools import exec_type_checking exec_type_checking() from chat import Chat chat = Chat(messages=[]) print(chat.__annotations__) ``` -------------------------------- ### Traceback of Raised Errors with DebugTrail.ALL Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Illustrates the detailed traceback produced when 'DebugTrail.ALL' is enabled, showing the complete loading process and potential error points. ```python from adaptix import load_error # Example demonstrating detailed error reporting with DebugTrail.ALL # This snippet itself does not produce output, but is referenced for its traceback example. ``` -------------------------------- ### Direct Conversion Function Source: https://github.com/reagento/adaptix/blob/main/docs/conversion/tutorial.rst Illustrates the use of the direct convert function, noting its limitations regarding configuration. ```python from adaptix.conversion import convert from dataclasses import dataclass @dataclass class Book: title: str author: str @dataclass class BookDTO: title: str author: str book = Book(title="The Book", author="John Doe") book_dto = convert(book, BookDTO) ``` -------------------------------- ### Compile dependencies Source: https://github.com/reagento/adaptix/blob/main/docs/reference/contributing.rst Compile raw dependencies into locked versions. ```bash just deps-compile ``` ```bash just deps-compile-upgrade ``` -------------------------------- ### Configure DebugTrail.FIRST for error reporting Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Displays the traceback when debug_trail is set to DebugTrail.FIRST, which raises only the first encountered error. ```pytb .. literalinclude:: /examples/loading-and-dumping/tutorial/load_error_dt_first.pytb ``` -------------------------------- ### Retort Combination for Layered Configuration Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Demonstrates how to include one Retort within another to separate loader and dumper configurations for specific types. Upper-level retort options do not affect inner retorts. ```python from adaptix import Retort literature_retort = Retort() main_retort = Retort().extend(literature_retort) ``` -------------------------------- ### Manual Field Linking Source: https://github.com/reagento/adaptix/blob/main/docs/conversion/tutorial.rst Explains how to link fields with different names using the link decorator. ```python from adaptix.conversion import impl_converter, link, P from dataclasses import dataclass @dataclass class Book: title: str author: str @dataclass class BookDTO: title: str writer: str @impl_converter @link(P.author, BookDTO.writer) def convert_book_to_dto(data: Book) -> BookDTO: ... ``` -------------------------------- ### Generate coverage report Source: https://github.com/reagento/adaptix/blob/main/docs/reference/contributing.rst Produce a merged coverage report in the working directory. ```bash just cov ``` -------------------------------- ### Date and Time Handling Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/specific-types-behavior.rst Configuration for date, time, and datetime serialization formats. ```APIDOC ## Date and Time Handling ### Description By default, date and time objects are represented as ISO format strings. Alternative providers are available for specific formatting or UNIX timestamp conversion. ### Customization - **datetime_by_format**: Load/dump using specific format strings. - **datetime_by_timestamp**: Load/dump using UNIX timestamps. - **date_by_timestamp**: Load/dump date using UNIX timestamps. ``` -------------------------------- ### Handle unknown fields during loading Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Configure how extra data absent in the target structure is handled using ExtraSkip, ExtraForbid, or ExtraKwargs. ```python .. literalinclude:: /examples/loading-and-dumping/extended_usage/unknown_fields_processing/on_loading_extra_skip.py ``` ```python .. literalinclude:: /examples/loading-and-dumping/extended_usage/unknown_fields_processing/on_loading_extra_forbid.py ``` ```python .. literalinclude:: /examples/loading-and-dumping/extended_usage/unknown_fields_processing/on_loading_extra_kwargs.py ``` -------------------------------- ### Model Conversion with get_converter Source: https://context7.com/reagento/adaptix/llms.txt Generate functions to convert between different model types, such as SQLAlchemy models and dataclasses. Requires importing get_converter from adaptix.conversion. ```python from dataclasses import dataclass from adaptix.conversion import get_converter @dataclass class Book: id: int title: str price: int @dataclass class BookDTO: id: int title: str price: int convert_book_to_dto = get_converter(Book, BookDTO) book = Book(id=183, title="Fahrenheit 451", price=100) dto = convert_book_to_dto(book) assert dto == BookDTO(id=183, title="Fahrenheit 451", price=100) ``` -------------------------------- ### Upcasting Models Source: https://github.com/reagento/adaptix/blob/main/docs/conversion/tutorial.rst Demonstrates how extra fields in the source model are ignored during conversion. ```python from adaptix.conversion import impl_converter from dataclasses import dataclass @dataclass class Book: title: str author: str year: int @dataclass class BookDTO: title: str author: str @impl_converter def convert_book_to_dto(data: Book) -> BookDTO: ... ``` -------------------------------- ### Extend Retort Functionality Source: https://github.com/reagento/adaptix/blob/main/docs/conversion/extended-usage.rst Demonstrates how to extend the base Retort functionality for custom conversion logic. ```python from adaptix import Retort from adaptix.conversion import get_converter # Example of extending retort functionality # This snippet assumes the existence of custom logic defined in the project structure retort = Retort() ``` -------------------------------- ### Handle unexpected errors with ExceptionGroup Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/tutorial.rst Demonstrates how unexpected errors are wrapped in an ExceptionGroup during the dumping process. ```python .. literalinclude:: /examples/loading-and-dumping/tutorial/unexpected_error.py :lines: 2- ``` -------------------------------- ### SQLAlchemy JSON Mutation Tracking Source: https://github.com/reagento/adaptix/blob/main/docs/reference/integrations.rst Demonstrates the challenge of mutation tracking with SQLAlchemy and JSON types, as __setattr__ does not automatically mark instances as dirty. ```python from sqlalchemy.orm.attributes import flag_modified user = session.query(User).first() # SQLAlchemy cannot track mutation of the object associated with the attribute # user.data.value = 2 # This change will not be detected by SQLAlchemy # To track mutation, you must explicitly call flag_modified: # flag_modified(user, 'data') # session.commit() ``` -------------------------------- ### Use TypedDict for Optional Fields Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Demonstrates using TypedDict with NotRequired fields. ```python from typing import TypedDict, NotRequired from adaptix import Retort class Book(TypedDict): title: str author: NotRequired[str] retort = Retort() assert retort.load({"title": "The Great Gatsby"}, Book) == {"title": "The Great Gatsby"} assert retort.load({"title": "The Great Gatsby", "author": "F. Scott Fitzgerald"}, Book) == { "title": "The Great Gatsby", "author": "F. Scott Fitzgerald" } ``` -------------------------------- ### Map models to lists Source: https://github.com/reagento/adaptix/blob/main/docs/loading-and-dumping/extended-usage.rst Techniques for serializing models as lists instead of dictionaries, useful for specific API requirements. ```python from adaptix import Retort, name_mapping class Book: def __init__(self, title: str, author: str): self.title = title self.author = author retort = Retort( recipe=[ name_mapping(Book, as_list=True) ] ) book = Book("1984", "George Orwell") assert retort.dump(book) == ["1984", "George Orwell"] ``` ```python from adaptix import Retort, name_mapping class Book: def __init__(self, title: str, author: str): self.title = title self.author = author retort = Retort( recipe=[ name_mapping(Book, as_list=True, map={"author": 0, "title": 1}) ] ) book = Book("1984", "George Orwell") assert retort.dump(book) == ["George Orwell", "1984"] ``` ```python from adaptix import Retort, name_mapping class Book: def __init__(self, title: str, author: str): self.title = title self.author = author retort = Retort( recipe=[ name_mapping(Book, map={"author": 0, "title": 1}) ] ) book = Book("1984", "George Orwell") assert retort.dump(book) == ["George Orwell", "1984"] ``` -------------------------------- ### Benchmark for Pydantic vs. Dataclass Instantiation Source: https://github.com/reagento/adaptix/blob/main/docs/why-not-pydantic.rst Benchmarks the time taken to create instances of Pydantic models versus standard Python dataclasses. This helps illustrate the performance overhead of Pydantic. ```python from timeit import timeit def instantiate_pydantic(): User(**external_data) def instantiate_dataclass(): UserDataclass(**external_data) print(f'pydantic {timeit(instantiate_pydantic, number=10000):.6f}') print(f'dataclass {timeit(instantiate_dataclass, number=10000):.6f}') ``` -------------------------------- ### Handling Default Values in Class Mapping Source: https://github.com/reagento/adaptix/blob/main/docs/why-not-pydantic.rst Demonstrates a scenario where field defaults in target models might lead to unexpected behavior during mapping. ```python from dataclasses import dataclass from adaptix import Retort @dataclass class Target: field: int = 10 retort = Retort() # This example illustrates a case where default values might be skipped # depending on the mapping configuration. print(retort.load({}, Target)) ```