Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly rebuild Pydantic models if necessary #1176

Merged
merged 5 commits into from
Dec 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .changeset/properly_rebuild_pydantic_models_if_necessary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
default: patch
---

# Fix compatibility with Pydantic 2.10+

#1176 by @Viicos

Set `defer_build` to models that we know will fail to build, and call `model_rebuild`
in the `__init__.py` file.
11 changes: 11 additions & 0 deletions openapi_python_client/schema/openapi_schema_pydantic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,14 @@
from .server_variable import ServerVariable
from .tag import Tag
from .xml import XML

PathItem.model_rebuild()
Operation.model_rebuild()
Components.model_rebuild()
Encoding.model_rebuild()
MediaType.model_rebuild()
OpenAPI.model_rebuild()
Parameter.model_rebuild()
Header.model_rebuild()
RequestBody.model_rebuild()
Response.model_rebuild()
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class Components(BaseModel):
links: Optional[dict[str, Union[Link, Reference]]] = None
callbacks: Optional[dict[str, Union[Callback, Reference]]] = None
model_config = ConfigDict(
# `Callback` contains an unresolvable forward reference, will rebuild in `__init__.py`:
defer_build=True,
extra="allow",
json_schema_extra={
"examples": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@

if TYPE_CHECKING: # pragma: no cover
from .header import Header
else:
Header = "Header"


class Encoding(BaseModel):
Expand All @@ -19,11 +17,13 @@ class Encoding(BaseModel):
"""

contentType: Optional[str] = None
headers: Optional[dict[str, Union[Header, Reference]]] = None
headers: Optional[dict[str, Union["Header", Reference]]] = None
style: Optional[str] = None
explode: bool = False
allowReserved: bool = False
model_config = ConfigDict(
# `Header` is an unresolvable forward reference, will rebuild in `__init__.py`:
defer_build=True,
extra="allow",
json_schema_extra={
"examples": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class Header(Parameter):
name: str = Field(default="")
param_in: ParameterLocation = Field(default=ParameterLocation.HEADER, alias="in")
model_config = ConfigDict(
# `Parameter` is not build yet, will rebuild in `__init__.py`:
defer_build=True,
extra="allow",
populate_by_name=True,
json_schema_extra={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ class MediaType(BaseModel):
examples: Optional[dict[str, Union[Example, Reference]]] = None
encoding: Optional[dict[str, Encoding]] = None
model_config = ConfigDict(
# `Encoding` is not build yet, will rebuild in `__init__.py`:
defer_build=True,
extra="allow",
populate_by_name=True,
json_schema_extra={
Expand Down
12 changes: 5 additions & 7 deletions openapi_python_client/schema/openapi_schema_pydantic/open_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,6 @@
from .components import Components
from .external_documentation import ExternalDocumentation
from .info import Info

# Required to update forward ref after object creation
from .path_item import PathItem # noqa: F401
from .paths import Paths
from .security_requirement import SecurityRequirement
from .server import Server
Expand All @@ -32,7 +29,11 @@ class OpenAPI(BaseModel):
tags: Optional[list[Tag]] = None
externalDocs: Optional[ExternalDocumentation] = None
openapi: str
model_config = ConfigDict(extra="allow")
model_config = ConfigDict(
# `Components` is not build yet, will rebuild in `__init__.py`:
defer_build=True,
extra="allow",
)

@field_validator("openapi")
@classmethod
Expand All @@ -46,6 +47,3 @@ def check_openapi_version(cls, value: str) -> str:
if int(parts[1]) > 1:
raise ValueError(f"Only OpenAPI versions 3.1.* are supported, got {value}")
return value


OpenAPI.model_rebuild()
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@

from .callback import Callback
from .external_documentation import ExternalDocumentation
from .header import Header # noqa: F401
from .parameter import Parameter

# Required to update forward ref after object creation, as this is not imported yet
from .path_item import PathItem # noqa: F401
from .reference import Reference
from .request_body import RequestBody
from .responses import Responses
Expand Down Expand Up @@ -38,6 +34,8 @@ class Operation(BaseModel):
security: Optional[list[SecurityRequirement]] = None
servers: Optional[list[Server]] = None
model_config = ConfigDict(
# `Callback` contains an unresolvable forward reference, will rebuild in `__init__.py`:
defer_build=True,
extra="allow",
json_schema_extra={
"examples": [
Expand Down Expand Up @@ -89,7 +87,3 @@ class Operation(BaseModel):
]
},
)


# PathItem in Callback uses Operation, so we need to update forward refs due to circular dependency
Operation.model_rebuild()
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class Parameter(BaseModel):
examples: Optional[dict[str, Union[Example, Reference]]] = None
content: Optional[dict[str, MediaType]] = None
model_config = ConfigDict(
# `MediaType` is not build yet, will rebuild in `__init__.py`:
defer_build=True,
extra="allow",
populate_by_name=True,
json_schema_extra={
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from typing import Optional, Union
from typing import TYPE_CHECKING, Optional, Union

from pydantic import BaseModel, ConfigDict, Field

from .parameter import Parameter
from .reference import Reference
from .server import Server

if TYPE_CHECKING:
from .operation import Operation # pragma: no cover


class PathItem(BaseModel):
"""
Expand Down Expand Up @@ -33,6 +36,8 @@ class PathItem(BaseModel):
servers: Optional[list[Server]] = None
parameters: Optional[list[Union[Parameter, Reference]]] = None
model_config = ConfigDict(
# `Operation` is an unresolvable forward reference, will rebuild in `__init__.py`:
defer_build=True,
extra="allow",
populate_by_name=True,
json_schema_extra={
Expand Down Expand Up @@ -69,9 +74,3 @@ class PathItem(BaseModel):
]
},
)


# Operation uses PathItem via Callback, so we need late import and to update forward refs due to circular dependency
from .operation import Operation # noqa: E402

PathItem.model_rebuild()
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ class RequestBody(BaseModel):
content: dict[str, MediaType]
required: bool = False
model_config = ConfigDict(
# `MediaType` is not build yet, will rebuild in `__init__.py`:
defer_build=True,
extra="allow",
json_schema_extra={
"examples": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ class Response(BaseModel):
content: Optional[dict[str, MediaType]] = None
links: Optional[dict[str, Union[Link, Reference]]] = None
model_config = ConfigDict(
# `MediaType` is not build yet, will rebuild in `__init__.py`:
defer_build=True,
extra="allow",
json_schema_extra={
"examples": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class Schema(BaseModel):
anyOf: list[Union[Reference, "Schema"]] = Field(default_factory=list)
schema_not: Optional[Union[Reference, "Schema"]] = Field(default=None, alias="not")
items: Optional[Union[Reference, "Schema"]] = None
prefixItems: Optional[list[Union[Reference, "Schema"]]] = Field(default_factory=list)
prefixItems: list[Union[Reference, "Schema"]] = Field(default_factory=list)
properties: Optional[dict[str, Union[Reference, "Schema"]]] = None
additionalProperties: Optional[Union[bool, Reference, "Schema"]] = None
description: Optional[str] = None
Expand Down Expand Up @@ -206,6 +206,3 @@ def handle_nullable(self) -> "Schema":
self.oneOf = [Schema(type=DataType.NULL), Schema(allOf=self.allOf)]
self.allOf = []
return self


Schema.model_rebuild()
Loading
Loading