### Basic apischema usage example Source: https://github.com/wyfo/apischema/blob/master/docs/index.md A quickstart example demonstrating the basic usage of the apischema library. It shows how apischema works out-of-the-box with defined data models. ```python from dataclasses import dataclass from apischema import schema, Json @dataclass class Point: x: int y: int # Generate JSON schema print(schema(Point)) # Serialize to JSON print(Json.encode(Point(1, 2))) # Deserialize from JSON print(Json.decode(Point, '{"x": 1, "y": 2}')) ``` -------------------------------- ### Install Apischema with Examples Source: https://github.com/wyfo/apischema/blob/master/README.md Command to install apischema along with its example dependencies, such as SQLAlchemy or attrs, using pip. ```shell pip install apischema[examples] ``` -------------------------------- ### Install Apischema Source: https://github.com/wyfo/apischema/blob/master/README.md Installation command for the apischema library using pip. It requires Python 3.8+ and also supports PyPy3. ```shell pip install apischema ``` -------------------------------- ### Install Apischema with GraphQL Support Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/overview.md This command installs Apischema with the necessary extra requirements to enable GraphQL support. This dependency is required to use Apischema's GraphQL generation capabilities. ```shell pip install apischema[graphql] ``` -------------------------------- ### Implement SQLAlchemy Support Source: https://github.com/wyfo/apischema/blob/master/docs/examples/sqlalchemy_support.md This snippet demonstrates the basic setup required to support SQLAlchemy models in apischema. It assumes an existing SQLAlchemy ORM configuration and shows how to map these models for serialization or validation. ```python from apischema.sqlalchemy import model_to_schema from my_models import User # Generate schema from SQLAlchemy model user_schema = model_to_schema(User) ``` -------------------------------- ### Implement Tagged Unions with apischema.tagged_unions.TaggedUnion Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/data_model_and_resolvers.md Provides an example of implementing the tagged union pattern using `apischema.tagged_unions.TaggedUnion`. Fields within the union must be typed using the `apischema.tagged_unions.Tagged` generic type. This feature is provisional. ```python from apischema.tagged_unions import TaggedUnion, Tagged class MyUnion(TaggedUnion): field1: Tagged[str] field2: Tagged[int] ``` -------------------------------- ### Apischema Example: Data Handling and Schema Generation Source: https://github.com/wyfo/apischema/blob/master/README.md Demonstrates JSON deserialization, serialization, validation, JSON schema generation, and GraphQL schema generation using apischema with Python dataclasses. It includes defining a data model, processing JSON data, generating schemas, and handling validation errors. ```python from collections.abc import Collection from dataclasses import dataclass, field from uuid import UUID, uuid4 import pytest from graphql import print_schema from apischema import ValidationError, deserialize, serialize from apischema.graphql import graphql_schema from apischema.json_schema import deserialization_schema # Define a schema with standard dataclasses @dataclass class Resource: id: UUID name: str tags: set[str] = field(default_factory=set) # Get some data uuid = uuid4() data = {"id": str(uuid), "name": "wyfo", "tags": ["some_tag"]} # Deserialize data resource = deserialize(Resource, data) assert resource == Resource(uuid, "wyfo", {"some_tag"}) # Serialize objects assert serialize(Resource, resource) == data # Validate during deserialization with pytest.raises(ValidationError) as err: # pytest checks exception is raised deserialize(Resource, {"id": "42", "name": "wyfo"}) assert err.value.errors == [ {"loc": ["id"], "err": "badly formed hexadecimal UUID string"} ] # Generate JSON Schema assert deserialization_schema(Resource) == { "$schema": "http://json-schema.org/draft/2020-12/schema#", "type": "object", "properties": { "id": {"type": "string", "format": "uuid"}, "name": {"type": "string"}, "tags": { "type": "array", "items": {"type": "string"}, "uniqueItems": True, "default": [], }, }, "required": ["id", "name"], "additionalProperties": False, } # Define GraphQL operations def resources(tags: Collection[str] | None = None) -> Collection[Resource] | None: ... # Generate GraphQL schema schema = graphql_schema(query=[resources], id_types={UUID}) schema_str = """ type Query { resources(tags: [String!]): [Resource!] } type Resource { id: ID! name: String! tags: [String!]! }""" assert print_schema(schema) == schema_str ``` -------------------------------- ### Configure Serialization Type Checking and Fallback Source: https://github.com/wyfo/apischema/blob/master/docs/de_serialization.md This example shows how to configure the `check_type` and `fall_back_on_any` parameters for `apischema.serialize`. `check_type` enforces that the object's type matches the expected serialized type, while `fall_back_on_any` allows falling back to `typing.Any` if a type mismatch occurs. These settings can also be modified globally. ```python from apischema import serialize from apischema.settings import serialization class MyClass: def __init__(self, value: int): self.value = value obj = MyClass(1) # Serialize with type checking enabled and fallback disabled print(serialize(obj, MyClass, check_type=True, fall_back_on_any=False)) # Serialize with type checking enabled and fallback enabled print(serialize(obj, MyClass, check_type=True, fall_back_on_any=True)) # Modify global settings serialization.check_type = True serialization.fall_back_on_any = True print(serialize(obj, MyClass)) ``` -------------------------------- ### Implement Subclass Union Deserialization in Python Source: https://github.com/wyfo/apischema/blob/master/docs/examples/subclass_union.md This example demonstrates how to register multiple deserializers for a base class using apischema. When the deserializer is invoked, it attempts to match the input data against the registered subclasses to return the correct type. ```python from apischema import deserializer from dataclasses import dataclass from typing import Union @dataclass class Base: pass @dataclass class SubA(Base): a: int @dataclass class SubB(Base): b: str deserializer(SubA) deserializer(SubB) # Base is now effectively Union[SubA, SubB] ``` -------------------------------- ### Discriminator Performance Example Source: https://github.com/wyfo/apischema/blob/master/docs/optimizations_and_benchmark.md Demonstrates the performance benefits of using discriminators for union deserialization in apischema. While there's an initial cost, it leads to more homogeneous deserialization times. ```python from apischema import Union, Schema, Dict, str from apischema.json_schema import Schema class Base: pass class A(Base): a: int class B(Base): b: str Schema(Union[A, B], discriminator='type').validate({'type': 'A', 'a': 1}) Schema(Union[A, B], discriminator='type').validate({'type': 'B', 'b': 'hello'}) ``` -------------------------------- ### Define Tagged Union using Subclasses Source: https://github.com/wyfo/apischema/blob/master/docs/examples/subclass_tagged_union.md This example demonstrates how to define a class hierarchy where the base class acts as a tagged union. It utilizes apischema's metadata to map subclasses to specific tags for serialization and deserialization. ```python from typing import Union from apischema import tagged from apischema.metadata import alias class Base: pass @tagged("A") class SubA(Base): x: int @tagged("B") class SubB(Base): y: str UnionType = Union[SubA, SubB] ``` -------------------------------- ### Pydantic Validator Example Source: https://github.com/wyfo/apischema/blob/master/docs/difference_with_pydantic.md This snippet demonstrates a validator as typically written in pydantic, showcasing its approach to handling data validation within models. It serves as a reference point for comparing with apischema's validator implementation. ```python from pydantic import BaseModel, validator class Model(BaseModel): field: str @validator('field') def check_field(cls, v): if v != 'expected_value': raise ValueError('field must be expected_value') return v ``` -------------------------------- ### Python Example for Recoverable Fields Source: https://github.com/wyfo/apischema/blob/master/docs/examples/recoverable_fields.md This Python code snippet demonstrates the implementation of recoverable fields within an API schema. It shows how to define fields that can be recovered or updated, potentially useful for partial updates or versioning. ```python from typing import Optional from pydantic import BaseModel class User(BaseModel): id: int name: str = 'John Doe' signup_ts: Optional[float] = None friends: list[int] = [] extra_fields = { 'email': 'user@example.com', 'age': 30 } user = User(**{ 'id': 1, 'friends': [1, 2, '3'], **extra_fields }) print(user.id) print(user.friends) print(user.email) print(user.age) # You can also pass extra fields in a dict print(user.dict(exclude_unset=True)) # You can also pass extra fields in a dict print(user.dict(exclude_none=True)) ``` -------------------------------- ### Deserialize JSON to Python Objects Source: https://context7.com/wyfo/apischema/llms.txt Demonstrates converting JSON-compatible data into typed Python objects using the deserialize function. It includes examples for basic types, NewTypes, and nested collections, while highlighting validation error handling. ```python from collections.abc import Collection, Mapping from dataclasses import dataclass from typing import NewType from apischema import deserialize, ValidationError @dataclass class User: name: str age: int user = deserialize(User, {"name": "Alice", "age": 30}) assert user == User("Alice", 30) UserId = NewType("UserId", int) assert deserialize(UserId, 42) == 42 data = {"users": [{"name": "Bob", "age": 25}]} result = deserialize(Mapping[str, Collection[User]], data) assert result == {"users": [User("Bob", 25)]} try: deserialize(User, {"name": "Alice", "age": "not_an_int"}) except ValidationError as err: print(err.errors) ``` -------------------------------- ### Flexible Type Coercion with `coerce` Source: https://context7.com/wyfo/apischema/llms.txt Illustrates enabling type coercion during deserialization to automatically convert between compatible types. This is useful for parsing configuration files or handling loosely-typed input, with examples for integers, floats, and booleans, including custom boolean mappings. ```Python from apischema import deserialize, ValidationError # Without coercion, type must match exactly try: deserialize(int, "42") except ValidationError: print("String '42' is not an int") # With coercion, compatible types are converted assert deserialize(int, "42", coerce=True) == 42 assert deserialize(float, "3.14", coerce=True) == 3.14 assert deserialize(bool, "yes", coerce=True) == True assert deserialize(bool, "no", coerce=True) == False # Boolean coercion mapping (case-insensitive): # False: 0, f, n, no, false, off, ko # True: 1, t, y, yes, true, on, ok assert deserialize(bool, "OK", coerce=True) == True assert deserialize(bool, "FALSE", coerce=True) == False ``` -------------------------------- ### Handle Errors in Serialized Methods Source: https://github.com/wyfo/apischema/blob/master/docs/de_serialization.md This example shows how to implement error handling for serialized methods using the `error_handler` parameter in the `apischema.serialized` decorator. The handler function receives the exception, object, and alias, and can return a new value or re-raise an exception. If `error_handler` is `None`, exceptions result in `None` being serialized. ```python from dataclasses import dataclass from apischema import serialized, Undefined from apischema.error_handler import ErrorHandler def custom_error_handler(ex: Exception, obj: object, alias: str) -> object: print(f"Error processing {alias}: {ex}") return None @dataclass class Data: value: int @serialized(error_handler=custom_error_handler) def process_value(self) -> int: if self.value < 0: raise ValueError("Value cannot be negative") return self.value * 2 @serialized(error_handler=None) def safe_value(self) -> int: if self.value < 0: raise ValueError("Value cannot be negative") return self.value print(Data(5).to_dict()) print(Data(-5).to_dict()) ``` -------------------------------- ### Using Conversion Objects Source: https://github.com/wyfo/apischema/blob/master/docs/conversions.md Shows how to register a conversion using the Conversion class instance, which allows for additional metadata and explicit type specification when annotations are insufficient. ```python from apischema.conversions import Conversion, register_deserializer class Target: pass def converter(data: str) -> Target: return Target() register_deserializer(Conversion(converter, source=str, target=Target)) ``` -------------------------------- ### Apply Dynamic Conversions at Runtime Source: https://github.com/wyfo/apischema/blob/master/docs/conversions.md Demonstrates how to provide conversions at runtime using the conversion parameter in serialization and deserialization functions. These conversions are local and discarded after use. ```python from apischema import deserialize # Example usage of dynamic conversion result = deserialize(TargetType, data, conversion=my_conversion) ``` -------------------------------- ### Implement GraphQL Subscriptions Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/schema.md Covers the implementation of subscriptions using AsyncIterable, both with and without dedicated post-processing resolvers. ```python from typing import AsyncIterable from apischema.graphql import Subscription async def event_generator() -> AsyncIterable[Event]: ... sub = Subscription(event_generator) ``` -------------------------------- ### Configure Enum Metadata Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/schema.md Demonstrates how to provide descriptions and deprecation reasons for Enum members using the enum_schemas parameter. ```python from enum import Enum from apischema.graphql import graphql_schema, EnumSchema class Status(Enum): ACTIVE = 1 schema = graphql_schema(enum_schemas={Status: EnumSchema(description="..." )}) ``` -------------------------------- ### Define GraphQL Operations Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/schema.md Demonstrates how to define GraphQL operations using Query, Mutation, and Subscription wrappers to map Python functions to schema operations. ```python from apischema.graphql import Query, Mutation, Subscription, graphql_schema def get_user(id: int) -> User: ... schema = graphql_schema(queries=[Query(get_user)]) ``` -------------------------------- ### Registering Conversions with Decorators Source: https://github.com/wyfo/apischema/blob/master/docs/conversions.md Demonstrates how to use @apischema.deserializer and @apischema.serializer decorators to register conversion functions. The source and target types are inferred directly from the function signatures. ```python from apischema import deserializer, serializer class MyClass: def __init__(self, value: str): self.value = value @deserializer def deserialize_my_class(data: str) -> MyClass: return MyClass(data) @serializer def serialize_my_class(obj: MyClass) -> str: return obj.value ``` -------------------------------- ### Serialize Methods in Generic Classes Source: https://github.com/wyfo/apischema/blob/master/docs/de_serialization.md This example illustrates how serialized methods in generic classes correctly infer their types when the generic class is specialized. Apischema ensures that type annotations are preserved and correctly applied even when dealing with generic type parameters. ```python from dataclasses import dataclass from typing import Generic, TypeVar from apischema import serialized T = TypeVar('T') @dataclass class GenericContainer(Generic[T]): value: T @serialized def get_value_type(self) -> type: return type(self.value) container_int = GenericContainer(10) print(container_int.to_dict()) container_str = GenericContainer("hello") print(container_str.to_dict()) ``` -------------------------------- ### Implementing Custom Type Support with Apischema Source: https://github.com/wyfo/apischema/blob/master/docs/difference_with_pydantic.md Demonstrates how to register a conversion for a custom type in apischema, allowing seamless serialization and deserialization of types not natively supported by standard libraries. ```python from apischema import conversion from bson import ObjectId # Register conversion for ObjectId conversion.register_deserializer(ObjectId) conversion.register_serializer(str, ObjectId) ``` -------------------------------- ### Precomputed Deserialization Methods in Python Source: https://github.com/wyfo/apischema/blob/master/docs/optimizations_and_benchmark.md Demonstrates how apischema precomputes and caches deserialization methods using functools.lru_cache for improved performance. It also shows the direct usage of apischema.deserialization_method for further speed gains. ```python from apischema import deserialize, serialize, deserialization_method, serialization_method from apischema.settings import get_settings def deserialize_int(data: str) -> int: return deserialization_method(int)(data) def serialize_int(obj: int) -> str: return serialization_method(int)(obj) # Example usage: serialized_data = serialize_int(123) print(f"Serialized: {serialized_data}") deserialized_obj = deserialize_int(serialized_data) print(f"Deserialized: {deserialized_obj}") # Using deserialize and serialize directly (cached) print(deserialize(int, '123')) print(serialize(int, 123)) # Modifying settings resets the cache settings = get_settings() some_setting = settings.deserialization.some_setting settings.deserialization.some_setting = not some_setting # The cache is now reset, methods will be recomputed if called again. ``` -------------------------------- ### Configuring Serialization Passthrough Options in Python Source: https://github.com/wyfo/apischema/blob/master/docs/optimizations_and_benchmark.md Demonstrates how to use `apischema.PassThroughOptions` to specify which types should be passed through during serialization, leveraging native JSON library support or a `default` fallback. This includes options for `any`, `collections`, `dataclasses`, and `enums`. ```python from apischema import serialization_method, PassThroughOptions, serialization_default from apischema.settings import get_settings import uuid import datetime # Example: Pass through UUID and datetime using orjson's native support # Assuming orjson is installed and configured as the underlying serializer # Define passthrough options passthrough_options = PassThroughOptions( any=True, # Pass through Any type collections=True, # Pass through collections like list, dict, tuple dataclasses=True, # Pass through dataclasses if they meet criteria enums=True, # Pass through Enum subclasses # Specify types to pass through natively or via default uuid=True, datetime=True ) # Create a serialization_default instance with these options # If using a specific serializer like orjson, ensure it's configured. # serialization_default handles fallback if native support is not found. # Instantiate with same kwargs as serialization_method # For simplicity, let's assume default settings for other kwargs like aliaser # This part requires a concrete serializer setup, e.g., using orjson # For demonstration, we show the setup conceptually: # Assuming 'my_serializer' is an instance configured to use passthrough_options # For example, if using a custom serializer or a library that integrates with apischema: # Example of how serialization_default might be used with a default handler: # default_handler = serialization_default(passthrough_options=passthrough_options) # Then, when calling serialization_method or serialize: # serialized_value = serialize(my_object, default=default_handler) # Direct usage with serialization_method for a specific type: # If you have a custom default handler that supports UUID: # custom_default = serialization_default(passthrough_options=passthrough_options) # serialized_uuid = serialization_method(uuid.UUID, default=custom_default)(uuid.uuid4()) # Note: Actual implementation depends on the underlying JSON library and apischema integration. # The key is to pass PassThroughOptions to serialization_default or the serializer configuration. ``` -------------------------------- ### Customizing Connection and Edge Types Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/relay.md Demonstrates how to create custom connection and edge types by subclassing the generic relay.Connection and relay.Edge classes. Custom fields can be added to these subclasses to extend their functionality. ```python from dataclasses import dataclass from typing import Generic, Optional, Sequence, TypeVar from apischema.graphql import relay Node = TypeVar('Node') Cursor = TypeVar('Cursor') @dataclass class CustomEdge(relay.Edge[Node, Cursor]): # Add custom fields to the edge custom_edge_field: str @dataclass class CustomConnection(relay.Connection[Node, Cursor, CustomEdge[Node, Cursor]]): # Add custom fields to the connection custom_connection_field: int ``` -------------------------------- ### Define Resolver Parameter Metadata with typing.Annotated Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/data_model_and_resolvers.md Demonstrates how to add metadata to resolver parameters using `typing.Annotated`. This allows for richer parameter definitions similar to dataclass fields. Dependencies include the `typing` module. ```python from typing import Annotated def my_resolver(param: Annotated[str, "some metadata"]) -> str: return f"Received: {param}" ``` -------------------------------- ### Dynamic Conversions API Source: https://github.com/wyfo/apischema/blob/master/docs/conversions.md Explains how to apply conversions at runtime using the conversion parameter in serialization and deserialization functions. ```APIDOC ## POST /deserialize or /serialize ### Description Applies a conversion to a type at runtime, overriding or supplementing registered conversions for the duration of the call. ### Method POST ### Parameters #### Request Body - **conversion** (callable/tuple) - Optional - The conversion function or tuple of conversions to apply. - **default_conversion** (callable) - Optional - Dynamically modifies the default conversion behavior. ### Request Example { "data": { "key": "value" }, "conversion": "my_conversion_function" } ### Response #### Success Response (200) - **result** (any) - The transformed data after applying the dynamic conversion. ``` -------------------------------- ### Validate Constraints and Schema Generation Source: https://context7.com/wyfo/apischema/llms.txt Demonstrates how to validate data against constraints during deserialization and how to extract the underlying JSON schema from a dataclass. ```python from apischema import deserialize, ValidationError from apischema.json_schema import deserialization_schema try: deserialize(Resource, {"id": 1, "name": "", "tags": ["ab"]}) except ValidationError as err: for error in err.errors: print(f"{error['loc']}: {error['err']}") result = deserialization_schema(Resource) assert result["properties"]["tags"]["maxItems"] == 5 ``` -------------------------------- ### Perform Basic Deserialization Source: https://github.com/wyfo/apischema/blob/master/docs/de_serialization.md Demonstrates how to deserialize JSON-like data into Python types such as dataclasses or standard types using apischema.deserialize. ```python from apischema import deserialize # Example usage data = {"name": "John", "age": 30} # Assuming a dataclass User exists user = deserialize(User, data) ``` -------------------------------- ### Dataclass Additional Properties with Metadata Source: https://github.com/wyfo/apischema/blob/master/docs/json_schema.md Demonstrates how to extend dataclasses with 'additionalProperties' and 'patternProperties' using metadata. Properties not matching regular fields are deserialized into a 'Mapping' type field. ```python from dataclasses import dataclass from typing import Mapping from apischema import schema @dataclass class Foo: x: int properties: Mapping assert schema(Foo) == { 'type': 'object', 'properties': { 'x': {'type': 'integer'} }, 'additionalProperties': True } ``` -------------------------------- ### Include Additional Types Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/schema.md Shows how to manually include types in the schema that are not directly referenced in resolver signatures. ```python schema = graphql_schema(types=[ExtraType, AnotherType]) ``` -------------------------------- ### Registering Lazy Conversions in Apischema Source: https://github.com/wyfo/apischema/blob/master/docs/conversions.md Demonstrates how to register lazy conversions in Apischema. This requires passing the deserialization target and serialization source explicitly. ```python from apischema import deserialize, schema, JsonType from apischema.conversions import lazy_registered_conversion class MyInt(int): def __str__(self): return str(self) @lazy_registered_conversion def my_int_conversion(target: type[MyInt], source: type[int]) -> None: pass @schema class MySchema: value: MyInt assert deserialize(MySchema, {"value": 1}) ``` -------------------------------- ### Dataclass with Additional Properties Source: https://github.com/wyfo/apischema/blob/master/docs/json_schema.md Demonstrates how to use `properties` metadata in dataclasses to handle `additionalProperties` and `patternProperties` from JSON schema. ```APIDOC ## Dataclass with Additional Properties ### Description Use `properties` metadata on dataclass fields to deserialize properties not explicitly defined in the dataclass. These fields must be of type `Mapping` or deserializable from a mapping. ### Method N/A (Configuration) ### Endpoint N/A ### Parameters #### Request Body - **properties** (dict) - Metadata field for additional properties. ### Request Example ```python from dataclasses import dataclass, field from typing import Mapping @dataclass class MyData: regular_field: str properties: Mapping = field(metadata=dict(properties=True)) ``` ### Response #### Success Response (200) N/A #### Response Example N/A ``` -------------------------------- ### Using InitVar in Validators Source: https://github.com/wyfo/apischema/blob/master/docs/validation.md Demonstrates accessing InitVar fields within validators, similar to how they are used in __post_init__. ```python {!validator_post_init.py!} ``` -------------------------------- ### Field and Method Conversions Source: https://github.com/wyfo/apischema/blob/master/docs/conversions.md Details how to register specific conversions for dataclass fields or serialized method returns. ```APIDOC ## Metadata: conversion ### Description Registers a conversion specifically for a dataclass field or a serialized method return value using metadata. ### Usage - **Field Metadata**: Use `field(metadata={"conversion": my_conv})` within a dataclass. - **Serialized Method**: Apply conversion logic to the return value of a method decorated with `@serialized`. ### Parameters - **conversion** (Conversion) - Required - The conversion object to apply to the field or method output. ``` -------------------------------- ### Configure Relay ClientMutationId Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/relay.md Demonstrates how to use the _client_mutation_id class variable to control the presence and nullability of the clientMutationId field in a mutation. It shows how the field is automatically forwarded from input to payload. ```python from apischema.graphql import relay class MyMutation(relay.Mutation): _client_mutation_id = True def mutate(self, client_mutation_id: relay.ClientMutationId, data: str) -> str: return data ``` -------------------------------- ### Define Resolver Parameter Base Schema Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/data_model_and_resolvers.md Shows how to define a base schema for resolver parameters, analogous to type or field base schemas. This promotes consistency in parameter definitions. It relies on the `apischema` library. ```python from apischema import schema def my_resolver(param: str = schema.Field(default="default_value")) -> str: return f"Received: {param}" ``` -------------------------------- ### Property Dependencies Source: https://github.com/wyfo/apischema/blob/master/docs/json_schema.md Explains how to define and use property dependencies in Apischema for dataclasses, ensuring specific properties are present when others are. ```APIDOC ## Property Dependencies ### Description Apischema supports JSON schema's property dependencies for dataclasses using a class member. This feature is also used for validation. A shortcut notation `dependent_required` is available for bidirectional dependencies. ### Method N/A (Configuration) ### Endpoint N/A ### Parameters #### Request Body - **dependent_required** (list[str]) - List of fields that are required based on the presence of other fields. ### Request Example ```python from dataclasses import dataclass, field from typing import List @dataclass class Payment: amount: float credit_card: str | None = None billing_address: str | None = None dependent_required: List[str] = field(default_factory=lambda: ["credit_card", "billing_address"]) # Shortcut notation @dataclass class PaymentShortcut: amount: float credit_card: str | None = None billing_address: str | None = None dependent_required = ["credit_card", "billing_address"] ``` ### Response #### Success Response (200) N/A #### Response Response Example N/A ``` -------------------------------- ### Implement Generic Types in Apischema Source: https://github.com/wyfo/apischema/blob/master/docs/data_model.md Demonstrates the usage of typing.Generic with apischema. Note that generic types require explicit type name assignment for schema generation. ```python {!generic.py!} ``` -------------------------------- ### Handle None as Undefined Source: https://github.com/wyfo/apischema/blob/master/docs/data_model.md Demonstrates the use of none_as_undefined metadata to treat None values as absent fields, preventing them from being serialized as nullable types. ```python {!none_as_undefined.py!} ``` -------------------------------- ### Apply Schema Annotations Source: https://github.com/wyfo/apischema/blob/master/docs/json_schema.md Uses the schema decorator to add metadata like patterns, min/max values, and descriptions to types or fields. ```python from apischema import schema from typing import Annotated UserId = Annotated[str, schema(pattern=r"^ID-\d+$")] ``` -------------------------------- ### Generate JSON Schema from Data Model Source: https://github.com/wyfo/apischema/blob/master/docs/json_schema.md Demonstrates how to generate serialization and deserialization schemas from a defined data model using apischema. ```python from apischema import deserialization_schema, serialization_schema from dataclasses import dataclass @dataclass class User: id: int name: str print(deserialization_schema(User)) print(serialization_schema(User)) ``` -------------------------------- ### POST /conversions/as-str Source: https://github.com/wyfo/apischema/blob/master/docs/conversions.md Registers a string-based deserializer and serializer for classes that support string constructors and __str__ methods. ```APIDOC ## POST /conversions/as-str ### Description Uses apischema.conversions.as_str to register a string-deserializer and string-serializer. Automatically converts ValueError to ValidationError. ### Method POST ### Endpoint /conversions/as-str ### Parameters #### Request Body - **cls** (class) - Required - The class to register for string conversion. ### Request Example { "cls": "uuid.UUID" } ### Response #### Success Response (200) - **message** (string) - Registration successful. ``` -------------------------------- ### Apply Class-Level Aliasing Source: https://github.com/wyfo/apischema/blob/master/docs/json_schema.md Demonstrates how to apply a global aliasing function to all fields within a class to enforce specific naming conventions. ```python from dataclasses import dataclass from apischema import aliaser @aliaser(lambda s: s.upper()) @dataclass class Config: host: str port: int ``` -------------------------------- ### Control Field Ordering with Apischema (Python) Source: https://github.com/wyfo/apischema/blob/master/docs/de_serialization.md Explains how to control the order of fields in serialized JSON output using `apischema.order`. This can be applied at the class level as a decorator or at the field level using metadata, with options to specify exact order, or relative order using `after` and `before`. ```Python from dataclasses import dataclass, field from apischema import serialize, order, metadata # Class-level ordering @order({'b': 0, 'a': 1}) @dataclass class DataClassOrdered: a: int = 1 b: int = 2 print(serialize(DataClassOrdered())) # Output: {"b": 2, "a": 1} # Field-level ordering using metadata @dataclass class DataFieldOrdered: a: int = field(default=1, metadata=metadata(order=1)) b: int = field(default=2, metadata=metadata(order=-1)) c: int = field(default=3) print(serialize(DataFieldOrdered())) # Output: {"b": 2, "a": 1, "c": 3} # Field-level ordering using after/before @dataclass class DataOrderAfterBefore: a: int = 1 b: int = field(default=2, metadata=metadata(order=order(after='a'))) c: int = 3 print(serialize(DataOrderAfterBefore())) # Output: {"a": 1, "b": 2, "c": 3} @dataclass class DataOrderBefore: a: int = 1 b: int = field(default=2, metadata=metadata(order=order(before='a'))) c: int = 3 print(serialize(DataOrderBefore())) # Output: {"b": 2, "a": 1, "c": 3} ``` -------------------------------- ### Generic Conversions Source: https://github.com/wyfo/apischema/blob/master/docs/conversions.md Demonstrates the use of generic types in conversion functions. Apischema supports generic conversions natively, provided they are not specialized (e.g., Foo[int]). ```python from typing import TypeVar, Generic from apischema import deserializer T = TypeVar("T") class Box(Generic[T]): def __init__(self, item: T): self.item = item @deserializer def deserialize_box(data: T) -> Box[T]: return Box(data) ``` -------------------------------- ### Set Custom Object Fields using set_object_fields in Python Source: https://github.com/wyfo/apischema/blob/master/docs/data_model.md Demonstrates how to explicitly map a class to its list of ObjectField instances using the `set_object_fields` function. This allows Apischema to correctly handle custom or non-standard object types by defining their structure. ```python # Assuming set_object_fields is imported from apischema.objects # and MyClass is a custom class # my_class_fields = [ # ObjectField(name="field1", type=str), # ObjectField(name="field2", type=int, required=False) # ] # set_object_fields(MyClass, my_class_fields) ``` -------------------------------- ### Generic Type Support in apischema Source: https://context7.com/wyfo/apischema/llms.txt Shows how apischema fully supports generic types, enabling the creation of reusable type-safe containers with proper (de)serialization and schema generation. It covers defining generic classes, registering deserializers, and how serialization and schema generation adapt to type parameters. ```Python from typing import Generic, TypeVar from apischema import deserialize, serialize, ValidationError from apischema.conversions import deserializer, serializer from apischema.json_schema import deserialization_schema T = TypeVar("T") class Wrapper(Generic[T]): def __init__(self, value: T): self.value = value @serializer def unwrap(self) -> T: return self.value # Constructor works as deserializer deserializer(Wrapper) # Works with any type parameter int_wrapper = deserialize(Wrapper[int], 42) assert int_wrapper.value == 42 list_wrapper = deserialize(Wrapper[list[str]], ["a", "b"]) assert list_wrapper.value == ["a", "b"] # Serialization unwraps the value assert serialize(Wrapper[str], Wrapper("hello")) == "hello" # Schema reflects the type parameter schema = deserialization_schema(Wrapper[int]) assert schema == { "$schema": "http://json-schema.org/draft/2020-12/schema#", "type": "integer" } ``` -------------------------------- ### Complex Schema with $ref References Source: https://github.com/wyfo/apischema/blob/master/docs/json_schema.md Shows how to handle complex JSON schemas with type reuse by extracting definitions and using $ref pointers. This is mandatory for recursive types and convenient for reusable schema components. ```python from dataclasses import dataclass from apischema import schema @dataclass class Foo: bar: 'Foo' assert schema(Foo) == { '$ref': '#/$defs/Foo', '$defs': { 'Foo': { 'type': 'object', 'properties': { 'bar': {'$ref': '#/$defs/Foo'} } } } } ``` -------------------------------- ### Connection and Edge Types for Pagination Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/relay.md Provides generic apischema.graphql.relay.Connection and apischema.graphql.relay.Edge types for implementing pagination. The Connection type includes edges, pagination information, and start/end cursors. It automatically computes pageInfo via a resolver. ```python from dataclasses import dataclass, field from typing import Generic, Optional, Sequence, TypeVar from apischema.metadata import skip from apischema.graphql.resolvers import resolver Node = TypeVar('Node') Cursor = TypeVar('Cursor') Edge = TypeVar('Edge') @dataclass class PageInfo(Generic[Cursor]): has_previous_page: bool has_next_page: bool start_cursor: Optional[Cursor] = None end_cursor: Optional[Cursor] = None @dataclass class Edge(Generic[Node, Cursor]): node: Node cursor: Cursor @dataclass class Connection(Generic[Node, Cursor, Edge]): edges: Optional[Sequence[Optional[Edge]]] has_previous_page: bool = field(default=False, metadata=skip) has_next_page: bool = field(default=False, metadata=skip) start_cursor: Optional[Cursor] = field(default=None, metadata=skip) end_cursor: Optional[Cursor] = field(default=None, metadata=skip) @resolver def page_info(self) -> PageInfo[Cursor]: ... ``` -------------------------------- ### Reference Factory Source: https://github.com/wyfo/apischema/blob/master/docs/json_schema.md Explains the role of `ref_factory` in Apischema for constructing the format of JSON schema `$ref` pointers, and how it interacts with definition generation. ```APIDOC ## Reference Factory ### Description The `ref_factory` parameter in schema generation functions determines how type names are converted into JSON schema `$ref` pointers (e.g., `#/$defs/Foo` or `#/components/schema/Foo`). When a custom `ref_factory` is provided, definitions are not automatically added to the schema. ### Method N/A (Configuration) ### Endpoint N/A ### Parameters #### Request Body - **ref_factory** (callable) - A function that takes a type name and returns the formatted `$ref` string. ### Request Example ```python from apischema import deserialization_schema def custom_ref_factory(name: str) -> str: return f"#/my_definitions/{name}" # Assuming MyType is defined # schema = deserialization_schema(MyType, ref_factory=custom_ref_factory) ``` ### Response #### Success Response (200) N/A #### Response Example N/A ``` -------------------------------- ### Configure Global CamelCase Settings Source: https://github.com/wyfo/apischema/blob/master/docs/json_schema.md Enables camelCase naming conventions globally across the application using apischema settings. ```python from apischema import settings settings.camel_case = True ``` -------------------------------- ### Handle Union Type Names Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/schema.md Explains how to name Union types in GraphQL, which is a requirement for valid schemas, using type_name or union_ref. ```python from typing import Union from apischema import type_name @type_name(graphql="MyUnion") class UnionType = Union[TypeA, TypeB] ``` -------------------------------- ### String Conversion Helper (as_str) in Apischema Source: https://github.com/wyfo/apischema/blob/master/docs/conversions.md Utilizes the `as_str` helper to register string-based deserialization from a constructor and serialization from a `__str__` method. It handles `ValueError` by converting it to `ValidationError`. ```python from apischema.conversions import as_str from apischema.errors import ValidationError import uuid from pathlib import Path import ipaddress @as_str class UUID(uuid.UUID): pass @as_str class Path(Path): pass @as_str class IPv4Address(ipaddress.IPv4Address): pass assert UUID("123e4567-e89b-12d3-a456-426614174000") == uuid.UUID("123e4567-e89b-12d3-a456-426614174000") assert Path("a/b") == Path("a/b") assert IPv4Address("127.0.0.1") == ipaddress.IPv4Address("127.0.0.1") try: UUID("invalid-uuid") except ValidationError: pass ``` -------------------------------- ### Handling Multiple Deserializers Source: https://github.com/wyfo/apischema/blob/master/docs/conversions.md Shows how to register multiple deserializers for the same target type. Apischema treats these as a Union, attempting to match the input to the appropriate deserializer. ```python from apischema import deserializer class Target: def __init__(self, val): self.val = val @deserializer def from_int(val: int) -> Target: return Target(val) @deserializer def from_str(val: str) -> Target: return Target(int(val)) ``` -------------------------------- ### Apply JSON Schema Constraints Source: https://context7.com/wyfo/apischema/llms.txt Demonstrates using the schema function to add validation constraints like minimum length, patterns, and descriptions to types and dataclass fields. ```python from dataclasses import dataclass, field from typing import NewType from apischema import schema Tag = NewType("Tag", str) schema(min_len=3, pattern=r"^\w+$", examples=["python", "api"])(Tag) @dataclass class Resource: id: int name: str = field(metadata=schema(min_len=1, max_len=100)) tags: list[Tag] = field( default_factory=list, metadata=schema(description="Resource tags", max_items=5, unique=True) ) ``` -------------------------------- ### Implementing Custom Dataclass Validators Source: https://github.com/wyfo/apischema/blob/master/docs/validation.md Illustrates how to add custom validation logic to dataclasses using decorated methods. These validators run after field deserialization and can raise exceptions or yield errors for more complex validation scenarios. ```python from apischema import deserialize, validator from dataclasses import dataclass @dataclass class User: name: str age: int @validator def name_is_not_empty(self): if not self.name: raise ValueError("name cannot be empty") def test_validator(): deserialize(User, {"name": "", "age": 10}) test_validator() ``` -------------------------------- ### Definitions Schema Source: https://github.com/wyfo/apischema/blob/master/docs/json_schema.md Shows how to extract definition schemas separately using `definitions_schema` in Apischema, which is useful for generating OpenAPI components. ```APIDOC ## Definitions Schema ### Description Use `apischema.json_schema.definitions_schema` to extract a dictionary of all referenced schemas (definitions). This is particularly useful for generating the `components/schema` section in OpenAPI specifications. ### Method N/A (Utility Function) ### Endpoint N/A ### Parameters #### Request Body - **deserialization_types** (list[type]) - List of types for which to generate deserialization definitions. - **serialization_types** (list[type]) - List of types for which to generate serialization definitions. ### Request Example ```python from apischema.json_schema import definitions_schema # Assuming MyType and OtherType are defined # definitions = definitions_schema([MyType], [OtherType]) ``` ### Response #### Success Response (200) - **definitions** (dict) - A dictionary containing the schema definitions. #### Response Example ```json { "MyType": { ... schema for MyType ... }, "OtherType": { ... schema for OtherType ... } } ``` ``` -------------------------------- ### Integrate Pydantic models with apischema Source: https://github.com/wyfo/apischema/blob/master/docs/examples/pydantic_support.md This snippet demonstrates how to register Pydantic models with apischema using the conversions feature. It ensures that Pydantic's internal logic is preserved while enabling apischema's integration features. ```python from pydantic import BaseModel from apischema.conversions import conversion # Example of Pydantic integration @conversion(source=BaseModel, target=dict) def pydantic_to_dict(model: BaseModel) -> dict: return model.dict() @conversion(source=dict, target=BaseModel) def dict_to_pydantic(data: dict, model_cls: type[BaseModel]) -> BaseModel: return model_cls(**data) ``` -------------------------------- ### Manage Undefined vs Null Values Source: https://github.com/wyfo/apischema/blob/master/docs/data_model.md Explains how to use the Undefined constant to distinguish between null fields and absent fields, which is useful for PATCH operations. ```python {!undefined.py!} ``` -------------------------------- ### Object Identification with relay.Node Source: https://github.com/wyfo/apischema/blob/master/docs/graphql/relay.md Defines a generic relay.Node interface for identified resources. Each node must implement get_by_id and can be retrieved using relay.nodes and the relay.node query. Ensure relay.nodes is called before schema generation. ```python from dataclasses import dataclass from typing import Generic, TypeVar import graphql from apischema import schema from apischema.graphql import relay class Node(Generic[Id]): @classmethod def get_by_id(cls: type[T], id: Id, info: graphql.GraphQLResolveInfo = None) -> T: raise NotImplementedError @dataclass class GlobalId(Generic[Node]): id: str node_class: type[Node] @dataclass class PageInfo(Generic[Cursor]): has_previous_page: bool has_next_page: bool start_cursor: Optional[Cursor] = None end_cursor: Optional[Cursor] = None @dataclass class Edge(Generic[Node, Cursor]): node: Node cursor: Cursor @dataclass class Connection(Generic[Node, Cursor, Edge]): edges: Optional[Sequence[Optional[Edge]]] has_previous_page: bool = field(default=False, metadata=skip) has_next_page: bool = field(default=False, metadata=skip) start_cursor: Optional[Cursor] = field(default=None, metadata=skip) end_cursor: Optional[Cursor] = field(default=None, metadata=skip) @resolver def page_info(self) -> PageInfo[Cursor]: ... class Mutation(Generic[Payload]): @classmethod def mutate(cls, *args, **kwargs) -> Payload: ... # Example usage class User(relay.Node[int]): name: str @classmethod def get_by_id(cls, id: int, info: graphql.GraphQLResolveInfo = None) -> "User": # Fetch user from database return User(id=id, name="John Doe") users = [User(id=1, name="John Doe"), User(id=2, name="Jane Smith")] @dataclass class UserConnection(relay.Connection[User, str, relay.Edge[User, str]]): ... @dataclass class CreateUser(relay.Mutation[User]): name: str @classmethod def mutate(cls, name: str) -> User: # Create user in database new_user = User(id=3, name=name) users.append(new_user) return new_user schema( types=relay.nodes([User]), queries=relay.node, mutations=relay.mutations([CreateUser]), id_encoding=relay.base64_encoding, ) ``` -------------------------------- ### Define Recursive Types with Annotations Source: https://github.com/wyfo/apischema/blob/master/docs/data_model.md Shows how to define recursive classes using standard string annotations or PEP 563 postponed evaluation. Requires careful handling of scope for type resolution. ```python {!recursive.py!} ``` ```python {!recursive_postponned.py!} ```