Skip to content

Fields API Reference

This page provides API documentation for the pydapter.fields module.

Installation

pip install pydapter

Overview

The fields module provides tools for building robust, reusable model definitions:

┌─────────────────┐  ┌─────────────────┐  ┌─────────────────┐
│      Field      │  │  FieldTemplate  │  │  FieldFamilies  │
│  (Descriptor)   │  │   (Reusable)    │  │   (Collections) │
└─────────────────┘  └─────────────────┘  └─────────────────┘

┌─────────────────┐  ┌─────────────────┐
│DomainModelBuilder│  │ValidationPatterns│
│   (Fluent API)  │  │   (Validators)  │
└─────────────────┘  └─────────────────┘

Quick Start

from pydapter.fields import DomainModelBuilder, FieldTemplate

# Build models with field families
User = (
    DomainModelBuilder("User")
    .with_entity_fields()                    # id, created_at, updated_at
    .with_audit_fields()                     # created_by, updated_by, version
    .add_field("name", FieldTemplate(base_type=str))
    .add_field("email", FieldTemplate(base_type=str))
    .build()
)

Field Templates

from pydapter.fields import FieldTemplate

# Reusable field configuration
email_template = FieldTemplate(
    base_type=str,
    description="Email address",
    validator=lambda cls, v: v.lower()
)

# Create variations
user_email = email_template.create_field("user_email")
optional_email = email_template.as_nullable()
email_list = email_template.as_listable()

Field Families

from pydapter.fields import FieldFamilies

# Pre-defined collections
entity_fields = FieldFamilies.ENTITY        # id, created_at, updated_at
audit_fields = FieldFamilies.AUDIT          # created_by, updated_by, version
soft_delete_fields = FieldFamilies.SOFT_DELETE  # deleted_at, is_deleted

Model Creation

from pydapter.fields import create_model, Field

# Create models with field lists
fields = [
    Field(name="id", annotation=str),
    Field(name="name", annotation=str),
    Field(name="email", annotation=str)
]

User = create_model("User", fields=fields)

API Reference

Core Types

pydapter.fields.types

Classes

Field

Field descriptor for Pydantic models.

Source code in src/pydapter/fields/types.py
class Field:
    """Field descriptor for Pydantic models."""

    __slots__ = (
        "name",
        "annotation",
        "default",
        "default_factory",
        "title",
        "description",
        "examples",
        "exclude",
        "frozen",
        "validator",
        "validator_kwargs",
        "alias",
        "extra_info",
        "immutable",
    )

    def __init__(
        self,
        name: str,
        annotation: type | UndefinedType = Undefined,
        default: Any = Undefined,
        default_factory: Callable | UndefinedType = Undefined,
        title: str | UndefinedType = Undefined,
        description: str | UndefinedType = Undefined,
        examples: list | UndefinedType = Undefined,
        exclude: bool | UndefinedType = Undefined,
        frozen: bool | UndefinedType = Undefined,
        validator: Callable | UndefinedType = Undefined,
        validator_kwargs: dict[Any, Any] | UndefinedType = Undefined,
        alias: str | UndefinedType = Undefined,
        immutable: bool = False,
        **extra_info: Any,
    ):
        """Initialize a field descriptor."""
        if default is not Undefined and default_factory is not Undefined:
            raise ValueError("Cannot have both default and default_factory")

        self.name = name
        self.annotation = annotation if annotation is not Undefined else Any
        self.default = default
        self.default_factory = default_factory
        self.title = title
        self.description = description
        self.examples = examples
        self.exclude = exclude
        self.frozen = frozen
        self.validator = validator
        self.validator_kwargs = validator_kwargs
        self.alias = alias
        self.extra_info = extra_info
        self.immutable = immutable

    def _to_dict(self) -> dict[str, Any]:
        return {
            "name": self.name,
            "annotation": self.annotation,
            "default": self.default,
            "default_factory": self.default_factory,
            "title": self.title,
            "description": self.description,
            "examples": self.examples,
            "exclude": self.exclude,
            "frozen": self.frozen,
            "validator": self.validator,
            "validator_kwargs": self.validator_kwargs,
            "alias": self.alias,
            **self.extra_info,
        }

    @property
    def field_info(self) -> FieldInfo:
        params = {
            "default": self.default,
            "default_factory": self.default_factory,
            "title": self.title,
            "description": self.description,
            "examples": self.examples,
            "exclude": self.exclude,
            "frozen": self.frozen,
            "alias": self.alias,
            **self.extra_info,
        }
        field_obj: FieldInfo = PydanticField(
            **{k: v for k, v in params.items() if v is not Undefined}
        )
        field_obj.annotation = (
            self.annotation if self.annotation is not Undefined else None
        )  # type: ignore[assignment]
        return field_obj

    @property
    def field_validator(self) -> dict[str, Callable] | None:
        if self.validator is Undefined:
            return None
        kwargs: dict[Any, Any] = (
            {} if self.validator_kwargs is Undefined else self.validator_kwargs  # type: ignore[assignment]
        )
        return {
            f"{self.name}_field_validator": field_validator(self.name, **kwargs)(
                self.validator
            )
        }

    def __hash__(self) -> int:
        return hash(self.name)

    def copy(self, **kwargs: Any) -> Field:
        """Create a copy of the field with updated values."""
        params = self._to_dict()
        params.update(kwargs)
        return Field(**params)

    def as_nullable(self, **kwargs) -> Field:
        """Create a copy of a field descriptor with a nullable annotation and None as default value.

        WARNING: the new_field will have no default value, nor default_factory.
        """
        annotation = str(self.annotation).lower().strip()
        if "none" in annotation or "optional" in annotation:
            return self.copy()

        # If the field has a validator, wrap it to handle None values
        new_validator = Undefined
        if self.validator is not Undefined:
            original_validator = self.validator

            def nullable_validator(cls, v):
                if v is None:
                    return v
                # Type guard: we know original_validator is not Undefined here
                if original_validator is not Undefined and callable(original_validator):
                    return original_validator(cls, v)
                return v

            new_validator = nullable_validator  # type: ignore[assignment]

        # Handle union type creation safely
        from typing import Union

        new_annotation = None
        if self.annotation is Undefined:
            new_annotation = type(None)
        else:
            try:
                new_annotation = self.annotation | None
            except TypeError:
                # Fallback for older Python versions or complex types
                new_annotation = Union[self.annotation, None]  # type: ignore[arg-type]

        return self.copy(
            annotation=new_annotation,
            default=None,
            default_factory=Undefined,
            validator=new_validator,
            **kwargs,
        )

    def as_listable(self, strict: bool = False, **kwargs) -> Field:
        """Create a copy of a field descriptor with a listable annotation.

        This method does not check whether the field is already listable.
        If strict is True, the annotation will be converted to a list of the current annotation.
        Otherwise, the list is an optional type.
        """
        # Handle annotation union safely
        from typing import Union

        annotation = None
        if strict:
            annotation = list[self.annotation]
        else:
            try:
                annotation = list[self.annotation] | self.annotation
            except TypeError:
                # Fallback for complex types
                annotation = Union[list[self.annotation], self.annotation]  # type: ignore[arg-type]

        # If the field has a validator, wrap it to handle lists
        new_validator = Undefined
        if self.validator is not Undefined:
            original_validator = self.validator

            def listable_validator(cls, v):
                if isinstance(v, list):
                    # Validate each item in the list
                    if original_validator is not Undefined and callable(
                        original_validator
                    ):
                        return [original_validator(cls, item) for item in v]
                    return v
                else:
                    # Single value - validate directly (only if not strict)
                    if strict:
                        raise ValueError("Expected a list")
                    if original_validator is not Undefined and callable(
                        original_validator
                    ):
                        return original_validator(cls, v)
                    return v

            new_validator = listable_validator  # type: ignore[assignment]

        return self.copy(annotation=annotation, validator=new_validator, **kwargs)

    def __setattr__(self, name: str, value: Any) -> None:
        if hasattr(self, "immutable") and self.immutable:
            raise AttributeError(f"Cannot modify immutable field {self.name}")
        object.__setattr__(self, name, value)
Functions
__init__(name, annotation=Undefined, default=Undefined, default_factory=Undefined, title=Undefined, description=Undefined, examples=Undefined, exclude=Undefined, frozen=Undefined, validator=Undefined, validator_kwargs=Undefined, alias=Undefined, immutable=False, **extra_info)

Initialize a field descriptor.

Source code in src/pydapter/fields/types.py
def __init__(
    self,
    name: str,
    annotation: type | UndefinedType = Undefined,
    default: Any = Undefined,
    default_factory: Callable | UndefinedType = Undefined,
    title: str | UndefinedType = Undefined,
    description: str | UndefinedType = Undefined,
    examples: list | UndefinedType = Undefined,
    exclude: bool | UndefinedType = Undefined,
    frozen: bool | UndefinedType = Undefined,
    validator: Callable | UndefinedType = Undefined,
    validator_kwargs: dict[Any, Any] | UndefinedType = Undefined,
    alias: str | UndefinedType = Undefined,
    immutable: bool = False,
    **extra_info: Any,
):
    """Initialize a field descriptor."""
    if default is not Undefined and default_factory is not Undefined:
        raise ValueError("Cannot have both default and default_factory")

    self.name = name
    self.annotation = annotation if annotation is not Undefined else Any
    self.default = default
    self.default_factory = default_factory
    self.title = title
    self.description = description
    self.examples = examples
    self.exclude = exclude
    self.frozen = frozen
    self.validator = validator
    self.validator_kwargs = validator_kwargs
    self.alias = alias
    self.extra_info = extra_info
    self.immutable = immutable
as_listable(strict=False, **kwargs)

Create a copy of a field descriptor with a listable annotation.

This method does not check whether the field is already listable. If strict is True, the annotation will be converted to a list of the current annotation. Otherwise, the list is an optional type.

Source code in src/pydapter/fields/types.py
def as_listable(self, strict: bool = False, **kwargs) -> Field:
    """Create a copy of a field descriptor with a listable annotation.

    This method does not check whether the field is already listable.
    If strict is True, the annotation will be converted to a list of the current annotation.
    Otherwise, the list is an optional type.
    """
    # Handle annotation union safely
    from typing import Union

    annotation = None
    if strict:
        annotation = list[self.annotation]
    else:
        try:
            annotation = list[self.annotation] | self.annotation
        except TypeError:
            # Fallback for complex types
            annotation = Union[list[self.annotation], self.annotation]  # type: ignore[arg-type]

    # If the field has a validator, wrap it to handle lists
    new_validator = Undefined
    if self.validator is not Undefined:
        original_validator = self.validator

        def listable_validator(cls, v):
            if isinstance(v, list):
                # Validate each item in the list
                if original_validator is not Undefined and callable(
                    original_validator
                ):
                    return [original_validator(cls, item) for item in v]
                return v
            else:
                # Single value - validate directly (only if not strict)
                if strict:
                    raise ValueError("Expected a list")
                if original_validator is not Undefined and callable(
                    original_validator
                ):
                    return original_validator(cls, v)
                return v

        new_validator = listable_validator  # type: ignore[assignment]

    return self.copy(annotation=annotation, validator=new_validator, **kwargs)
as_nullable(**kwargs)

Create a copy of a field descriptor with a nullable annotation and None as default value.

WARNING: the new_field will have no default value, nor default_factory.

Source code in src/pydapter/fields/types.py
def as_nullable(self, **kwargs) -> Field:
    """Create a copy of a field descriptor with a nullable annotation and None as default value.

    WARNING: the new_field will have no default value, nor default_factory.
    """
    annotation = str(self.annotation).lower().strip()
    if "none" in annotation or "optional" in annotation:
        return self.copy()

    # If the field has a validator, wrap it to handle None values
    new_validator = Undefined
    if self.validator is not Undefined:
        original_validator = self.validator

        def nullable_validator(cls, v):
            if v is None:
                return v
            # Type guard: we know original_validator is not Undefined here
            if original_validator is not Undefined and callable(original_validator):
                return original_validator(cls, v)
            return v

        new_validator = nullable_validator  # type: ignore[assignment]

    # Handle union type creation safely
    from typing import Union

    new_annotation = None
    if self.annotation is Undefined:
        new_annotation = type(None)
    else:
        try:
            new_annotation = self.annotation | None
        except TypeError:
            # Fallback for older Python versions or complex types
            new_annotation = Union[self.annotation, None]  # type: ignore[arg-type]

    return self.copy(
        annotation=new_annotation,
        default=None,
        default_factory=Undefined,
        validator=new_validator,
        **kwargs,
    )
copy(**kwargs)

Create a copy of the field with updated values.

Source code in src/pydapter/fields/types.py
def copy(self, **kwargs: Any) -> Field:
    """Create a copy of the field with updated values."""
    params = self._to_dict()
    params.update(kwargs)
    return Field(**params)

Functions

create_model(model_name, config=None, doc=None, base=None, fields=None, frozen=False)

Create a new pydantic model basing on fields and base class.

Parameters:

Name Type Description Default
model_name str

Name of the new model.

required
config dict[str, Any]

Configuration dictionary for the model.

None
doc str

Documentation string for the model.

None
base type[BaseModel]

Base class to inherit from.

None
fields list[Field] | dict[str, Field | FieldTemplate]

List of fields or dict of field names to Field/FieldTemplate instances.

None
frozen bool

Whether the model should be immutable (frozen).

False
Source code in src/pydapter/fields/types.py
def create_model(
    model_name: str,
    config: dict[str, Any] | None = None,
    doc: str | None = None,
    base: type[BaseModel] | None = None,
    fields: list[Field] | dict[str, Field | Any] | None = None,
    frozen: bool = False,
):
    """Create a new pydantic model basing on fields and base class.

    Args:
        model_name (str): Name of the new model.
        config (dict[str, Any], optional): Configuration dictionary for the model.
        doc (str, optional): Documentation string for the model.
        base (type[BaseModel], optional): Base class to inherit from.
        fields (list[Field] | dict[str, Field | FieldTemplate], optional): List of fields or dict of field names to Field/FieldTemplate instances.
        frozen (bool, optional): Whether the model should be immutable (frozen).
    """
    if config and base:
        raise ValidationError(
            message="Error creating new model: cannot provide both config and base class",
            details={"model_name": model_name},
        )

    _use_fields: list[Field] = [] if isinstance(fields, dict) else fields or []

    if isinstance(fields, dict):
        # Import here to avoid circular imports
        from pydapter.fields.template import FieldTemplate

        for name, field_or_template in fields.items():
            if isinstance(field_or_template, FieldTemplate):
                # Create Field from FieldTemplate
                field = field_or_template.create_field(name)
            elif isinstance(field_or_template, dict):
                # Handle dict-style field definition
                field_dict = field_or_template.copy()
                field_dict["name"] = name
                field = Field(**field_dict)
            else:
                # Assume it's a Field
                field = field_or_template
                if hasattr(field, "name") and name != field.name:
                    field = field.copy(name=name)
            _use_fields.append(field)

    use_fields: dict[str, tuple[type, FieldInfo]] = {
        field.name: (field.annotation, field.field_info) for field in _use_fields
    }

    # Collect validators for fields that have them
    validators: dict[str, Callable[..., Any]] = {}
    for field in _use_fields:
        if field.validator is not Undefined and callable(field.validator):
            kwargs = (
                {} if field.validator_kwargs is Undefined else field.validator_kwargs
            )
            validator_name = f"validate_{field.name}"
            validators[validator_name] = field_validator(field.name, **kwargs)(
                field.validator
            )

    # Create the base model with validators included - handle type issues by filtering valid fields
    valid_fields = {
        name: (annotation, field_info)
        for name, (annotation, field_info) in use_fields.items()
        if annotation is not Undefined
    }

    model: type[BaseModel] = create_pydantic_model(
        model_name,
        __config__=config,
        __doc__=doc,
        __base__=base,
        __validators__=validators,
        **valid_fields,
    )

    if frozen:
        from pydantic import ConfigDict

        config_dict = getattr(model, "model_config", {})
        if isinstance(config_dict, dict):
            config_dict["frozen"] = True
            model.model_config = config_dict
        else:
            # If it's already a ConfigDict, create a new one with frozen=True
            new_config = ConfigDict(**config_dict, frozen=True)
            model.model_config = new_config
    return model

pydapter.fields.template

Classes

FieldTemplate

Template for creating reusable field definitions with naming flexibility.

FieldTemplate provides a way to define field configurations that can be reused across multiple models with different field names. It supports Pydantic v2 Annotated types and provides compositional methods for creating nullable and listable variations.

Examples:

>>> # Create a reusable email field template
>>> email_template = FieldTemplate(
...     base_type=EmailStr,
...     description="Email address",
...     title="Email"
... )
>>>
>>> # Use the template in different models with different names
>>> user_email = email_template.create_field("user_email")
>>> contact_email = email_template.create_field("contact_email")
>>>
>>> # Create variations
>>> optional_email = email_template.as_nullable()
>>> email_list = email_template.as_listable()
Source code in src/pydapter/fields/template.py
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
class FieldTemplate:
    """Template for creating reusable field definitions with naming flexibility.

    FieldTemplate provides a way to define field configurations that can be reused
    across multiple models with different field names. It supports Pydantic v2
    Annotated types and provides compositional methods for creating nullable and
    listable variations.

    Examples:
        >>> # Create a reusable email field template
        >>> email_template = FieldTemplate(
        ...     base_type=EmailStr,
        ...     description="Email address",
        ...     title="Email"
        ... )
        >>>
        >>> # Use the template in different models with different names
        >>> user_email = email_template.create_field("user_email")
        >>> contact_email = email_template.create_field("contact_email")
        >>>
        >>> # Create variations
        >>> optional_email = email_template.as_nullable()
        >>> email_list = email_template.as_listable()
    """

    def __init__(
        self,
        base_type: type | Any,
        default: Any = Undefined,
        default_factory: Callable | UndefinedType = Undefined,
        title: str | UndefinedType = Undefined,
        description: str | UndefinedType = Undefined,
        examples: list | UndefinedType = Undefined,
        exclude: bool | UndefinedType = Undefined,
        frozen: bool | UndefinedType = Undefined,
        validator: Callable | UndefinedType = Undefined,
        validator_kwargs: dict[Any, Any] | UndefinedType = Undefined,
        alias: str | UndefinedType = Undefined,
        immutable: bool = False,
        **pydantic_field_kwargs: Any,
    ):
        """Initialize a field template.

        Args:
            base_type: The base type for the field (can be Annotated)
            default: Default value for the field
            default_factory: Factory function for default value
            title: Title for the field
            description: Description for the field
            examples: Examples for the field
            exclude: Whether to exclude the field from serialization
            frozen: Whether the field is frozen
            validator: Validator function for the field
            validator_kwargs: Keyword arguments for the validator
            alias: Alias for the field
            immutable: Whether the field is immutable
            **pydantic_field_kwargs: Additional Pydantic FieldInfo arguments
        """
        self.base_type = base_type
        self.default = default
        self.default_factory = default_factory
        self.title = title
        self.description = description
        self.examples = examples
        self.exclude = exclude
        self.frozen = frozen
        self.validator = validator
        self.validator_kwargs = validator_kwargs
        self.alias = alias
        self.immutable = immutable
        self.pydantic_field_kwargs = pydantic_field_kwargs

        # Extract Pydantic FieldInfo from Annotated type if present
        self._extract_pydantic_info()

        # Cache for base field info (without overrides)
        self._base_field_info_cache: FieldInfo | None = None

    def _extract_pydantic_info(self) -> None:
        """Extract Pydantic FieldInfo from Annotated base_type."""
        self._extracted_type = self.base_type
        self._extracted_field_info = None

        # For constrained types (constr, conint, etc.), we should NOT extract
        # We need to preserve the full Annotated type with constraints
        if get_origin(self.base_type) is Annotated:
            args = get_args(self.base_type)

            # Check if this has constraint annotations (StringConstraints, Interval, etc.)
            has_constraints = False
            for arg in args[1:]:
                arg_str = str(arg)
                if any(
                    constraint in arg_str
                    for constraint in ["StringConstraints", "Interval", "Constraints"]
                ):
                    has_constraints = True
                    break

            if has_constraints:
                # This is a constrained type from constr/conint/etc
                # Keep the full Annotated type
                return

            # Otherwise extract normally
            self._extracted_type = args[0]

            # Look for FieldInfo in the annotations
            for arg in args[1:]:
                if isinstance(arg, FieldInfo):
                    self._extracted_field_info = arg
                    break

    def _get_base_field_info(self) -> dict[str, Any]:
        """Get base field info parameters with caching.

        This method caches the expensive operation of extracting and merging
        base parameters from extracted FieldInfo and template settings.

        Returns:
            Dictionary of base field parameters
        """
        if hasattr(self, "_base_params_cache"):
            return self._base_params_cache.copy()

        base_params = {}
        if self._extracted_field_info:
            # Extract relevant attributes from existing FieldInfo
            for attr in [
                "default",
                "default_factory",
                "title",
                "description",
                "examples",
                "exclude",
                "frozen",
                "alias",
            ]:
                value = getattr(self._extracted_field_info, attr, Undefined)
                if value is not Undefined:
                    base_params[attr] = value

        # Override with template-level settings
        template_params = {
            "default": self.default,
            "default_factory": self.default_factory,
            "title": self.title,
            "description": self.description,
            "examples": self.examples,
            "exclude": self.exclude,
            "frozen": self.frozen,
            "alias": self.alias,
            **self.pydantic_field_kwargs,
        }

        # Filter out Undefined values
        template_params = {
            k: v for k, v in template_params.items() if v is not Undefined
        }
        base_params.update(template_params)

        # Cache the result
        self._base_params_cache = base_params.copy()
        return base_params

    def _merge_field_info(self, **overrides: Any) -> FieldInfo:
        """Merge FieldInfo from various sources.

        This internal method combines field information from:
        1. Extracted FieldInfo from Annotated types
        2. Template-level settings
        3. Runtime overrides

        The precedence order is: overrides > template settings > extracted FieldInfo

        Args:
            **overrides: Keyword arguments that override template settings

        Returns:
            A Pydantic FieldInfo instance with merged settings
        """
        # Get base parameters (with caching)
        base_params = self._get_base_field_info()

        # Apply overrides
        base_params.update(overrides)

        # Create new FieldInfo
        return PydanticField(**base_params)

    def create_field(self, name: str, **overrides: Any) -> Field:
        """Create a Field instance with the given name.

        This method creates a new Field instance using the template's configuration
        combined with any overrides provided. The resulting Field can be used in
        pydapter's create_model function.

        Args:
            name: The name for the field. Must be a valid Python identifier.
            **overrides: Keyword arguments to override template settings.
                Supported overrides include all Field constructor parameters
                like default, description, title, validator, etc.

        Returns:
            A Field instance configured with the template settings and overrides.

        Raises:
            ValueError: If the field name is not a valid Python identifier.
            TypeError: If there are conflicts between template settings and overrides.
            RuntimeError: If attempting to override a frozen field property.

        Examples:
            >>> template = FieldTemplate(base_type=str, description="A string field")
            >>> field = template.create_field("username", title="User Name")
            >>> # field will have description from template and title from override
        """
        # Validate field name
        if not name.isidentifier():
            raise ValueError(
                f"Field name '{name}' is not a valid Python identifier. "
                f"Field names must start with a letter or underscore and contain "
                f"only letters, numbers, and underscores."
            )

        # Check for frozen field overrides
        if self.frozen and "frozen" in overrides and overrides["frozen"] is False:
            raise RuntimeError(
                f"Cannot override frozen=True on field '{name}'. "
                f"Frozen fields cannot be made mutable at runtime."
            )

        # Validate that both default and default_factory are not provided
        effective_default = overrides.get("default", self.default)
        effective_default_factory = overrides.get(
            "default_factory", self.default_factory
        )

        if (
            effective_default is not Undefined
            and effective_default_factory is not Undefined
        ):
            raise ValueError(
                f"Field '{name}' cannot have both 'default' and 'default_factory'. "
                f"Please provide only one."
            )
        # Merge pydapter-specific kwargs
        pydapter_kwargs = {
            "validator": self.validator,
            "validator_kwargs": self.validator_kwargs,
            "immutable": self.immutable,
        }

        # Extract pydapter-specific overrides
        pydapter_overrides = {}
        pydantic_overrides = {}

        for key, value in overrides.items():
            if key in ["validator", "validator_kwargs", "immutable"]:
                pydapter_overrides[key] = value
            else:
                pydantic_overrides[key] = value

        # Update pydapter kwargs
        for key, value in pydapter_overrides.items():
            if value is not Undefined:
                pydapter_kwargs[key] = value

        # Get merged FieldInfo
        field_info = self._merge_field_info(**pydantic_overrides)

        # Create the final annotation
        # If we have a constrained type (where _extracted_type == base_type), use it as-is
        if (
            self._extracted_type == self.base_type
            and get_origin(self.base_type) is Annotated
        ):
            # This is a constrained type, use as-is
            final_annotation = self._extracted_type
        elif self._extracted_field_info or pydantic_overrides:
            final_annotation = Annotated[self._extracted_type, field_info]
        else:
            final_annotation = self._extracted_type

        # Create the Field - handle default vs default_factory properly
        field_kwargs = {
            "name": name,
            "annotation": final_annotation,
        }

        # Extract field info attributes carefully
        # Check for PydanticUndefined as well
        from pydantic_core import PydanticUndefined

        if hasattr(field_info, "default") and field_info.default not in (
            Undefined,
            PydanticUndefined,
        ):
            field_kwargs["default"] = field_info.default
        elif hasattr(
            field_info, "default_factory"
        ) and field_info.default_factory not in (None, Undefined):
            field_kwargs["default_factory"] = field_info.default_factory

        # Add other attributes
        for attr in ["title", "description", "examples", "exclude", "frozen", "alias"]:
            if hasattr(field_info, attr):
                value = getattr(field_info, attr)
                if value is not None:
                    field_kwargs[attr] = value

        # Add pydapter-specific kwargs
        field_kwargs.update(pydapter_kwargs)

        # Extract json_schema_extra and pass it to Field's extra_info
        if hasattr(field_info, "json_schema_extra") and field_info.json_schema_extra:
            field_kwargs.update(field_info.json_schema_extra)

        return Field(**field_kwargs)

    def copy(self, **kwargs: Any) -> Self:
        """Create a copy of the template with updated values."""
        params = {
            "base_type": self.base_type,
            "default": self.default,
            "default_factory": self.default_factory,
            "title": self.title,
            "description": self.description,
            "examples": self.examples,
            "exclude": self.exclude,
            "frozen": self.frozen,
            "validator": self.validator,
            "validator_kwargs": self.validator_kwargs,
            "alias": self.alias,
            "immutable": self.immutable,
            **self.pydantic_field_kwargs,
        }
        params.update(kwargs)
        return FieldTemplate(**params)

    def as_nullable(self) -> Self:
        """Create a nullable version of this template.

        Returns a new FieldTemplate where the base type is modified to accept None
        values. The default value is set to None and any existing default_factory
        is removed. If the template has a validator, it's wrapped to handle None values.

        Returns:
            A new FieldTemplate instance that accepts None values

        Examples:
            >>> int_template = FieldTemplate(base_type=int, default=0)
            >>> nullable_int = int_template.as_nullable()
            >>> # nullable_int will have type Union[int, None] with default=None
        """
        # Create nullable type
        nullable_type = Union[self._extracted_type, None]
        if get_origin(self.base_type) is Annotated:
            # Preserve annotations but update the base type
            args = get_args(self.base_type)
            # For Python 3.10 compatibility, we need to handle this differently
            if len(args) > 1:
                # Keep the original Annotated type but with nullable base
                nullable_type = self.base_type
            else:
                nullable_type = Annotated[Union[args[0], None]]

        # Wrap validator to handle None
        new_validator = Undefined
        if self.validator is not Undefined:
            original_validator = self.validator

            def nullable_validator(cls, v):
                if v is None:
                    return v
                if callable(original_validator):
                    return original_validator(cls, v)
                return v

            new_validator = nullable_validator

        return self.copy(
            base_type=nullable_type,
            default=None,
            default_factory=Undefined,
            validator=new_validator,
        )

    def as_listable(self, strict: bool = False) -> Self:
        """Create a listable version of this template.

        Returns a new FieldTemplate where the base type can accept either a single
        value or a list of values (flexible mode), or only lists (strict mode).

        Args:
            strict: If True, only lists are accepted. If False (default), both
                single values and lists are accepted.

        Returns:
            A new FieldTemplate instance that accepts lists

        Examples:
            >>> str_template = FieldTemplate(base_type=str)
            >>> # Flexible mode: accepts "value" or ["value1", "value2"]
            >>> flexible_list = str_template.as_listable(strict=False)
            >>> # Strict mode: only accepts ["value1", "value2"]
            >>> strict_list = str_template.as_listable(strict=True)
        """
        # Create list type
        if strict:
            list_type = list[self._extracted_type]
        else:
            list_type = Union[list[self._extracted_type], self._extracted_type]

        if get_origin(self.base_type) is Annotated:
            # Preserve annotations but update the base type
            args = get_args(self.base_type)
            # For Python 3.10 compatibility
            if strict:
                list_type = list[args[0]]
            else:
                list_type = Union[list[args[0]], args[0]]

        # Wrap validator to handle lists
        new_validator = Undefined
        if self.validator is not Undefined:
            original_validator = self.validator

            def listable_validator(cls, v):
                if isinstance(v, list):
                    if callable(original_validator):
                        return [original_validator(cls, item) for item in v]
                    return v
                else:
                    if strict:
                        raise ValueError("Expected a list")
                    if callable(original_validator):
                        return original_validator(cls, v)
                    return v

            new_validator = listable_validator

        return self.copy(base_type=list_type, validator=new_validator)
Functions
__init__(base_type, default=Undefined, default_factory=Undefined, title=Undefined, description=Undefined, examples=Undefined, exclude=Undefined, frozen=Undefined, validator=Undefined, validator_kwargs=Undefined, alias=Undefined, immutable=False, **pydantic_field_kwargs)

Initialize a field template.

Parameters:

Name Type Description Default
base_type type | Any

The base type for the field (can be Annotated)

required
default Any

Default value for the field

Undefined
default_factory Callable | UndefinedType

Factory function for default value

Undefined
title str | UndefinedType

Title for the field

Undefined
description str | UndefinedType

Description for the field

Undefined
examples list | UndefinedType

Examples for the field

Undefined
exclude bool | UndefinedType

Whether to exclude the field from serialization

Undefined
frozen bool | UndefinedType

Whether the field is frozen

Undefined
validator Callable | UndefinedType

Validator function for the field

Undefined
validator_kwargs dict[Any, Any] | UndefinedType

Keyword arguments for the validator

Undefined
alias str | UndefinedType

Alias for the field

Undefined
immutable bool

Whether the field is immutable

False
**pydantic_field_kwargs Any

Additional Pydantic FieldInfo arguments

{}
Source code in src/pydapter/fields/template.py
def __init__(
    self,
    base_type: type | Any,
    default: Any = Undefined,
    default_factory: Callable | UndefinedType = Undefined,
    title: str | UndefinedType = Undefined,
    description: str | UndefinedType = Undefined,
    examples: list | UndefinedType = Undefined,
    exclude: bool | UndefinedType = Undefined,
    frozen: bool | UndefinedType = Undefined,
    validator: Callable | UndefinedType = Undefined,
    validator_kwargs: dict[Any, Any] | UndefinedType = Undefined,
    alias: str | UndefinedType = Undefined,
    immutable: bool = False,
    **pydantic_field_kwargs: Any,
):
    """Initialize a field template.

    Args:
        base_type: The base type for the field (can be Annotated)
        default: Default value for the field
        default_factory: Factory function for default value
        title: Title for the field
        description: Description for the field
        examples: Examples for the field
        exclude: Whether to exclude the field from serialization
        frozen: Whether the field is frozen
        validator: Validator function for the field
        validator_kwargs: Keyword arguments for the validator
        alias: Alias for the field
        immutable: Whether the field is immutable
        **pydantic_field_kwargs: Additional Pydantic FieldInfo arguments
    """
    self.base_type = base_type
    self.default = default
    self.default_factory = default_factory
    self.title = title
    self.description = description
    self.examples = examples
    self.exclude = exclude
    self.frozen = frozen
    self.validator = validator
    self.validator_kwargs = validator_kwargs
    self.alias = alias
    self.immutable = immutable
    self.pydantic_field_kwargs = pydantic_field_kwargs

    # Extract Pydantic FieldInfo from Annotated type if present
    self._extract_pydantic_info()

    # Cache for base field info (without overrides)
    self._base_field_info_cache: FieldInfo | None = None
as_listable(strict=False)

Create a listable version of this template.

Returns a new FieldTemplate where the base type can accept either a single value or a list of values (flexible mode), or only lists (strict mode).

Parameters:

Name Type Description Default
strict bool

If True, only lists are accepted. If False (default), both single values and lists are accepted.

False

Returns:

Type Description
Self

A new FieldTemplate instance that accepts lists

Examples:

>>> str_template = FieldTemplate(base_type=str)
>>> # Flexible mode: accepts "value" or ["value1", "value2"]
>>> flexible_list = str_template.as_listable(strict=False)
>>> # Strict mode: only accepts ["value1", "value2"]
>>> strict_list = str_template.as_listable(strict=True)
Source code in src/pydapter/fields/template.py
def as_listable(self, strict: bool = False) -> Self:
    """Create a listable version of this template.

    Returns a new FieldTemplate where the base type can accept either a single
    value or a list of values (flexible mode), or only lists (strict mode).

    Args:
        strict: If True, only lists are accepted. If False (default), both
            single values and lists are accepted.

    Returns:
        A new FieldTemplate instance that accepts lists

    Examples:
        >>> str_template = FieldTemplate(base_type=str)
        >>> # Flexible mode: accepts "value" or ["value1", "value2"]
        >>> flexible_list = str_template.as_listable(strict=False)
        >>> # Strict mode: only accepts ["value1", "value2"]
        >>> strict_list = str_template.as_listable(strict=True)
    """
    # Create list type
    if strict:
        list_type = list[self._extracted_type]
    else:
        list_type = Union[list[self._extracted_type], self._extracted_type]

    if get_origin(self.base_type) is Annotated:
        # Preserve annotations but update the base type
        args = get_args(self.base_type)
        # For Python 3.10 compatibility
        if strict:
            list_type = list[args[0]]
        else:
            list_type = Union[list[args[0]], args[0]]

    # Wrap validator to handle lists
    new_validator = Undefined
    if self.validator is not Undefined:
        original_validator = self.validator

        def listable_validator(cls, v):
            if isinstance(v, list):
                if callable(original_validator):
                    return [original_validator(cls, item) for item in v]
                return v
            else:
                if strict:
                    raise ValueError("Expected a list")
                if callable(original_validator):
                    return original_validator(cls, v)
                return v

        new_validator = listable_validator

    return self.copy(base_type=list_type, validator=new_validator)
as_nullable()

Create a nullable version of this template.

Returns a new FieldTemplate where the base type is modified to accept None values. The default value is set to None and any existing default_factory is removed. If the template has a validator, it's wrapped to handle None values.

Returns:

Type Description
Self

A new FieldTemplate instance that accepts None values

Examples:

>>> int_template = FieldTemplate(base_type=int, default=0)
>>> nullable_int = int_template.as_nullable()
>>> # nullable_int will have type Union[int, None] with default=None
Source code in src/pydapter/fields/template.py
def as_nullable(self) -> Self:
    """Create a nullable version of this template.

    Returns a new FieldTemplate where the base type is modified to accept None
    values. The default value is set to None and any existing default_factory
    is removed. If the template has a validator, it's wrapped to handle None values.

    Returns:
        A new FieldTemplate instance that accepts None values

    Examples:
        >>> int_template = FieldTemplate(base_type=int, default=0)
        >>> nullable_int = int_template.as_nullable()
        >>> # nullable_int will have type Union[int, None] with default=None
    """
    # Create nullable type
    nullable_type = Union[self._extracted_type, None]
    if get_origin(self.base_type) is Annotated:
        # Preserve annotations but update the base type
        args = get_args(self.base_type)
        # For Python 3.10 compatibility, we need to handle this differently
        if len(args) > 1:
            # Keep the original Annotated type but with nullable base
            nullable_type = self.base_type
        else:
            nullable_type = Annotated[Union[args[0], None]]

    # Wrap validator to handle None
    new_validator = Undefined
    if self.validator is not Undefined:
        original_validator = self.validator

        def nullable_validator(cls, v):
            if v is None:
                return v
            if callable(original_validator):
                return original_validator(cls, v)
            return v

        new_validator = nullable_validator

    return self.copy(
        base_type=nullable_type,
        default=None,
        default_factory=Undefined,
        validator=new_validator,
    )
copy(**kwargs)

Create a copy of the template with updated values.

Source code in src/pydapter/fields/template.py
def copy(self, **kwargs: Any) -> Self:
    """Create a copy of the template with updated values."""
    params = {
        "base_type": self.base_type,
        "default": self.default,
        "default_factory": self.default_factory,
        "title": self.title,
        "description": self.description,
        "examples": self.examples,
        "exclude": self.exclude,
        "frozen": self.frozen,
        "validator": self.validator,
        "validator_kwargs": self.validator_kwargs,
        "alias": self.alias,
        "immutable": self.immutable,
        **self.pydantic_field_kwargs,
    }
    params.update(kwargs)
    return FieldTemplate(**params)
create_field(name, **overrides)

Create a Field instance with the given name.

This method creates a new Field instance using the template's configuration combined with any overrides provided. The resulting Field can be used in pydapter's create_model function.

Parameters:

Name Type Description Default
name str

The name for the field. Must be a valid Python identifier.

required
**overrides Any

Keyword arguments to override template settings. Supported overrides include all Field constructor parameters like default, description, title, validator, etc.

{}

Returns:

Type Description
Field

A Field instance configured with the template settings and overrides.

Raises:

Type Description
ValueError

If the field name is not a valid Python identifier.

TypeError

If there are conflicts between template settings and overrides.

RuntimeError

If attempting to override a frozen field property.

Examples:

>>> template = FieldTemplate(base_type=str, description="A string field")
>>> field = template.create_field("username", title="User Name")
>>> # field will have description from template and title from override
Source code in src/pydapter/fields/template.py
def create_field(self, name: str, **overrides: Any) -> Field:
    """Create a Field instance with the given name.

    This method creates a new Field instance using the template's configuration
    combined with any overrides provided. The resulting Field can be used in
    pydapter's create_model function.

    Args:
        name: The name for the field. Must be a valid Python identifier.
        **overrides: Keyword arguments to override template settings.
            Supported overrides include all Field constructor parameters
            like default, description, title, validator, etc.

    Returns:
        A Field instance configured with the template settings and overrides.

    Raises:
        ValueError: If the field name is not a valid Python identifier.
        TypeError: If there are conflicts between template settings and overrides.
        RuntimeError: If attempting to override a frozen field property.

    Examples:
        >>> template = FieldTemplate(base_type=str, description="A string field")
        >>> field = template.create_field("username", title="User Name")
        >>> # field will have description from template and title from override
    """
    # Validate field name
    if not name.isidentifier():
        raise ValueError(
            f"Field name '{name}' is not a valid Python identifier. "
            f"Field names must start with a letter or underscore and contain "
            f"only letters, numbers, and underscores."
        )

    # Check for frozen field overrides
    if self.frozen and "frozen" in overrides and overrides["frozen"] is False:
        raise RuntimeError(
            f"Cannot override frozen=True on field '{name}'. "
            f"Frozen fields cannot be made mutable at runtime."
        )

    # Validate that both default and default_factory are not provided
    effective_default = overrides.get("default", self.default)
    effective_default_factory = overrides.get(
        "default_factory", self.default_factory
    )

    if (
        effective_default is not Undefined
        and effective_default_factory is not Undefined
    ):
        raise ValueError(
            f"Field '{name}' cannot have both 'default' and 'default_factory'. "
            f"Please provide only one."
        )
    # Merge pydapter-specific kwargs
    pydapter_kwargs = {
        "validator": self.validator,
        "validator_kwargs": self.validator_kwargs,
        "immutable": self.immutable,
    }

    # Extract pydapter-specific overrides
    pydapter_overrides = {}
    pydantic_overrides = {}

    for key, value in overrides.items():
        if key in ["validator", "validator_kwargs", "immutable"]:
            pydapter_overrides[key] = value
        else:
            pydantic_overrides[key] = value

    # Update pydapter kwargs
    for key, value in pydapter_overrides.items():
        if value is not Undefined:
            pydapter_kwargs[key] = value

    # Get merged FieldInfo
    field_info = self._merge_field_info(**pydantic_overrides)

    # Create the final annotation
    # If we have a constrained type (where _extracted_type == base_type), use it as-is
    if (
        self._extracted_type == self.base_type
        and get_origin(self.base_type) is Annotated
    ):
        # This is a constrained type, use as-is
        final_annotation = self._extracted_type
    elif self._extracted_field_info or pydantic_overrides:
        final_annotation = Annotated[self._extracted_type, field_info]
    else:
        final_annotation = self._extracted_type

    # Create the Field - handle default vs default_factory properly
    field_kwargs = {
        "name": name,
        "annotation": final_annotation,
    }

    # Extract field info attributes carefully
    # Check for PydanticUndefined as well
    from pydantic_core import PydanticUndefined

    if hasattr(field_info, "default") and field_info.default not in (
        Undefined,
        PydanticUndefined,
    ):
        field_kwargs["default"] = field_info.default
    elif hasattr(
        field_info, "default_factory"
    ) and field_info.default_factory not in (None, Undefined):
        field_kwargs["default_factory"] = field_info.default_factory

    # Add other attributes
    for attr in ["title", "description", "examples", "exclude", "frozen", "alias"]:
        if hasattr(field_info, attr):
            value = getattr(field_info, attr)
            if value is not None:
                field_kwargs[attr] = value

    # Add pydapter-specific kwargs
    field_kwargs.update(pydapter_kwargs)

    # Extract json_schema_extra and pass it to Field's extra_info
    if hasattr(field_info, "json_schema_extra") and field_info.json_schema_extra:
        field_kwargs.update(field_info.json_schema_extra)

    return Field(**field_kwargs)

Specialized Fields

pydapter.fields.ids

Classes

pydapter.fields.dts

Classes

pydapter.fields.embedding

Classes

pydapter.fields.execution

Classes

Execution

Bases: BaseModel

Represents the execution state of an event.

Source code in src/pydapter/fields/execution.py
class Execution(BaseModel):
    """Represents the execution state of an event."""

    model_config = ConfigDict(
        use_enum_values=True,
        arbitrary_types_allowed=True,
    )
    duration: float | None = None
    response: dict | None = None
    status: ExecutionStatus = ExecutionStatus.PENDING
    error: str | None = None
    response_obj: Any = PydanticField(None, exclude=True)
    updated_at: datetime | None = PydanticField(
        default_factory=lambda: datetime.now(tz=timezone.utc),
        exclude=True,
    )

    @field_validator("response", mode="before")
    def _validate_response(cls, v: BaseModel | dict | None):
        return validate_model_to_params(v)

    def validate_response(self):
        if self.response is None and self.response_obj is None:
            raise ValidationError("Response and response_obj are both None")
        if not isinstance(self.response, dict):
            self.response = validate_model_to_params(self.response_obj)

ExecutionStatus

Bases: str, Enum

Status states for tracking action execution progress.

Source code in src/pydapter/fields/execution.py
class ExecutionStatus(str, Enum):
    """Status states for tracking action execution progress."""

    PENDING = "pending"
    PROCESSING = "processing"
    COMPLETED = "completed"
    FAILED = "failed"

pydapter.fields.params

Classes

Field Collections

pydapter.fields.common_templates

Classes

pydapter.fields.families

Field Families - Predefined collections of field templates for core database patterns.

This module provides pre-configured field families that group commonly used fields together for database models. These families focus on core abstractions like entity tracking, soft deletion, and audit trails.

Classes

FieldFamilies

Collection of predefined field template groups for core database patterns.

This class provides field families that represent common database patterns like entity tracking, soft deletion, and audit trails. These are foundational patterns that align with pydapter's core abstractions.

Source code in src/pydapter/fields/families.py
class FieldFamilies:
    """Collection of predefined field template groups for core database patterns.

    This class provides field families that represent common database patterns
    like entity tracking, soft deletion, and audit trails. These are foundational
    patterns that align with pydapter's core abstractions.
    """

    # Basic entity fields (id, created_at, updated_at)
    # Maps to Identifiable + Temporal protocols
    ENTITY: dict[str, FieldTemplate] = {
        "id": ID_TEMPLATE,
        "created_at": CREATED_AT_TEMPLATE,
        "updated_at": UPDATED_AT_TEMPLATE,
    }

    # Entity fields with timezone-aware timestamps
    ENTITY_TZ: dict[str, FieldTemplate] = {
        "id": ID_TEMPLATE,
        "created_at": CREATED_AT_TZ_TEMPLATE,
        "updated_at": UPDATED_AT_TZ_TEMPLATE,
    }

    # Soft delete support - common database pattern
    SOFT_DELETE: dict[str, FieldTemplate] = {
        "deleted_at": DELETED_AT_TEMPLATE,
        "is_deleted": None,  # Will be defined below
    }

    # Soft delete with timezone-aware timestamp
    SOFT_DELETE_TZ: dict[str, FieldTemplate] = {
        "deleted_at": DELETED_AT_TZ_TEMPLATE,
        "is_deleted": None,  # Will be defined below
    }

    # Audit/tracking fields - common pattern for tracking changes
    AUDIT: dict[str, FieldTemplate] = {
        "created_by": None,  # Will be defined below
        "updated_by": None,  # Will be defined below
        "version": None,  # Will be defined below
    }

Functions

create_field_dict(*families, **overrides)

Create a field dictionary by merging multiple field families.

This function takes multiple field families and merges them into a single dictionary of Pydantic fields. Later families override fields from earlier ones if there are naming conflicts.

Parameters:

Name Type Description Default
*families dict[str, FieldTemplate]

Variable number of field family dictionaries to merge

()
**overrides FieldTemplate

Individual field templates to add or override

{}

Returns:

Type Description
dict[str, Field]

Dict[str, Field]: A dictionary mapping field names to Pydantic Field instances

Example
# Combine entity and audit fields
fields = create_field_dict(
    FieldFamilies.ENTITY,
    FieldFamilies.AUDIT,
    name=FieldTemplate(base_type=str, description="Entity name")
)

# Create a model with the combined fields
AuditedEntity = create_model("AuditedEntity", fields=fields)
Source code in src/pydapter/fields/families.py
def create_field_dict(
    *families: dict[str, FieldTemplate], **overrides: FieldTemplate
) -> dict[str, Field]:
    """Create a field dictionary by merging multiple field families.

    This function takes multiple field families and merges them into a single
    dictionary of Pydantic fields. Later families override fields from earlier
    ones if there are naming conflicts.

    Args:
        *families: Variable number of field family dictionaries to merge
        **overrides: Individual field templates to add or override

    Returns:
        Dict[str, Field]: A dictionary mapping field names to Pydantic Field instances

    Example:
        ```python
        # Combine entity and audit fields
        fields = create_field_dict(
            FieldFamilies.ENTITY,
            FieldFamilies.AUDIT,
            name=FieldTemplate(base_type=str, description="Entity name")
        )

        # Create a model with the combined fields
        AuditedEntity = create_model("AuditedEntity", fields=fields)
        ```
    """

    result: dict[str, Field] = {}

    # Process field families in order
    for family in families:
        for field_name, template in family.items():
            if template is not None:
                result[field_name] = template.create_field(field_name)

    # Process individual overrides
    for field_name, template in overrides.items():
        if template is not None:
            result[field_name] = template.create_field(field_name)

    return result

pydapter.fields.protocol_families

Protocol Field Families - Field templates for protocol integration.

This module provides field families that correspond to pydapter protocols, enabling easy creation of models that implement specific protocol interfaces.

Classes

ProtocolFieldFamilies

Field families for pydapter protocol compliance.

This class provides field template collections that match the requirements of various pydapter protocols. Using these families ensures your models will be compatible with protocol mixins and functionality.

Source code in src/pydapter/fields/protocol_families.py
class ProtocolFieldFamilies:
    """Field families for pydapter protocol compliance.

    This class provides field template collections that match the requirements
    of various pydapter protocols. Using these families ensures your models
    will be compatible with protocol mixins and functionality.
    """

    # Identifiable protocol fields
    IDENTIFIABLE: dict[str, FieldTemplate] = {
        "id": ID_TEMPLATE,
    }

    # Temporal protocol fields (naive datetime)
    TEMPORAL: dict[str, FieldTemplate] = {
        "created_at": CREATED_AT_TEMPLATE,
        "updated_at": UPDATED_AT_TEMPLATE,
    }

    # Temporal protocol fields (timezone-aware)
    TEMPORAL_TZ: dict[str, FieldTemplate] = {
        "created_at": CREATED_AT_TZ_TEMPLATE,
        "updated_at": UPDATED_AT_TZ_TEMPLATE,
    }

    # Embeddable protocol fields
    EMBEDDABLE: dict[str, FieldTemplate] = {
        "embedding": FieldTemplate(
            base_type=list[float],
            description="Vector embedding",
            default_factory=list,
            json_schema_extra={"vector_dim": 1536},  # Default OpenAI dimension
        ),
    }

    # Invokable protocol fields
    INVOKABLE: dict[str, FieldTemplate] = {
        "execution": None,  # Will be defined below
    }

    # Cryptographical protocol fields
    CRYPTOGRAPHICAL: dict[str, FieldTemplate] = {
        "sha256": FieldTemplate(
            base_type=str,
            description="SHA256 hash of the content",
        ).as_nullable(),
    }

    AUDITABLE: dict[str, FieldTemplate] = {
        "created_by": ID_TEMPLATE.as_nullable(),
        "updated_by": ID_TEMPLATE.as_nullable(),
        "version": FieldTemplate(
            base_type=int,
            description="Version number for optimistic locking",
            default=1,
        ),
    }

    SOFT_DELETABLE: dict[str, FieldTemplate] = {
        "deleted_at": DELETED_AT_TZ_TEMPLATE,
        "is_deleted": FieldTemplate(
            base_type=bool,
            description="Soft delete flag",
            default=False,
        ),
    }

    # Event protocol base fields (combines multiple protocols)
    EVENT_BASE: dict[str, FieldTemplate] = {
        "id": ID_TEMPLATE.copy(frozen=True),  # Events have frozen IDs
        "created_at": CREATED_AT_TZ_TEMPLATE,
        "updated_at": UPDATED_AT_TZ_TEMPLATE,
        "event_type": FieldTemplate(
            base_type=str,
            description="Type of the event",
        ).as_nullable(),
        "content": FieldTemplate(
            base_type=str | dict | None,
            description="Content of the event",
            default=None,
        ),
        "request": JSON_TEMPLATE.copy(description="Request parameters"),
    }

    # Complete Event protocol fields (all protocols combined)
    EVENT_COMPLETE: dict[str, FieldTemplate] = {
        **EVENT_BASE,
        **EMBEDDABLE,
        **CRYPTOGRAPHICAL,
        "execution": None,  # Will be defined below
    }

Functions

create_protocol_model(name, *protocols, timezone_aware=True, base_fields=None, **extra_fields)

Create a model with fields required by specified protocols (structural compliance).

This function creates a Pydantic model with fields required by the specified protocols. It provides STRUCTURAL compliance by including the necessary fields, but does NOT add behavioral methods from protocol mixins.

Note: This function only adds the fields required by protocols. If you need the behavioral methods (e.g., update_timestamp() from TemporalMixin), you must explicitly inherit from the corresponding mixin classes when defining your final model class.

Parameters:

Name Type Description Default
name str

Name for the generated model class

required
*protocols str | ProtocolType

Protocol names to implement. Supported values: - "identifiable" or IDENTIFIABLE: Adds id field - "temporal" or TEMPORAL: Adds created_at and updated_at fields - "embeddable" or EMBEDDABLE: Adds embedding field - "invokable" or INVOKABLE: Adds execution field - "cryptographical" or CRYPTOGRAPHICAL: Adds sha256 field

()
timezone_aware bool

If True, uses timezone-aware datetime fields (default: True)

True
base_fields dict[str, FieldTemplate] | None

Optional base field family to start with

None
**extra_fields FieldTemplate

Additional field templates to include

{}

Returns:

Type Description
type

A new Pydantic model class with protocol-compliant fields (structure only)

Examples:

from pydapter.protocols import IDENTIFIABLE, TEMPORAL, EMBEDDABLE

# Create a model with ID and timestamps using constants
TrackedEntity = create_protocol_model(
    "TrackedEntity",
    IDENTIFIABLE,
    TEMPORAL,
)

# Create an embeddable document
Document = create_protocol_model(
    "Document",
    IDENTIFIABLE,
    TEMPORAL,
    EMBEDDABLE,
    title=NAME_TEMPLATE,
    content=FieldTemplate(base_type=str),
)

# For event-like models, use the Event class directly:
from pydapter.protocols import Event

class CustomEvent(Event):
    user_id: str
    action: str

# To add behavioral methods, inherit from the mixins:
from pydapter.protocols import IdentifiableMixin, TemporalMixin

# First create the structure
_UserStructure = create_protocol_model(
    "UserStructure",
    "identifiable",
    "temporal",
    username=FieldTemplate(base_type=str)
)

# Then add behavioral mixins
class User(_UserStructure, IdentifiableMixin, TemporalMixin):
    pass

# Now the model has both fields AND methods
user = User(username="test")
user.update_timestamp()  # Method from TemporalMixin
Source code in src/pydapter/fields/protocol_families.py
def create_protocol_model(
    name: str,
    *protocols: str | ProtocolType,
    timezone_aware: bool = True,
    base_fields: dict[str, FieldTemplate] | None = None,
    **extra_fields: FieldTemplate,
) -> type:
    """Create a model with fields required by specified protocols (structural compliance).

    This function creates a Pydantic model with fields required by the specified
    protocols. It provides STRUCTURAL compliance by including the necessary fields,
    but does NOT add behavioral methods from protocol mixins.

    Note: This function only adds the fields required by protocols. If you need
    the behavioral methods (e.g., update_timestamp() from TemporalMixin), you must
    explicitly inherit from the corresponding mixin classes when defining your
    final model class.

    Args:
        name: Name for the generated model class
        *protocols: Protocol names to implement. Supported values:
            - "identifiable" or IDENTIFIABLE: Adds id field
            - "temporal" or TEMPORAL: Adds created_at and updated_at fields
            - "embeddable" or EMBEDDABLE: Adds embedding field
            - "invokable" or INVOKABLE: Adds execution field
            - "cryptographical" or CRYPTOGRAPHICAL: Adds sha256 field
        timezone_aware: If True, uses timezone-aware datetime fields (default: True)
        base_fields: Optional base field family to start with
        **extra_fields: Additional field templates to include

    Returns:
        A new Pydantic model class with protocol-compliant fields (structure only)

    Examples:
        ```python
        from pydapter.protocols import IDENTIFIABLE, TEMPORAL, EMBEDDABLE

        # Create a model with ID and timestamps using constants
        TrackedEntity = create_protocol_model(
            "TrackedEntity",
            IDENTIFIABLE,
            TEMPORAL,
        )

        # Create an embeddable document
        Document = create_protocol_model(
            "Document",
            IDENTIFIABLE,
            TEMPORAL,
            EMBEDDABLE,
            title=NAME_TEMPLATE,
            content=FieldTemplate(base_type=str),
        )

        # For event-like models, use the Event class directly:
        from pydapter.protocols import Event

        class CustomEvent(Event):
            user_id: str
            action: str

        # To add behavioral methods, inherit from the mixins:
        from pydapter.protocols import IdentifiableMixin, TemporalMixin

        # First create the structure
        _UserStructure = create_protocol_model(
            "UserStructure",
            "identifiable",
            "temporal",
            username=FieldTemplate(base_type=str)
        )

        # Then add behavioral mixins
        class User(_UserStructure, IdentifiableMixin, TemporalMixin):
            pass

        # Now the model has both fields AND methods
        user = User(username="test")
        user.update_timestamp()  # Method from TemporalMixin
        ```
    """
    from pydapter.fields.families import create_field_dict
    from pydapter.fields.types import create_model

    # Start with base fields if provided
    field_families = []
    if base_fields:
        field_families.append(base_fields)

    # Add protocol fields
    for protocol in protocols:
        protocol_lower = protocol.lower()

        if protocol_lower == "identifiable":
            field_families.append(ProtocolFieldFamilies.IDENTIFIABLE)
        elif protocol_lower == "temporal":
            if timezone_aware:
                field_families.append(ProtocolFieldFamilies.TEMPORAL_TZ)
            else:
                field_families.append(ProtocolFieldFamilies.TEMPORAL)
        elif protocol_lower == "embeddable":
            field_families.append(ProtocolFieldFamilies.EMBEDDABLE)
        elif protocol_lower == "invokable":
            field_families.append(ProtocolFieldFamilies.INVOKABLE)
        elif protocol_lower == "cryptographical":
            field_families.append(ProtocolFieldFamilies.CRYPTOGRAPHICAL)
        else:
            raise ValueError(
                f"Unknown protocol: {protocol}. Supported protocols are: "
                f"identifiable, temporal, embeddable, invokable, cryptographical"
            )

    # Create field dictionary
    fields = create_field_dict(*field_families, **extra_fields)

    # Create and return the model
    return create_model(name, fields=fields)

Builders and Utilities

pydapter.fields.builder

Domain Model Builder - Fluent API for creating models with field families.

This module provides a builder pattern implementation for creating database models using core field families and templates. It offers a fluent API that makes model creation more intuitive and less error-prone.

Classes

DomainModelBuilder

Fluent builder for creating database models with field families.

This class provides a fluent API for building Pydantic models by composing core field families and individual field templates. It supports method chaining for a clean, readable syntax.

Examples:

# Create an entity with soft delete and audit fields
TrackedEntity = (
    DomainModelBuilder("TrackedEntity")
    .with_entity_fields(timezone_aware=True)
    .with_soft_delete(timezone_aware=True)
    .with_audit_fields()
    .add_field("name", FieldTemplate(base_type=str, description="Entity name"))
    .build()
)

# Create a simple versioned model
VersionedModel = (
    DomainModelBuilder("VersionedModel")
    .with_entity_fields()
    .with_audit_fields()
    .add_field("data", FieldTemplate(base_type=dict, default_factory=dict))
    .build()
)
Source code in src/pydapter/fields/builder.py
class DomainModelBuilder:
    """Fluent builder for creating database models with field families.

    This class provides a fluent API for building Pydantic models by composing
    core field families and individual field templates. It supports method chaining
    for a clean, readable syntax.

    Examples:
        ```python
        # Create an entity with soft delete and audit fields
        TrackedEntity = (
            DomainModelBuilder("TrackedEntity")
            .with_entity_fields(timezone_aware=True)
            .with_soft_delete(timezone_aware=True)
            .with_audit_fields()
            .add_field("name", FieldTemplate(base_type=str, description="Entity name"))
            .build()
        )

        # Create a simple versioned model
        VersionedModel = (
            DomainModelBuilder("VersionedModel")
            .with_entity_fields()
            .with_audit_fields()
            .add_field("data", FieldTemplate(base_type=dict, default_factory=dict))
            .build()
        )
        ```
    """

    def __init__(self, model_name: str, **model_config: Any):
        """Initialize the builder with a model name.

        Args:
            model_name: Name for the generated model class
            **model_config: Additional Pydantic model configuration options
                (e.g., orm_mode=True, validate_assignment=True)
        """
        self.model_name = model_name
        self.model_config = model_config
        self._fields: dict[str, FieldTemplate] = {}

    def with_entity_fields(self, timezone_aware: bool = False) -> DomainModelBuilder:
        """Add basic entity fields (id, created_at, updated_at).

        Args:
            timezone_aware: If True, uses timezone-aware datetime fields

        Returns:
            Self for method chaining
        """
        family = FieldFamilies.ENTITY_TZ if timezone_aware else FieldFamilies.ENTITY
        self._merge_family(family)
        return self

    def with_soft_delete(self, timezone_aware: bool = False) -> DomainModelBuilder:
        """Add soft delete fields (deleted_at, is_deleted).

        Args:
            timezone_aware: If True, uses timezone-aware datetime for deleted_at

        Returns:
            Self for method chaining
        """
        family = (
            FieldFamilies.SOFT_DELETE_TZ
            if timezone_aware
            else FieldFamilies.SOFT_DELETE
        )
        self._merge_family(family)

        return self

    def with_audit_fields(self) -> DomainModelBuilder:
        """Add audit/tracking fields (created_by, updated_by, version).

        Returns:
            Self for method chaining
        """
        self._merge_family(FieldFamilies.AUDIT)
        return self

    def with_family(self, family: dict[str, FieldTemplate]) -> DomainModelBuilder:
        """Add a custom field family.

        Args:
            family: Dictionary mapping field names to FieldTemplate instances

        Returns:
            Self for method chaining
        """
        self._merge_family(family)
        return self

    def add_field(
        self, name: str, template: FieldTemplate, replace: bool = True
    ) -> DomainModelBuilder:
        """Add or update a single field.

        Args:
            name: Field name
            template: FieldTemplate instance for the field
            replace: If True (default), replaces existing field with same name.
                    If False, raises ValueError if field already exists.

        Returns:
            Self for method chaining

        Raises:
            ValueError: If field exists and replace=False
        """
        if not replace and name in self._fields:
            raise ValueError(
                f"Field '{name}' already exists. Set replace=True to override."
            )

        self._fields[name] = template
        return self

    def remove_field(self, name: str) -> DomainModelBuilder:
        """Remove a field from the builder.

        Args:
            name: Field name to remove

        Returns:
            Self for method chaining

        Raises:
            KeyError: If field doesn't exist
        """
        if name not in self._fields:
            raise KeyError(f"Field '{name}' not found in builder")

        del self._fields[name]
        return self

    def remove_fields(self, *names: str) -> DomainModelBuilder:
        """Remove multiple fields from the builder.

        Args:
            *names: Field names to remove

        Returns:
            Self for method chaining
        """
        for name in names:
            if name in self._fields:
                del self._fields[name]
        return self

    def _merge_family(self, family: dict[str, FieldTemplate]) -> None:
        """Merge a field family into the current fields.

        Args:
            family: Field family to merge
        """
        self._fields.update(family)

    def build(self, **extra_config: Any) -> type[BaseModel]:
        """Build the Pydantic model with all configured fields.

        Args:
            **extra_config: Additional model configuration to merge with
                           the configuration provided in __init__

        Returns:
            A new Pydantic model class

        Raises:
            ValueError: If no fields have been added to the builder
        """
        if not self._fields:
            raise ValueError(
                f"Cannot build model '{self.model_name}' with no fields. "
                f"Add at least one field or field family before building."
            )

        # Create field dictionary - unpack the single dict as keyword arguments
        fields = create_field_dict(**self._fields)

        # Merge model configuration
        config = {**self.model_config, **extra_config}

        # Create and return the model
        return create_model(self.model_name, fields=fields, config=config)

    def preview(self) -> dict[str, str]:
        """Preview the fields that will be included in the model.

        Returns:
            Dictionary mapping field names to their descriptions
        """
        return {
            name: template.description or f"{template.base_type} field"
            for name, template in self._fields.items()
        }
Functions
__init__(model_name, **model_config)

Initialize the builder with a model name.

Parameters:

Name Type Description Default
model_name str

Name for the generated model class

required
**model_config Any

Additional Pydantic model configuration options (e.g., orm_mode=True, validate_assignment=True)

{}
Source code in src/pydapter/fields/builder.py
def __init__(self, model_name: str, **model_config: Any):
    """Initialize the builder with a model name.

    Args:
        model_name: Name for the generated model class
        **model_config: Additional Pydantic model configuration options
            (e.g., orm_mode=True, validate_assignment=True)
    """
    self.model_name = model_name
    self.model_config = model_config
    self._fields: dict[str, FieldTemplate] = {}
add_field(name, template, replace=True)

Add or update a single field.

Parameters:

Name Type Description Default
name str

Field name

required
template FieldTemplate

FieldTemplate instance for the field

required
replace bool

If True (default), replaces existing field with same name. If False, raises ValueError if field already exists.

True

Returns:

Type Description
DomainModelBuilder

Self for method chaining

Raises:

Type Description
ValueError

If field exists and replace=False

Source code in src/pydapter/fields/builder.py
def add_field(
    self, name: str, template: FieldTemplate, replace: bool = True
) -> DomainModelBuilder:
    """Add or update a single field.

    Args:
        name: Field name
        template: FieldTemplate instance for the field
        replace: If True (default), replaces existing field with same name.
                If False, raises ValueError if field already exists.

    Returns:
        Self for method chaining

    Raises:
        ValueError: If field exists and replace=False
    """
    if not replace and name in self._fields:
        raise ValueError(
            f"Field '{name}' already exists. Set replace=True to override."
        )

    self._fields[name] = template
    return self
build(**extra_config)

Build the Pydantic model with all configured fields.

Parameters:

Name Type Description Default
**extra_config Any

Additional model configuration to merge with the configuration provided in init

{}

Returns:

Type Description
type[BaseModel]

A new Pydantic model class

Raises:

Type Description
ValueError

If no fields have been added to the builder

Source code in src/pydapter/fields/builder.py
def build(self, **extra_config: Any) -> type[BaseModel]:
    """Build the Pydantic model with all configured fields.

    Args:
        **extra_config: Additional model configuration to merge with
                       the configuration provided in __init__

    Returns:
        A new Pydantic model class

    Raises:
        ValueError: If no fields have been added to the builder
    """
    if not self._fields:
        raise ValueError(
            f"Cannot build model '{self.model_name}' with no fields. "
            f"Add at least one field or field family before building."
        )

    # Create field dictionary - unpack the single dict as keyword arguments
    fields = create_field_dict(**self._fields)

    # Merge model configuration
    config = {**self.model_config, **extra_config}

    # Create and return the model
    return create_model(self.model_name, fields=fields, config=config)
preview()

Preview the fields that will be included in the model.

Returns:

Type Description
dict[str, str]

Dictionary mapping field names to their descriptions

Source code in src/pydapter/fields/builder.py
def preview(self) -> dict[str, str]:
    """Preview the fields that will be included in the model.

    Returns:
        Dictionary mapping field names to their descriptions
    """
    return {
        name: template.description or f"{template.base_type} field"
        for name, template in self._fields.items()
    }
remove_field(name)

Remove a field from the builder.

Parameters:

Name Type Description Default
name str

Field name to remove

required

Returns:

Type Description
DomainModelBuilder

Self for method chaining

Raises:

Type Description
KeyError

If field doesn't exist

Source code in src/pydapter/fields/builder.py
def remove_field(self, name: str) -> DomainModelBuilder:
    """Remove a field from the builder.

    Args:
        name: Field name to remove

    Returns:
        Self for method chaining

    Raises:
        KeyError: If field doesn't exist
    """
    if name not in self._fields:
        raise KeyError(f"Field '{name}' not found in builder")

    del self._fields[name]
    return self
remove_fields(*names)

Remove multiple fields from the builder.

Parameters:

Name Type Description Default
*names str

Field names to remove

()

Returns:

Type Description
DomainModelBuilder

Self for method chaining

Source code in src/pydapter/fields/builder.py
def remove_fields(self, *names: str) -> DomainModelBuilder:
    """Remove multiple fields from the builder.

    Args:
        *names: Field names to remove

    Returns:
        Self for method chaining
    """
    for name in names:
        if name in self._fields:
            del self._fields[name]
    return self
with_audit_fields()

Add audit/tracking fields (created_by, updated_by, version).

Returns:

Type Description
DomainModelBuilder

Self for method chaining

Source code in src/pydapter/fields/builder.py
def with_audit_fields(self) -> DomainModelBuilder:
    """Add audit/tracking fields (created_by, updated_by, version).

    Returns:
        Self for method chaining
    """
    self._merge_family(FieldFamilies.AUDIT)
    return self
with_entity_fields(timezone_aware=False)

Add basic entity fields (id, created_at, updated_at).

Parameters:

Name Type Description Default
timezone_aware bool

If True, uses timezone-aware datetime fields

False

Returns:

Type Description
DomainModelBuilder

Self for method chaining

Source code in src/pydapter/fields/builder.py
def with_entity_fields(self, timezone_aware: bool = False) -> DomainModelBuilder:
    """Add basic entity fields (id, created_at, updated_at).

    Args:
        timezone_aware: If True, uses timezone-aware datetime fields

    Returns:
        Self for method chaining
    """
    family = FieldFamilies.ENTITY_TZ if timezone_aware else FieldFamilies.ENTITY
    self._merge_family(family)
    return self
with_family(family)

Add a custom field family.

Parameters:

Name Type Description Default
family dict[str, FieldTemplate]

Dictionary mapping field names to FieldTemplate instances

required

Returns:

Type Description
DomainModelBuilder

Self for method chaining

Source code in src/pydapter/fields/builder.py
def with_family(self, family: dict[str, FieldTemplate]) -> DomainModelBuilder:
    """Add a custom field family.

    Args:
        family: Dictionary mapping field names to FieldTemplate instances

    Returns:
        Self for method chaining
    """
    self._merge_family(family)
    return self
with_soft_delete(timezone_aware=False)

Add soft delete fields (deleted_at, is_deleted).

Parameters:

Name Type Description Default
timezone_aware bool

If True, uses timezone-aware datetime for deleted_at

False

Returns:

Type Description
DomainModelBuilder

Self for method chaining

Source code in src/pydapter/fields/builder.py
def with_soft_delete(self, timezone_aware: bool = False) -> DomainModelBuilder:
    """Add soft delete fields (deleted_at, is_deleted).

    Args:
        timezone_aware: If True, uses timezone-aware datetime for deleted_at

    Returns:
        Self for method chaining
    """
    family = (
        FieldFamilies.SOFT_DELETE_TZ
        if timezone_aware
        else FieldFamilies.SOFT_DELETE
    )
    self._merge_family(family)

    return self

Functions

pydapter.fields.validation_patterns

Validation Patterns - Common validation patterns for field templates.

This module provides pre-built validation patterns and constraint builders for creating field templates with consistent validation rules.

Classes

ValidationPatterns

Common validation patterns for field templates.

This class provides regex patterns and validation functions for common field types like emails, URLs, phone numbers, etc. These can be used with FieldTemplate to create consistently validated fields.

Source code in src/pydapter/fields/validation_patterns.py
class ValidationPatterns:
    """Common validation patterns for field templates.

    This class provides regex patterns and validation functions for common
    field types like emails, URLs, phone numbers, etc. These can be used
    with FieldTemplate to create consistently validated fields.
    """

    # Email pattern (simplified, RFC-compliant email validation is complex)
    EMAIL = re.compile(r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")

    # URL patterns
    HTTP_URL = re.compile(
        r"^https?://[a-zA-Z0-9.-]+(?:\.[a-zA-Z]{2,})+(?:/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$"
    )
    HTTPS_URL = re.compile(
        r"^https://[a-zA-Z0-9.-]+(?:\.[a-zA-Z]{2,})+(?:/[^?#]*)?(?:\?[^#]*)?(?:#.*)?$"
    )

    # Phone patterns
    US_PHONE = re.compile(
        r"^\+?1?\s*\(?([0-9]{3})\)?[-.\s]?([0-9]{3})[-.\s]?([0-9]{4})$"
    )
    INTERNATIONAL_PHONE = re.compile(r"^\+?[0-9\s\-\(\)]{10,20}$")

    # Username patterns
    USERNAME_ALPHANUMERIC = re.compile(r"^[a-zA-Z0-9_]{3,32}$")
    USERNAME_WITH_DASH = re.compile(r"^[a-zA-Z0-9_-]{3,32}$")
    USERNAME_STRICT = re.compile(r"^[a-z][a-z0-9_]{2,31}$")  # Must start with lowercase

    # Password patterns (for validation, not storage)
    PASSWORD_STRONG = re.compile(
        r"^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$"
    )

    # Identifier patterns
    SLUG = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
    UUID = re.compile(
        r"^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$",
        re.IGNORECASE,
    )
    ALPHANUMERIC_ID = re.compile(r"^[A-Z0-9]{6,12}$")

    # Code patterns
    HEX_COLOR = re.compile(r"^#?([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$")
    ISO_DATE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
    ISO_DATETIME = re.compile(
        r"^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?$"
    )

    # Geographic patterns
    LATITUDE = re.compile(r"^[-+]?([1-8]?\d(\.\d+)?|90(\.0+)?)$")
    LONGITUDE = re.compile(r"^[-+]?(180(\.0+)?|((1[0-7]\d)|([1-9]?\d))(\.\d+)?)$")
    ZIP_US = re.compile(r"^\d{5}(-\d{4})?$")
    ZIP_CA = re.compile(r"^[A-Z]\d[A-Z]\s?\d[A-Z]\d$", re.IGNORECASE)

    # Financial patterns
    CREDIT_CARD = re.compile(r"^\d{13,19}$")
    IBAN = re.compile(r"^[A-Z]{2}\d{2}[A-Z0-9]{1,30}$")
    BITCOIN_ADDRESS = re.compile(r"^(bc1|[13])[a-zA-HJ-NP-Z0-9]{25,39}$")

    # Social media patterns
    TWITTER_HANDLE = re.compile(r"^@[A-Za-z0-9_]{1,15}$")
    INSTAGRAM_HANDLE = re.compile(r"^@[A-Za-z0-9_.]{1,30}$")
    HASHTAG = re.compile(r"^#[A-Za-z0-9_]+$")

    @staticmethod
    def validate_pattern(
        pattern: Pattern[str], error_message: str
    ) -> Callable[[str], str]:
        """Create a validator function for a regex pattern.

        Args:
            pattern: Compiled regex pattern
            error_message: Error message to show on validation failure

        Returns:
            A validator function that checks the pattern
        """

        def validator(value: str) -> str:
            if not isinstance(value, str):
                raise ValueError(f"Expected string, got {type(value).__name__}")
            if not pattern.match(value):
                raise ValueError(error_message)
            return value

        return validator

    @staticmethod
    def normalize_whitespace() -> Callable[[str], str]:
        """Create a validator that normalizes whitespace in strings."""

        def validator(value: str) -> str:
            if isinstance(value, str):
                # Strip leading/trailing whitespace and normalize internal spaces
                return " ".join(value.split())
            return value

        return validator

    @staticmethod
    def strip_whitespace() -> Callable[[str], str]:
        """Create a validator that strips leading/trailing whitespace."""

        def validator(value: str) -> str:
            if isinstance(value, str):
                return value.strip()
            return value

        return validator

    @staticmethod
    def lowercase() -> Callable[[str], str]:
        """Create a validator that converts strings to lowercase."""

        def validator(value: str) -> str:
            if isinstance(value, str):
                return value.lower()
            return value

        return validator

    @staticmethod
    def uppercase() -> Callable[[str], str]:
        """Create a validator that converts strings to uppercase."""

        def validator(value: str) -> str:
            if isinstance(value, str):
                return value.upper()
            return value

        return validator

    @staticmethod
    def titlecase() -> Callable[[str], str]:
        """Create a validator that converts strings to title case."""

        def validator(value: str) -> str:
            if isinstance(value, str):
                return value.title()
            return value

        return validator
Functions
lowercase() staticmethod

Create a validator that converts strings to lowercase.

Source code in src/pydapter/fields/validation_patterns.py
@staticmethod
def lowercase() -> Callable[[str], str]:
    """Create a validator that converts strings to lowercase."""

    def validator(value: str) -> str:
        if isinstance(value, str):
            return value.lower()
        return value

    return validator
normalize_whitespace() staticmethod

Create a validator that normalizes whitespace in strings.

Source code in src/pydapter/fields/validation_patterns.py
@staticmethod
def normalize_whitespace() -> Callable[[str], str]:
    """Create a validator that normalizes whitespace in strings."""

    def validator(value: str) -> str:
        if isinstance(value, str):
            # Strip leading/trailing whitespace and normalize internal spaces
            return " ".join(value.split())
        return value

    return validator
strip_whitespace() staticmethod

Create a validator that strips leading/trailing whitespace.

Source code in src/pydapter/fields/validation_patterns.py
@staticmethod
def strip_whitespace() -> Callable[[str], str]:
    """Create a validator that strips leading/trailing whitespace."""

    def validator(value: str) -> str:
        if isinstance(value, str):
            return value.strip()
        return value

    return validator
titlecase() staticmethod

Create a validator that converts strings to title case.

Source code in src/pydapter/fields/validation_patterns.py
@staticmethod
def titlecase() -> Callable[[str], str]:
    """Create a validator that converts strings to title case."""

    def validator(value: str) -> str:
        if isinstance(value, str):
            return value.title()
        return value

    return validator
uppercase() staticmethod

Create a validator that converts strings to uppercase.

Source code in src/pydapter/fields/validation_patterns.py
@staticmethod
def uppercase() -> Callable[[str], str]:
    """Create a validator that converts strings to uppercase."""

    def validator(value: str) -> str:
        if isinstance(value, str):
            return value.upper()
        return value

    return validator
validate_pattern(pattern, error_message) staticmethod

Create a validator function for a regex pattern.

Parameters:

Name Type Description Default
pattern Pattern[str]

Compiled regex pattern

required
error_message str

Error message to show on validation failure

required

Returns:

Type Description
Callable[[str], str]

A validator function that checks the pattern

Source code in src/pydapter/fields/validation_patterns.py
@staticmethod
def validate_pattern(
    pattern: Pattern[str], error_message: str
) -> Callable[[str], str]:
    """Create a validator function for a regex pattern.

    Args:
        pattern: Compiled regex pattern
        error_message: Error message to show on validation failure

    Returns:
        A validator function that checks the pattern
    """

    def validator(value: str) -> str:
        if not isinstance(value, str):
            raise ValueError(f"Expected string, got {type(value).__name__}")
        if not pattern.match(value):
            raise ValueError(error_message)
        return value

    return validator

Functions

create_pattern_template(pattern, base_type=str, description='Pattern-validated field', error_message=None, **kwargs)

Create a FieldTemplate with pattern validation.

Parameters:

Name Type Description Default
pattern str | Pattern[str]

Regex pattern (string or compiled Pattern)

required
base_type type

Base type for the field (default: str)

str
description str

Field description

'Pattern-validated field'
error_message str | None

Custom error message for validation failures

None
**kwargs Any

Additional arguments for FieldTemplate

{}

Returns:

Type Description
FieldTemplate

A FieldTemplate with pattern validation

Examples:

# Create a field for US phone numbers
us_phone = create_pattern_template(
    ValidationPatterns.US_PHONE,
    description="US phone number",
    error_message="Invalid US phone number format"
)

# Create a field for slugs
slug_field = create_pattern_template(
    r"^[a-z0-9]+(?:-[a-z0-9]+)*$",
    description="URL-friendly slug",
    error_message="Slug must contain only lowercase letters, numbers, and hyphens"
)
Source code in src/pydapter/fields/validation_patterns.py
def create_pattern_template(
    pattern: str | Pattern[str],
    base_type: type = str,
    description: str = "Pattern-validated field",
    error_message: str | None = None,
    **kwargs: Any,
) -> FieldTemplate:
    """Create a FieldTemplate with pattern validation.

    Args:
        pattern: Regex pattern (string or compiled Pattern)
        base_type: Base type for the field (default: str)
        description: Field description
        error_message: Custom error message for validation failures
        **kwargs: Additional arguments for FieldTemplate

    Returns:
        A FieldTemplate with pattern validation

    Examples:
        ```python
        # Create a field for US phone numbers
        us_phone = create_pattern_template(
            ValidationPatterns.US_PHONE,
            description="US phone number",
            error_message="Invalid US phone number format"
        )

        # Create a field for slugs
        slug_field = create_pattern_template(
            r"^[a-z0-9]+(?:-[a-z0-9]+)*$",
            description="URL-friendly slug",
            error_message="Slug must contain only lowercase letters, numbers, and hyphens"
        )
        ```
    """
    if isinstance(pattern, str):
        pattern = re.compile(pattern)

    if error_message is None:
        error_message = f"Value does not match pattern: {pattern.pattern}"

    # Use constr for pattern validation
    annotated_type = constr(pattern=pattern.pattern)

    return FieldTemplate(
        base_type=annotated_type,
        description=description,
        **kwargs,
    )

create_range_template(base_type, *, gt=None, ge=None, lt=None, le=None, description='Range-constrained numeric field', **kwargs)

Create a FieldTemplate with numeric range constraints.

Parameters:

Name Type Description Default
base_type type[int] | type[float]

Either int or float

required
gt int | float | None

Greater than constraint

None
ge int | float | None

Greater than or equal constraint

None
lt int | float | None

Less than constraint

None
le int | float | None

Less than or equal constraint

None
description str

Field description

'Range-constrained numeric field'
**kwargs Any

Additional arguments for FieldTemplate

{}

Returns:

Type Description
FieldTemplate

A FieldTemplate with range constraints

Examples:

# Create a percentage field (0-100)
percentage = create_range_template(
    float,
    ge=0,
    le=100,
    description="Percentage value"
)

# Create an age field (0-150)
age = create_range_template(
    int,
    ge=0,
    le=150,
    description="Person's age"
)

# Create a temperature field (-273.15 to infinity)
temperature_celsius = create_range_template(
    float,
    gt=-273.15,
    description="Temperature in Celsius"
)
Source code in src/pydapter/fields/validation_patterns.py
def create_range_template(
    base_type: type[int] | type[float],
    *,
    gt: int | float | None = None,
    ge: int | float | None = None,
    lt: int | float | None = None,
    le: int | float | None = None,
    description: str = "Range-constrained numeric field",
    **kwargs: Any,
) -> FieldTemplate:
    """Create a FieldTemplate with numeric range constraints.

    Args:
        base_type: Either int or float
        gt: Greater than constraint
        ge: Greater than or equal constraint
        lt: Less than constraint
        le: Less than or equal constraint
        description: Field description
        **kwargs: Additional arguments for FieldTemplate

    Returns:
        A FieldTemplate with range constraints

    Examples:
        ```python
        # Create a percentage field (0-100)
        percentage = create_range_template(
            float,
            ge=0,
            le=100,
            description="Percentage value"
        )

        # Create an age field (0-150)
        age = create_range_template(
            int,
            ge=0,
            le=150,
            description="Person's age"
        )

        # Create a temperature field (-273.15 to infinity)
        temperature_celsius = create_range_template(
            float,
            gt=-273.15,
            description="Temperature in Celsius"
        )
        ```
    """
    if base_type is int:
        # Use conint for integer constraints
        annotated_type = conint(gt=gt, ge=ge, lt=lt, le=le)
    elif base_type is float:
        # Use confloat for float constraints
        annotated_type = confloat(gt=gt, ge=ge, lt=lt, le=le)
    else:
        raise ValueError("base_type must be int or float")

    return FieldTemplate(
        base_type=annotated_type,
        description=description,
        **kwargs,
    )