Skip to content

Commit c0ee981

Browse files
committed
fix - pydantic 2.11.* compat
c.f. pydantic/pydantic#11032
1 parent a55a084 commit c0ee981

4 files changed

Lines changed: 82 additions & 8 deletions

File tree

aiopenapi3/model.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
from .base import ReferenceBase, SchemaBase
2020
from . import me
21-
from .pydanticv2 import field_class_to_schema
21+
from .pydanticv2 import field_class_to_schema, create_model
2222

2323
if typing.TYPE_CHECKING:
2424
from .base import DiscriminatorBase
@@ -241,7 +241,7 @@ def model(self) -> Union[type[BaseModel], type[None]]:
241241
m = self.root
242242
else:
243243
if self.type_ == "object":
244-
m = pydantic.create_model(
244+
m = create_model(
245245
self.name,
246246
__module__=me.__name__,
247247
__config__=self.config,
@@ -259,13 +259,11 @@ def collapse(cls, type_name, items: list["_ClassInfo"]) -> type[BaseModel]:
259259

260260
if len(r) > 1:
261261
ru: object = Union[tuple(r)]
262-
m: type[RootModel] = pydantic.create_model(
263-
type_name, __base__=(ConfiguredRootModel[ru],), __module__=me.__name__
264-
)
262+
m: type[RootModel] = create_model(type_name, __base__=(ConfiguredRootModel[ru],), __module__=me.__name__)
265263
elif len(r) == 1:
266264
m: type[BaseModel] = cast(type[BaseModel], r[0])
267265
if not is_basemodel(m):
268-
m = pydantic.create_model(type_name, __base__=(ConfiguredRootModel[m],), __module__=me.__name__)
266+
m = create_model(type_name, __base__=(ConfiguredRootModel[m],), __module__=me.__name__)
269267
else: # == 0
270268
assert len(r), r
271269
return m

aiopenapi3/pydanticv2.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,79 @@
3838
field_class_to_schema: tuple[tuple[Any, dict[str, Any]], ...] = tuple(
3939
(field_class, TypeAdapter(field_class).json_schema()) for field_class in field_classes_to_support
4040
)
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+
)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ authors = [
66
]
77
dependencies = [
88
"PyYaml",
9-
"pydantic>=2.10.5",
9+
"pydantic",
1010
"email-validator",
1111
"yarl",
1212
"httpx",

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)