|
38 | 38 | field_class_to_schema: tuple[tuple[Any, dict[str, Any]], ...] = tuple( |
39 | 39 | (field_class, TypeAdapter(field_class).json_schema()) for field_class in field_classes_to_support |
40 | 40 | ) |
| 41 | + |
| 42 | +from pydantic import ConfigDict, BaseModel, PydanticUserError |
| 43 | +from pydantic.main import ModelT |
| 44 | +from typing import Callable, cast |
| 45 | +import sys |
| 46 | +import types |
| 47 | + |
| 48 | + |
| 49 | +def create_model( # noqa: C901 |
| 50 | + model_name: str, |
| 51 | + /, |
| 52 | + *, |
| 53 | + __config__: ConfigDict | None = None, |
| 54 | + __doc__: str | None = None, |
| 55 | + __base__: type[ModelT] | tuple[type[ModelT], ...] | None = None, |
| 56 | + __module__: str | None = None, |
| 57 | + __validators__: dict[str, Callable[..., Any]] | None = None, |
| 58 | + __cls_kwargs__: dict[str, Any] | None = None, |
| 59 | + # TODO PEP 747: replace `Any` by the TypeForm: |
| 60 | + **field_definitions: Any | tuple[str, Any], |
| 61 | +) -> type[ModelT]: |
| 62 | + """ |
| 63 | + unfortunate this is required, but … |
| 64 | + c.f. https://github.com/pydantic/pydantic/pull/11032#issuecomment-2797667916 |
| 65 | + """ |
| 66 | + if __base__ is None: |
| 67 | + __base__ = (cast("type[ModelT]", BaseModel),) |
| 68 | + elif not isinstance(__base__, tuple): |
| 69 | + __base__ = (__base__,) |
| 70 | + |
| 71 | + __cls_kwargs__ = __cls_kwargs__ or {} |
| 72 | + |
| 73 | + fields: dict[str, Any] = {} |
| 74 | + annotations: dict[str, Any] = {} |
| 75 | + |
| 76 | + for f_name, f_def in field_definitions.items(): |
| 77 | + if isinstance(f_def, tuple): |
| 78 | + if len(f_def) != 2: |
| 79 | + raise PydanticUserError( |
| 80 | + f"Field definition for {f_name!r} should a single element representing the type or a two-tuple, the first element " |
| 81 | + "being the type and the second element the assigned value (either a default or the `Field()` function).", |
| 82 | + code="create-model-field-definitions", |
| 83 | + ) |
| 84 | + |
| 85 | + if f_def[0]: |
| 86 | + annotations[f_name] = f_def[0] |
| 87 | + fields[f_name] = f_def[1] |
| 88 | + else: |
| 89 | + annotations[f_name] = f_def |
| 90 | + |
| 91 | + if __module__ is None: |
| 92 | + f = sys._getframe(1) |
| 93 | + __module__ = f.f_globals["__name__"] |
| 94 | + |
| 95 | + namespace: dict[str, Any] = {"__annotations__": annotations, "__module__": __module__} |
| 96 | + if __doc__: |
| 97 | + namespace.update({"__doc__": __doc__}) |
| 98 | + if __validators__: |
| 99 | + namespace.update(__validators__) |
| 100 | + namespace.update(fields) |
| 101 | + if __config__: |
| 102 | + namespace["model_config"] = __config__ |
| 103 | + resolved_bases = types.resolve_bases(__base__) |
| 104 | + meta, ns, kwds = types.prepare_class(model_name, resolved_bases, kwds=__cls_kwargs__) |
| 105 | + if resolved_bases is not __base__: |
| 106 | + ns["__orig_bases__"] = __base__ |
| 107 | + namespace.update(ns) |
| 108 | + |
| 109 | + return meta( |
| 110 | + model_name, |
| 111 | + resolved_bases, |
| 112 | + namespace, |
| 113 | + __pydantic_reset_parent_namespace__=False, |
| 114 | + _create_model_module=__module__, |
| 115 | + **kwds, |
| 116 | + ) |
0 commit comments