diff --git a/README.md b/README.md
index 2769cce..2fab3c7 100644
--- a/README.md
+++ b/README.md
@@ -35,7 +35,7 @@ open_api = OpenAPI(
)
},
)
-print(open_api.json(by_alias=True, exclude_none=True, indent=2))
+print(open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2))
```
Result:
@@ -69,15 +69,14 @@ Result:
## Take advantage of Pydantic
-Pydantic is a great tool, allow you to use object / dict / mixed data for for input.
-
+Pydantic is a great tool. It allows you to use object / dict / mixed data for input.
The following examples give the same OpenAPI result as above:
```python
from openapi_schema_pydantic import OpenAPI, PathItem, Response
# Construct OpenAPI from dict
-open_api = OpenAPI.parse_obj({
+open_api = OpenAPI.model_validate({
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {
"/ping": {
@@ -87,7 +86,7 @@ open_api = OpenAPI.parse_obj({
})
# Construct OpenAPI with mix of dict/object
-open_api = OpenAPI.parse_obj({
+open_api = OpenAPI.model_validate({
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {
"/ping": PathItem(
@@ -100,10 +99,10 @@ open_api = OpenAPI.parse_obj({
## Use Pydantic classes as schema
- The [Schema Object](https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.3.md#schemaObject)
- in OpenAPI has definitions and tweaks in JSON Schema, which is hard to comprehend and define a good data class
+ in OpenAPI has definitions and tweaks in JSON Schema, which are hard to comprehend and define a good data class
- Pydantic already has a good way to [create JSON schema](https://pydantic-docs.helpmanual.io/usage/schema/),
let's not re-invent the wheel
-
+
The approach to deal with this:
1. Use `PydanticSchema` objects to represent the `Schema` in `OpenAPI` object
@@ -116,7 +115,7 @@ from openapi_schema_pydantic import OpenAPI
from openapi_schema_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
def construct_base_open_api() -> OpenAPI:
- return OpenAPI.parse_obj({
+ return OpenAPI.model_validate({
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {
"/ping": {
@@ -148,8 +147,8 @@ class PingResponse(BaseModel):
open_api = construct_base_open_api()
open_api = construct_open_api_with_schema_class(open_api)
-# print the result openapi.json
-print(open_api.json(by_alias=True, exclude_none=True, indent=2))
+# print the result of openapi.model_dump_json()
+print(open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2))
```
Result:
@@ -198,45 +197,45 @@ Result:
"components": {
"schemas": {
"PingRequest": {
- "title": "PingRequest",
- "required": [
- "req_foo",
- "req_bar"
- ],
- "type": "object",
"properties": {
"req_foo": {
- "title": "Req Foo",
"type": "string",
+ "title": "Req Foo",
"description": "foo value of the request"
},
"req_bar": {
- "title": "Req Bar",
"type": "string",
+ "title": "Req Bar",
"description": "bar value of the request"
}
},
+ "type": "object",
+ "required": [
+ "req_foo",
+ "req_bar"
+ ],
+ "title": "PingRequest",
"description": "Ping Request"
},
"PingResponse": {
- "title": "PingResponse",
- "required": [
- "resp_foo",
- "resp_bar"
- ],
- "type": "object",
"properties": {
"resp_foo": {
- "title": "Resp Foo",
"type": "string",
+ "title": "Resp Foo",
"description": "foo value of the response"
},
"resp_bar": {
- "title": "Resp Bar",
"type": "string",
+ "title": "Resp Bar",
"description": "bar value of the response"
}
},
+ "type": "object",
+ "required": [
+ "resp_foo",
+ "resp_bar"
+ ],
+ "title": "PingResponse",
"description": "Ping response"
}
}
@@ -246,21 +245,21 @@ Result:
## Notes
-### Use of OpenAPI.json() / OpenAPI.dict()
+### Use of OpenAPI.model_dump_json() / OpenAPI.model_dump()
-When using `OpenAPI.json()` / `OpenAPI.dict()` function,
-arguments `by_alias=True, exclude_none=True` has to be in place.
-Otherwise the result json will not fit the OpenAPI standard.
+When using `OpenAPI.model_dump_json()` / `OpenAPI.model_dump()` functions,
+the arguments `by_alias=True, exclude_none=True` have to be in place,
+otherwise the resulting json will not fit the OpenAPI standard.
```python
# OK
-open_api.json(by_alias=True, exclude_none=True, indent=2)
+open_api.model_dump_json(by_alias=True, exclude_none=True, indent=2)
# Not good
-open_api.json(indent=2)
+open_api.model_dump_json(indent=2)
```
-More info about field alias:
+More info about field aliases:
| OpenAPI version | Field alias info |
| --------------- | ---------------- |
@@ -280,7 +279,7 @@ Please refer to the following for more info:
### Use OpenAPI 3.0.3 instead of 3.1.0
Some UI renderings (e.g. Swagger) still do not support OpenAPI 3.1.0.
-It is allowed to use the old 3.0.3 version by importing from different paths:
+The old 3.0.3 version is available by importing from different paths:
```python
from openapi_schema_pydantic.v3.v3_0_3 import OpenAPI, ...
diff --git a/openapi_schema_pydantic/util.py b/openapi_schema_pydantic/util.py
index cb57969..240d5a6 100644
--- a/openapi_schema_pydantic/util.py
+++ b/openapi_schema_pydantic/util.py
@@ -1,27 +1,34 @@
import logging
-from typing import Any, List, Set, Type, TypeVar
+from typing import Any, List, Set, Type
-from pydantic import BaseModel
-from pydantic.schema import schema
+from pydantic import BaseModel, create_model
+from pydantic.json_schema import models_json_schema, JsonSchemaMode
from . import Components, OpenAPI, Reference, Schema
logger = logging.getLogger(__name__)
-PydanticType = TypeVar("PydanticType", bound=BaseModel)
+PydanticType = BaseModel
ref_prefix = "#/components/schemas/"
+ref_template = ref_prefix + "{model}"
class PydanticSchema(Schema):
"""Special `Schema` class to indicate a reference from pydantic class"""
- schema_class: Type[PydanticType] = ...
+ schema_class: Type[BaseModel]
"""the class that is used for generate the schema"""
+def get_mode(cls: Type[BaseModel], default: JsonSchemaMode = "validation") -> JsonSchemaMode:
+ if not hasattr(cls, "model_config"):
+ return default
+ return cls.model_config.get("json_schema_mode", default)
+
+
def construct_open_api_with_schema_class(
open_api: OpenAPI,
- schema_classes: List[Type[PydanticType]] = None,
+ schema_classes: List[Type[BaseModel]] | None = None,
scan_for_pydantic_schema_reference: bool = True,
by_alias: bool = True,
) -> OpenAPI:
@@ -36,7 +43,7 @@ def construct_open_api_with_schema_class(
:return: new OpenAPI object with "#/components/schemas" values updated.
If there is no update in "#/components/schemas" values, the original `open_api` will be returned.
"""
- new_open_api: OpenAPI = open_api.copy(deep=True)
+ new_open_api: OpenAPI = open_api.model_copy(deep=True)
if scan_for_pydantic_schema_reference:
extracted_schema_classes = _handle_pydantic_schema(new_open_api)
if schema_classes:
@@ -51,27 +58,31 @@ def construct_open_api_with_schema_class(
logger.debug(f"schema_classes{schema_classes}")
# update new_open_api with new #/components/schemas
- schema_definitions = schema(schema_classes, by_alias=by_alias, ref_prefix=ref_prefix)
+ key_map, schema_definitions = models_json_schema(
+ [(c, get_mode(c)) for c in schema_classes],
+ by_alias=by_alias,
+ ref_template=ref_template,
+ )
if not new_open_api.components:
new_open_api.components = Components()
if new_open_api.components.schemas:
for existing_key in new_open_api.components.schemas:
- if existing_key in schema_definitions.get("definitions"):
+ if existing_key in schema_definitions["$defs"]:
logger.warning(
f'"{existing_key}" already exists in {ref_prefix}. '
f'The value of "{ref_prefix}{existing_key}" will be overwritten.'
)
new_open_api.components.schemas.update(
- {key: Schema.parse_obj(schema_dict) for key, schema_dict in schema_definitions.get("definitions").items()}
+ {key: Schema.model_validate(schema_dict) for key, schema_dict in schema_definitions["$defs"].items()}
)
else:
new_open_api.components.schemas = {
- key: Schema.parse_obj(schema_dict) for key, schema_dict in schema_definitions.get("definitions").items()
+ key: Schema.model_validate(schema_dict) for key, schema_dict in schema_definitions["$defs"].items()
}
return new_open_api
-def _handle_pydantic_schema(open_api: OpenAPI) -> List[Type[PydanticType]]:
+def _handle_pydantic_schema(open_api: OpenAPI) -> List[Type[BaseModel]]:
"""
This function traverses the `OpenAPI` object and
@@ -84,11 +95,11 @@ def _handle_pydantic_schema(open_api: OpenAPI) -> List[Type[PydanticType]]:
:return: a list of schema classes extracted from `PydanticSchema` objects
"""
- pydantic_types: Set[Type[PydanticType]] = set()
+ pydantic_types: Set[Type[BaseModel]] = set()
def _traverse(obj: Any):
if isinstance(obj, BaseModel):
- fields = obj.__fields_set__
+ fields = obj.model_fields_set
for field in fields:
child_obj = obj.__getattribute__(field)
if isinstance(child_obj, PydanticSchema):
diff --git a/openapi_schema_pydantic/v3/v3_0_3/README.md b/openapi_schema_pydantic/v3/v3_0_3/README.md
index d5fae02..4a5a325 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/README.md
+++ b/openapi_schema_pydantic/v3/v3_0_3/README.md
@@ -20,7 +20,7 @@ the following fields are used with [alias](https://pydantic-docs.helpmanual.io/u
> The "in" field in Header object is actually a constant (`{"in": "header"}`).
> For convenience of object creation, the classes mentioned in above
-> has configured `allow_population_by_field_name=True`.
+> has configured `populate_by_name=True`.
>
> Reference: [Pydantic's Model Config](https://pydantic-docs.helpmanual.io/usage/model_config/)
diff --git a/openapi_schema_pydantic/v3/v3_0_3/__init__.py b/openapi_schema_pydantic/v3/v3_0_3/__init__.py
index 666664a..1cb9758 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/__init__.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/__init__.py
@@ -39,5 +39,7 @@
# resolve forward references
-Encoding.update_forward_refs(Header=Header)
-Schema.update_forward_refs()
+Encoding.model_rebuild()
+OpenAPI.model_rebuild()
+Components.model_rebuild()
+Operation.model_rebuild()
diff --git a/openapi_schema_pydantic/v3/v3_0_3/components.py b/openapi_schema_pydantic/v3/v3_0_3/components.py
index 9fab074..1ffd017 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/components.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/components.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional, Union
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .callback import Callback
from .example import Example
@@ -48,9 +48,9 @@ class Components(BaseModel):
callbacks: Optional[Dict[str, Union[Callback, Reference]]] = None
"""An object to hold reusable [Callback Objects](#callbackObject)."""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"schemas": {
@@ -111,4 +111,5 @@ class Config:
},
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/contact.py b/openapi_schema_pydantic/v3/v3_0_3/contact.py
index 2bda129..e504bd4 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/contact.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/contact.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import AnyUrl, BaseModel, Extra
+from pydantic import AnyUrl, BaseModel, ConfigDict
class Contact(BaseModel):
@@ -25,10 +25,11 @@ class Contact(BaseModel):
MUST be in the format of an email address.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"name": "API Support", "url": "http://www.example.com/support", "email": "support@example.com"}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/discriminator.py b/openapi_schema_pydantic/v3/v3_0_3/discriminator.py
index 699d4ea..95c15fa 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/discriminator.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/discriminator.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, Field, ConfigDict
class Discriminator(BaseModel):
@@ -14,7 +14,7 @@ class Discriminator(BaseModel):
When using the discriminator, _inline_ schemas will not be considered.
"""
- propertyName: str = ...
+ propertyName: str
"""
**REQUIRED**. The name of the property in the payload that will hold the discriminator value.
"""
@@ -24,9 +24,9 @@ class Discriminator(BaseModel):
An object to hold mappings between payload values and schema names or references.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"propertyName": "petType",
@@ -36,4 +36,5 @@ class Config:
},
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/encoding.py b/openapi_schema_pydantic/v3/v3_0_3/encoding.py
index 2821da4..21c40bf 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/encoding.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/encoding.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional, Union
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .reference import Reference
@@ -59,9 +59,9 @@ class Encoding(BaseModel):
This property SHALL be ignored if the request body media type is not `application/x-www-form-urlencoded`.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"contentType": "image/png, image/jpeg",
@@ -73,4 +73,5 @@ class Config:
},
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/example.py b/openapi_schema_pydantic/v3/v3_0_3/example.py
index 1d98bb1..46a73b7 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/example.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/example.py
@@ -1,10 +1,9 @@
from typing import Any, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
class Example(BaseModel):
-
summary: Optional[str] = None
"""
Short description for the example.
@@ -32,9 +31,9 @@ class Example(BaseModel):
The `value` field and `externalValue` field are mutually exclusive.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"summary": "A foo example", "value": {"foo": "bar"}},
{
@@ -43,4 +42,5 @@ class Config:
},
{"summary": "This is a text example", "externalValue": "http://foo.bar/examples/address-example.txt"},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/external_documentation.py b/openapi_schema_pydantic/v3/v3_0_3/external_documentation.py
index 599778c..8cb873f 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/external_documentation.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/external_documentation.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import AnyUrl, BaseModel, Extra
+from pydantic import AnyUrl, BaseModel, ConfigDict
class ExternalDocumentation(BaseModel):
@@ -12,12 +12,13 @@ class ExternalDocumentation(BaseModel):
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
"""
- url: AnyUrl = ...
+ url: AnyUrl
"""
**REQUIRED**. The URL for the target documentation.
Value MUST be in the format of a URL.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {"examples": [{"description": "Find more info here", "url": "https://example.com"}]}
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={"examples": [{"description": "Find more info here", "url": "https://example.com"}]},
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/header.py b/openapi_schema_pydantic/v3/v3_0_3/header.py
index c413cdc..1e078ee 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/header.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/header.py
@@ -1,4 +1,5 @@
-from pydantic import Extra, Field
+from pydantic import ConfigDict, Field
+from typing import Literal
from .parameter import Parameter
@@ -13,14 +14,15 @@ class Header(Parameter):
(for example, [`style`](#parameterStyle)).
"""
- name = Field(default="", const=True)
- param_in = Field(default="header", const=True, alias="in")
+ name: Literal[""] = Field(default="")
+ param_in: Literal["header"] = Field(default="header", alias="in")
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{"description": "The number of allowed requests in the current period", "schema": {"type": "integer"}}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/info.py b/openapi_schema_pydantic/v3/v3_0_3/info.py
index ee12e09..f125045 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/info.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/info.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import AnyUrl, BaseModel, Extra
+from pydantic import AnyUrl, BaseModel, ConfigDict
from .contact import Contact
from .license import License
@@ -13,7 +13,7 @@ class Info(BaseModel):
and MAY be presented in editing or documentation generation tools for convenience.
"""
- title: str = ...
+ title: str
"""
**REQUIRED**. The title of the API.
"""
@@ -40,15 +40,15 @@ class Info(BaseModel):
The license information for the exposed API.
"""
- version: str = ...
+ version: str
"""
**REQUIRED**. The version of the OpenAPI document
(which is distinct from the [OpenAPI Specification version](#oasVersion) or the API implementation version).
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"title": "Sample Pet Store App",
@@ -63,4 +63,5 @@ class Config:
"version": "1.0.1",
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/license.py b/openapi_schema_pydantic/v3/v3_0_3/license.py
index 29c97ab..6be0ba7 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/license.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/license.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import AnyUrl, BaseModel, Extra
+from pydantic import AnyUrl, BaseModel, ConfigDict
class License(BaseModel):
@@ -8,7 +8,7 @@ class License(BaseModel):
License information for the exposed API.
"""
- name: str = ...
+ name: str
"""
**REQUIRED**. The license name used for the API.
"""
@@ -19,6 +19,9 @@ class License(BaseModel):
MUST be in the format of a URL.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {"examples": [{"name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html"}]}
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
+ "examples": [{"name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html"}]
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/link.py b/openapi_schema_pydantic/v3/v3_0_3/link.py
index 5afd529..1361332 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/link.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/link.py
@@ -1,6 +1,6 @@
from typing import Any, Dict, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .server import Server
@@ -62,9 +62,9 @@ class Link(BaseModel):
A server object to be used by the target operation.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"operationId": "getUserAddressByUUID", "parameters": {"userUuid": "$response.body#/uuid"}},
{
@@ -72,4 +72,5 @@ class Config:
"parameters": {"username": "$response.body#/username"},
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/media_type.py b/openapi_schema_pydantic/v3/v3_0_3/media_type.py
index babdeab..4c3235b 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/media_type.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/media_type.py
@@ -1,6 +1,6 @@
from typing import Any, Dict, Optional, Union
-from pydantic import BaseModel, Extra, Field
+from pydantic import BaseModel, ConfigDict, Field
from .encoding import Encoding
from .example import Example
@@ -48,10 +48,10 @@ class MediaType(BaseModel):
when the media type is `multipart` or `application/x-www-form-urlencoded`.
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{
"schema": {"$ref": "#/components/schemas/Pet"},
@@ -79,4 +79,5 @@ class Config:
},
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/oauth_flow.py b/openapi_schema_pydantic/v3/v3_0_3/oauth_flow.py
index ce76309..ba4008a 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/oauth_flow.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/oauth_flow.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional, Union
-from pydantic import AnyUrl, BaseModel, Extra
+from pydantic import AnyUrl, BaseModel, ConfigDict
class OAuthFlow(BaseModel):
@@ -27,16 +27,16 @@ class OAuthFlow(BaseModel):
The URL to be used for obtaining refresh tokens. This MUST be in the form of a URL.
"""
- scopes: Dict[str, str] = ...
+ scopes: Dict[str, str]
"""
**REQUIRED**. The available scopes for the OAuth2 security scheme.
A map between the scope name and a short description for it.
The map MAY be empty.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"authorizationUrl": "https://example.com/api/oauth/dialog",
@@ -54,4 +54,5 @@ class Config:
"scopes": {"write:pets": "modify pets in your account", "read:pets": "read your pets"},
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/oauth_flows.py b/openapi_schema_pydantic/v3/v3_0_3/oauth_flows.py
index c60a8ff..edc48ab 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/oauth_flows.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/oauth_flows.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .oauth_flow import OAuthFlow
@@ -34,5 +34,4 @@ class OAuthFlows(BaseModel):
Previously called `accessCode` in OpenAPI 2.0.
"""
- class Config:
- extra = Extra.ignore
+ model_config = ConfigDict(extra="ignore")
diff --git a/openapi_schema_pydantic/v3/v3_0_3/open_api.py b/openapi_schema_pydantic/v3/v3_0_3/open_api.py
index 6b332e8..27b7200 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/open_api.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/open_api.py
@@ -1,6 +1,6 @@
from typing import List, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .components import Components
from .external_documentation import ExternalDocumentation
@@ -22,7 +22,7 @@ class OpenAPI(BaseModel):
This is *not* related to the API [`info.version`](#infoVersion) string.
"""
- info: Info = ...
+ info: Info
"""
**REQUIRED**. Provides metadata about the API. The metadata MAY be used by tooling as required.
"""
@@ -34,7 +34,7 @@ class OpenAPI(BaseModel):
the default value would be a [Server Object](#serverObject) with a [url](#serverUrl) value of `/`.
"""
- paths: Paths = ...
+ paths: Paths
"""
**REQUIRED**. The available paths and operations for the API.
"""
@@ -67,5 +67,4 @@ class OpenAPI(BaseModel):
Additional external documentation.
"""
- class Config:
- extra = Extra.ignore
+ model_config = ConfigDict(extra="ignore")
diff --git a/openapi_schema_pydantic/v3/v3_0_3/operation.py b/openapi_schema_pydantic/v3/v3_0_3/operation.py
index aa28a44..19f08ad 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/operation.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/operation.py
@@ -1,6 +1,6 @@
from typing import Dict, List, Optional, Union
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .callback import Callback
from .external_documentation import ExternalDocumentation
@@ -66,7 +66,7 @@ class Operation(BaseModel):
In other cases where the HTTP spec is vague, `requestBody` SHALL be ignored by consumers.
"""
- responses: Responses = ...
+ responses: Responses
"""
**REQUIRED**. The list of possible responses as they are returned from executing this operation.
"""
@@ -103,9 +103,9 @@ class Operation(BaseModel):
it will be overridden by this value.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"tags": ["pet"],
@@ -147,4 +147,5 @@ class Config:
"security": [{"petstore_auth": ["write:pets", "read:pets"]}],
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/parameter.py b/openapi_schema_pydantic/v3/v3_0_3/parameter.py
index 0f72df3..093b02e 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/parameter.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/parameter.py
@@ -1,6 +1,6 @@
from typing import Any, Dict, Optional, Union
-from pydantic import BaseModel, Field, Extra
+from pydantic import BaseModel, Field, ConfigDict
from .example import Example
from .media_type import MediaType
@@ -17,7 +17,7 @@ class Parameter(BaseModel):
"""Fixed Fields"""
- name: str = ...
+ name: str
"""
**REQUIRED**. The name of the parameter.
Parameter names are *case sensitive*.
@@ -140,10 +140,10 @@ class Parameter(BaseModel):
The map MUST only contain one entry.
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{
"name": "token",
@@ -189,4 +189,5 @@ class Config:
},
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/path_item.py b/openapi_schema_pydantic/v3/v3_0_3/path_item.py
index 4431ca2..45bac54 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/path_item.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/path_item.py
@@ -1,6 +1,6 @@
from typing import List, Optional, Union
-from pydantic import BaseModel, Extra, Field
+from pydantic import BaseModel, ConfigDict, Field
from .operation import Operation
from .parameter import Parameter
@@ -91,10 +91,10 @@ class PathItem(BaseModel):
[OpenAPI Object's components/parameters](#componentsParameters).
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{
"get": {
@@ -126,4 +126,5 @@ class Config:
],
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/reference.py b/openapi_schema_pydantic/v3/v3_0_3/reference.py
index 8155daa..747d0d2 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/reference.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/reference.py
@@ -1,4 +1,4 @@
-from pydantic import BaseModel, Extra, Field
+from pydantic import BaseModel, ConfigDict, Field
class Reference(BaseModel):
@@ -15,9 +15,10 @@ class Reference(BaseModel):
ref: str = Field(alias="$ref")
"""**REQUIRED**. The reference string."""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [{"$ref": "#/components/schemas/Pet"}, {"$ref": "Pet.json"}, {"$ref": "definitions.json#/Pet"}]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/request_body.py b/openapi_schema_pydantic/v3/v3_0_3/request_body.py
index 1bf58ce..6d79e5f 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/request_body.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/request_body.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .media_type import MediaType
@@ -16,7 +16,7 @@ class RequestBody(BaseModel):
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
"""
- content: Dict[str, MediaType] = ...
+ content: Dict[str, MediaType]
"""
**REQUIRED**. The content of the request body.
The key is a media type or [media type range](https://tools.ietf.org/html/rfc7231#appendix-D)
@@ -30,9 +30,9 @@ class RequestBody(BaseModel):
Determines if the request body is required in the request. Defaults to `false`.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"description": "user to add to the system",
@@ -78,4 +78,5 @@ class Config:
"content": {"text/plain": {"schema": {"type": "array", "items": {"type": "string"}}}},
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/response.py b/openapi_schema_pydantic/v3/v3_0_3/response.py
index 1f7042c..4a19135 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/response.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/response.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional, Union
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .header import Header
from .link import Link
@@ -14,7 +14,7 @@ class Response(BaseModel):
static `links` to operations based on the response.
"""
- description: str = ...
+ description: str
"""
**REQUIRED**. A short description of the response.
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
@@ -43,9 +43,9 @@ class Response(BaseModel):
following the naming constraints of the names for [Component Objects](#componentsObject).
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"description": "A complex object array response",
@@ -76,4 +76,5 @@ class Config:
},
{"description": "object created"},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/schema.py b/openapi_schema_pydantic/v3/v3_0_3/schema.py
index d29bd36..5428efe 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/schema.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/schema.py
@@ -1,6 +1,6 @@
from typing import Any, Dict, List, Optional, Union
-from pydantic import BaseModel, Extra, Field
+from pydantic import BaseModel, ConfigDict, Field
from .discriminator import Discriminator
from .external_documentation import ExternalDocumentation
from .reference import Reference
@@ -180,7 +180,7 @@ class Schema(BaseModel):
value of 0.
"""
- required: Optional[List[str]] = Field(default=None, min_items=1)
+ required: Optional[List[str]] = Field(default=None, min_length=1)
"""
The value of this keyword MUST be an array. This array MUST have at
least one element. Elements of this array MUST be strings, and MUST
@@ -190,7 +190,7 @@ class Schema(BaseModel):
contains all elements in this keyword's array value.
"""
- enum: Optional[List[Any]] = Field(default=None, min_items=1)
+ enum: Optional[List[Any]] = Field(default=None, min_length=1)
"""
The value of this keyword MUST be an array. This array SHOULD have
at least one element. Elements in the array SHOULD be unique.
@@ -470,10 +470,10 @@ class Schema(BaseModel):
Default value is `false`.
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{"type": "string", "format": "email"},
{
@@ -553,4 +553,5 @@ class Config:
],
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/security_scheme.py b/openapi_schema_pydantic/v3/v3_0_3/security_scheme.py
index df54c64..87962e1 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/security_scheme.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/security_scheme.py
@@ -1,6 +1,6 @@
from typing import Optional, Union
-from pydantic import AnyUrl, BaseModel, Extra, Field
+from pydantic import AnyUrl, BaseModel, ConfigDict, Field
from .oauth_flows import OAuthFlows
@@ -15,7 +15,7 @@ class SecurityScheme(BaseModel):
and [OpenID Connect Discovery](https://tools.ietf.org/html/draft-ietf-oauth-discovery-06).
"""
- type: str = ...
+ type: str
"""
**REQUIRED**. The type of the security scheme.
Valid values are `"apiKey"`, `"http"`, `"oauth2"`, `"openIdConnect"`.
@@ -65,10 +65,10 @@ class SecurityScheme(BaseModel):
This MUST be in the form of a URL.
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{"type": "http", "scheme": "basic"},
{"type": "apiKey", "name": "api_key", "in": "header"},
@@ -85,4 +85,5 @@ class Config:
{"type": "openIdConnect", "openIdConnectUrl": "https://example.com/openIdConnect"},
{"type": "openIdConnect", "openIdConnectUrl": "openIdConnect"}, # #5: allow relative path
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/server.py b/openapi_schema_pydantic/v3/v3_0_3/server.py
index aeeefd5..1e3371e 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/server.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/server.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .server_variable import ServerVariable
@@ -8,7 +8,7 @@
class Server(BaseModel):
"""An object representing a Server."""
- url: str = ...
+ url: str
"""
**REQUIRED**. A URL to the target host.
@@ -30,9 +30,9 @@ class Server(BaseModel):
The value is used for substitution in the server's URL template.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"url": "https://development.gigantic-server.com/v1", "description": "Development server"},
{
@@ -49,4 +49,5 @@ class Config:
},
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/server_variable.py b/openapi_schema_pydantic/v3/v3_0_3/server_variable.py
index 8808e4a..2e83a59 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/server_variable.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/server_variable.py
@@ -1,6 +1,6 @@
from typing import List, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
class ServerVariable(BaseModel):
@@ -12,7 +12,7 @@ class ServerVariable(BaseModel):
The array SHOULD NOT be empty.
"""
- default: str = ...
+ default: str
"""
**REQUIRED**. The default value to use for substitution,
which SHALL be sent if an alternate value is _not_ supplied.
@@ -27,5 +27,4 @@ class ServerVariable(BaseModel):
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
"""
- class Config:
- extra = Extra.ignore
+ model_config = ConfigDict(extra="ignore")
diff --git a/openapi_schema_pydantic/v3/v3_0_3/tag.py b/openapi_schema_pydantic/v3/v3_0_3/tag.py
index 24e926a..d1e439f 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/tag.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/tag.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .external_documentation import ExternalDocumentation
@@ -11,7 +11,7 @@ class Tag(BaseModel):
It is not mandatory to have a Tag Object per tag defined in the Operation Object instances.
"""
- name: str = ...
+ name: str
"""
**REQUIRED**. The name of the tag.
"""
@@ -27,6 +27,7 @@ class Tag(BaseModel):
Additional external documentation for this tag.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {"examples": [{"name": "pet", "description": "Pets operations"}]}
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={"examples": [{"name": "pet", "description": "Pets operations"}]},
+ )
diff --git a/openapi_schema_pydantic/v3/v3_0_3/util.py b/openapi_schema_pydantic/v3/v3_0_3/util.py
index cb57969..d59df65 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/util.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/util.py
@@ -1,27 +1,34 @@
import logging
-from typing import Any, List, Set, Type, TypeVar
+from typing import Any, List, Set, Type
from pydantic import BaseModel
-from pydantic.schema import schema
+from pydantic.json_schema import models_json_schema, JsonSchemaMode
from . import Components, OpenAPI, Reference, Schema
logger = logging.getLogger(__name__)
-PydanticType = TypeVar("PydanticType", bound=BaseModel)
+PydanticType = BaseModel
ref_prefix = "#/components/schemas/"
+ref_template = ref_prefix + "{model}"
class PydanticSchema(Schema):
"""Special `Schema` class to indicate a reference from pydantic class"""
- schema_class: Type[PydanticType] = ...
+ schema_class: Type[BaseModel]
"""the class that is used for generate the schema"""
+def get_mode(cls: Type[BaseModel], default: JsonSchemaMode = "validation") -> JsonSchemaMode:
+ if not hasattr(cls, "model_config"):
+ return default
+ return cls.model_config.get("json_schema_mode", default)
+
+
def construct_open_api_with_schema_class(
open_api: OpenAPI,
- schema_classes: List[Type[PydanticType]] = None,
+ schema_classes: List[Type[BaseModel]] | None = None,
scan_for_pydantic_schema_reference: bool = True,
by_alias: bool = True,
) -> OpenAPI:
@@ -36,7 +43,7 @@ def construct_open_api_with_schema_class(
:return: new OpenAPI object with "#/components/schemas" values updated.
If there is no update in "#/components/schemas" values, the original `open_api` will be returned.
"""
- new_open_api: OpenAPI = open_api.copy(deep=True)
+ new_open_api: OpenAPI = open_api.model_copy(deep=True)
if scan_for_pydantic_schema_reference:
extracted_schema_classes = _handle_pydantic_schema(new_open_api)
if schema_classes:
@@ -51,27 +58,33 @@ def construct_open_api_with_schema_class(
logger.debug(f"schema_classes{schema_classes}")
# update new_open_api with new #/components/schemas
- schema_definitions = schema(schema_classes, by_alias=by_alias, ref_prefix=ref_prefix)
+ # Note: the mode (validation or serialization) affects
+ # optional and computed fields.
+ key_map, schema_definitions = models_json_schema(
+ [(c, get_mode(c)) for c in schema_classes],
+ by_alias=by_alias,
+ ref_template=ref_template,
+ )
if not new_open_api.components:
new_open_api.components = Components()
if new_open_api.components.schemas:
for existing_key in new_open_api.components.schemas:
- if existing_key in schema_definitions.get("definitions"):
+ if existing_key in schema_definitions["$defs"]:
logger.warning(
f'"{existing_key}" already exists in {ref_prefix}. '
f'The value of "{ref_prefix}{existing_key}" will be overwritten.'
)
new_open_api.components.schemas.update(
- {key: Schema.parse_obj(schema_dict) for key, schema_dict in schema_definitions.get("definitions").items()}
+ {key: Schema.model_validate(schema_dict) for key, schema_dict in schema_definitions["$defs"].items()}
)
else:
new_open_api.components.schemas = {
- key: Schema.parse_obj(schema_dict) for key, schema_dict in schema_definitions.get("definitions").items()
+ key: Schema.model_validate(schema_dict) for key, schema_dict in schema_definitions["$defs"].items()
}
return new_open_api
-def _handle_pydantic_schema(open_api: OpenAPI) -> List[Type[PydanticType]]:
+def _handle_pydantic_schema(open_api: OpenAPI) -> List[Type[BaseModel]]:
"""
This function traverses the `OpenAPI` object and
@@ -84,11 +97,11 @@ def _handle_pydantic_schema(open_api: OpenAPI) -> List[Type[PydanticType]]:
:return: a list of schema classes extracted from `PydanticSchema` objects
"""
- pydantic_types: Set[Type[PydanticType]] = set()
+ pydantic_types: Set[Type[BaseModel]] = set()
def _traverse(obj: Any):
if isinstance(obj, BaseModel):
- fields = obj.__fields_set__
+ fields = obj.model_fields_set
for field in fields:
child_obj = obj.__getattribute__(field)
if isinstance(child_obj, PydanticSchema):
diff --git a/openapi_schema_pydantic/v3/v3_0_3/xml.py b/openapi_schema_pydantic/v3/v3_0_3/xml.py
index e451bee..139f84d 100644
--- a/openapi_schema_pydantic/v3/v3_0_3/xml.py
+++ b/openapi_schema_pydantic/v3/v3_0_3/xml.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
class XML(BaseModel):
@@ -47,11 +47,12 @@ class XML(BaseModel):
The definition takes effect only when defined alongside `type` being `array` (outside the `items`).
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"namespace": "http://example.com/schema/sample", "prefix": "sample"},
{"name": "aliens", "wrapped": True},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/README.md b/openapi_schema_pydantic/v3/v3_1_0/README.md
index d8a3718..a46e2ad 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/README.md
+++ b/openapi_schema_pydantic/v3/v3_1_0/README.md
@@ -22,7 +22,7 @@ the following fields are used with [alias](https://pydantic-docs.helpmanual.io/u
> The "in" field in Header object is actually a constant (`{"in": "header"}`).
> For convenience of object creation, the classes mentioned in above
-> has configured `allow_population_by_field_name=True`.
+> has configured `populate_by_name=True`.
>
> Reference: [Pydantic's Model Config](https://pydantic-docs.helpmanual.io/usage/model_config/)
diff --git a/openapi_schema_pydantic/v3/v3_1_0/__init__.py b/openapi_schema_pydantic/v3/v3_1_0/__init__.py
index dd938d1..9fd5b19 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/__init__.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/__init__.py
@@ -38,5 +38,7 @@
from .security_requirement import SecurityRequirement
# resolve forward references
-Encoding.update_forward_refs(Header=Header)
-Schema.update_forward_refs()
+Encoding.model_rebuild()
+OpenAPI.model_rebuild()
+Components.model_rebuild()
+Operation.model_rebuild()
diff --git a/openapi_schema_pydantic/v3/v3_1_0/components.py b/openapi_schema_pydantic/v3/v3_1_0/components.py
index 899d333..37f65c1 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/components.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/components.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional, Union
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .callback import Callback
from .example import Example
@@ -52,9 +52,9 @@ class Components(BaseModel):
pathItems: Optional[Dict[str, Union[PathItem, Reference]]] = None
"""An object to hold reusable [Path Item Object](#pathItemObject)."""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"schemas": {
@@ -115,4 +115,5 @@ class Config:
},
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/contact.py b/openapi_schema_pydantic/v3/v3_1_0/contact.py
index 26be261..ab0e0a5 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/contact.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/contact.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import AnyUrl, BaseModel, Extra
+from pydantic import AnyUrl, BaseModel, ConfigDict
class Contact(BaseModel):
@@ -25,10 +25,11 @@ class Contact(BaseModel):
MUST be in the form of an email address.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"name": "API Support", "url": "http://www.example.com/support", "email": "support@example.com"}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/discriminator.py b/openapi_schema_pydantic/v3/v3_1_0/discriminator.py
index 699d4ea..666456a 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/discriminator.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/discriminator.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
class Discriminator(BaseModel):
@@ -14,7 +14,7 @@ class Discriminator(BaseModel):
When using the discriminator, _inline_ schemas will not be considered.
"""
- propertyName: str = ...
+ propertyName: str
"""
**REQUIRED**. The name of the property in the payload that will hold the discriminator value.
"""
@@ -24,9 +24,9 @@ class Discriminator(BaseModel):
An object to hold mappings between payload values and schema names or references.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"propertyName": "petType",
@@ -36,4 +36,5 @@ class Config:
},
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/encoding.py b/openapi_schema_pydantic/v3/v3_1_0/encoding.py
index 7dc6220..73d399e 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/encoding.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/encoding.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional, Union
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .reference import Reference
@@ -67,9 +67,9 @@ class Encoding(BaseModel):
then the value of [`contentType`](#encodingContentType) (implicit or explicit) SHALL be ignored.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"contentType": "image/png, image/jpeg",
@@ -81,4 +81,5 @@ class Config:
},
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/example.py b/openapi_schema_pydantic/v3/v3_1_0/example.py
index a8c8bce..db3b7e9 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/example.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/example.py
@@ -1,10 +1,9 @@
from typing import Any, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
class Example(BaseModel):
-
summary: Optional[str] = None
"""
Short description for the example.
@@ -33,9 +32,9 @@ class Example(BaseModel):
See the rules for resolving [Relative References](#relativeReferencesURI).
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"summary": "A foo example", "value": {"foo": "bar"}},
{
@@ -44,4 +43,5 @@ class Config:
},
{"summary": "This is a text example", "externalValue": "http://foo.bar/examples/address-example.txt"},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/external_documentation.py b/openapi_schema_pydantic/v3/v3_1_0/external_documentation.py
index 0ce2747..5dcbdb0 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/external_documentation.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/external_documentation.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import AnyUrl, BaseModel, Extra
+from pydantic import AnyUrl, BaseModel, ConfigDict
class ExternalDocumentation(BaseModel):
@@ -12,12 +12,13 @@ class ExternalDocumentation(BaseModel):
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
"""
- url: AnyUrl = ...
+ url: AnyUrl
"""
**REQUIRED**. The URL for the target documentation.
Value MUST be in the form of a URL.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {"examples": [{"description": "Find more info here", "url": "https://example.com"}]}
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={"examples": [{"description": "Find more info here", "url": "https://example.com"}]},
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/header.py b/openapi_schema_pydantic/v3/v3_1_0/header.py
index c413cdc..1e078ee 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/header.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/header.py
@@ -1,4 +1,5 @@
-from pydantic import Extra, Field
+from pydantic import ConfigDict, Field
+from typing import Literal
from .parameter import Parameter
@@ -13,14 +14,15 @@ class Header(Parameter):
(for example, [`style`](#parameterStyle)).
"""
- name = Field(default="", const=True)
- param_in = Field(default="header", const=True, alias="in")
+ name: Literal[""] = Field(default="")
+ param_in: Literal["header"] = Field(default="header", alias="in")
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{"description": "The number of allowed requests in the current period", "schema": {"type": "integer"}}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/info.py b/openapi_schema_pydantic/v3/v3_1_0/info.py
index 5e8955b..9122a5f 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/info.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/info.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import AnyUrl, BaseModel, Extra
+from pydantic import AnyUrl, BaseModel, ConfigDict
from .contact import Contact
from .license import License
@@ -13,7 +13,7 @@ class Info(BaseModel):
and MAY be presented in editing or documentation generation tools for convenience.
"""
- title: str = ...
+ title: str
"""
**REQUIRED**. The title of the API.
"""
@@ -45,15 +45,15 @@ class Info(BaseModel):
The license information for the exposed API.
"""
- version: str = ...
+ version: str
"""
**REQUIRED**. The version of the OpenAPI document
(which is distinct from the [OpenAPI Specification version](#oasVersion) or the API implementation version).
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"title": "Sample Pet Store App",
@@ -69,4 +69,5 @@ class Config:
"version": "1.0.1",
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/license.py b/openapi_schema_pydantic/v3/v3_1_0/license.py
index f0ba1bb..8599cab 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/license.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/license.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import AnyUrl, BaseModel, Extra
+from pydantic import AnyUrl, BaseModel, ConfigDict
class License(BaseModel):
@@ -8,7 +8,7 @@ class License(BaseModel):
License information for the exposed API.
"""
- name: str = ...
+ name: str
"""
**REQUIRED**. The license name used for the API.
"""
@@ -26,11 +26,12 @@ class License(BaseModel):
The `url` field is mutually exclusive of the `identifier` field.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"name": "Apache 2.0", "identifier": "Apache-2.0"},
{"name": "Apache 2.0", "url": "https://www.apache.org/licenses/LICENSE-2.0.html"},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/link.py b/openapi_schema_pydantic/v3/v3_1_0/link.py
index df82633..4f11517 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/link.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/link.py
@@ -1,6 +1,6 @@
from typing import Any, Dict, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .server import Server
@@ -62,9 +62,9 @@ class Link(BaseModel):
A server object to be used by the target operation.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"operationId": "getUserAddressByUUID", "parameters": {"userUuid": "$response.body#/uuid"}},
{
@@ -72,4 +72,5 @@ class Config:
"parameters": {"username": "$response.body#/username"},
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/media_type.py b/openapi_schema_pydantic/v3/v3_1_0/media_type.py
index 07b3986..9c09589 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/media_type.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/media_type.py
@@ -1,6 +1,6 @@
from typing import Any, Dict, Optional, Union
-from pydantic import BaseModel, Extra, Field
+from pydantic import BaseModel, ConfigDict, Field
from .encoding import Encoding
from .example import Example
@@ -48,10 +48,10 @@ class MediaType(BaseModel):
when the media type is `multipart` or `application/x-www-form-urlencoded`.
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{
"schema": {"$ref": "#/components/schemas/Pet"},
@@ -80,4 +80,5 @@ class Config:
},
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/oauth_flow.py b/openapi_schema_pydantic/v3/v3_1_0/oauth_flow.py
index 57ded57..c53e035 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/oauth_flow.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/oauth_flow.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional, Union
-from pydantic import AnyUrl, BaseModel, Extra
+from pydantic import AnyUrl, BaseModel, ConfigDict
class OAuthFlow(BaseModel):
@@ -38,9 +38,9 @@ class OAuthFlow(BaseModel):
The map MAY be empty.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"authorizationUrl": "https://example.com/api/oauth/dialog",
@@ -58,4 +58,5 @@ class Config:
"scopes": {"write:pets": "modify pets in your account", "read:pets": "read your pets"},
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/oauth_flows.py b/openapi_schema_pydantic/v3/v3_1_0/oauth_flows.py
index c60a8ff..edc48ab 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/oauth_flows.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/oauth_flows.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .oauth_flow import OAuthFlow
@@ -34,5 +34,4 @@ class OAuthFlows(BaseModel):
Previously called `accessCode` in OpenAPI 2.0.
"""
- class Config:
- extra = Extra.ignore
+ model_config = ConfigDict(extra="ignore")
diff --git a/openapi_schema_pydantic/v3/v3_1_0/open_api.py b/openapi_schema_pydantic/v3/v3_1_0/open_api.py
index dc1e534..7049ca7 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/open_api.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/open_api.py
@@ -1,6 +1,6 @@
from typing import Dict, List, Optional, Union
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .components import Components
from .external_documentation import ExternalDocumentation
@@ -24,7 +24,7 @@ class OpenAPI(BaseModel):
This is *not* related to the API [`info.version`](#infoVersion) string.
"""
- info: Info = ...
+ info: Info
"""
**REQUIRED**. Provides metadata about the API. The metadata MAY be used by tooling as required.
"""
@@ -86,5 +86,4 @@ class OpenAPI(BaseModel):
Additional external documentation.
"""
- class Config:
- extra = Extra.ignore
+ model_config = ConfigDict(extra="ignore")
diff --git a/openapi_schema_pydantic/v3/v3_1_0/operation.py b/openapi_schema_pydantic/v3/v3_1_0/operation.py
index 7b5cde3..00a9278 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/operation.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/operation.py
@@ -1,6 +1,6 @@
from typing import Dict, List, Optional, Union
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .callback import Callback
from .external_documentation import ExternalDocumentation
@@ -106,9 +106,9 @@ class Operation(BaseModel):
it will be overridden by this value.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"tags": ["pet"],
@@ -150,4 +150,5 @@ class Config:
"security": [{"petstore_auth": ["write:pets", "read:pets"]}],
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/parameter.py b/openapi_schema_pydantic/v3/v3_1_0/parameter.py
index e4f69c4..851c68b 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/parameter.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/parameter.py
@@ -1,6 +1,6 @@
from typing import Any, Dict, Optional, Union
-from pydantic import BaseModel, Field, Extra
+from pydantic import BaseModel, Field, ConfigDict
from .example import Example
from .media_type import MediaType
@@ -17,7 +17,7 @@ class Parameter(BaseModel):
"""Fixed Fields"""
- name: str = ...
+ name: str
"""
**REQUIRED**. The name of the parameter.
Parameter names are *case sensitive*.
@@ -140,10 +140,10 @@ class Parameter(BaseModel):
The map MUST only contain one entry.
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{
"name": "token",
@@ -189,4 +189,5 @@ class Config:
},
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/path_item.py b/openapi_schema_pydantic/v3/v3_1_0/path_item.py
index 5625617..81f0512 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/path_item.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/path_item.py
@@ -1,6 +1,6 @@
from typing import List, Optional, Union
-from pydantic import BaseModel, Extra, Field
+from pydantic import BaseModel, ConfigDict, Field
from .operation import Operation
from .parameter import Parameter
@@ -92,10 +92,10 @@ class PathItem(BaseModel):
[OpenAPI Object's components/parameters](#componentsParameters).
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{
"get": {
@@ -127,4 +127,5 @@ class Config:
],
}
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/reference.py b/openapi_schema_pydantic/v3/v3_1_0/reference.py
index 76e16c3..c3cfddb 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/reference.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/reference.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import BaseModel, Extra, Field
+from pydantic import BaseModel, ConfigDict, Field
class Reference(BaseModel):
@@ -29,9 +29,10 @@ class Reference(BaseModel):
If the referenced object-type does not allow a `description` field, then this field has no effect.
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [{"$ref": "#/components/schemas/Pet"}, {"$ref": "Pet.json"}, {"$ref": "definitions.json#/Pet"}]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/request_body.py b/openapi_schema_pydantic/v3/v3_1_0/request_body.py
index 1bf58ce..6d79e5f 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/request_body.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/request_body.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .media_type import MediaType
@@ -16,7 +16,7 @@ class RequestBody(BaseModel):
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
"""
- content: Dict[str, MediaType] = ...
+ content: Dict[str, MediaType]
"""
**REQUIRED**. The content of the request body.
The key is a media type or [media type range](https://tools.ietf.org/html/rfc7231#appendix-D)
@@ -30,9 +30,9 @@ class RequestBody(BaseModel):
Determines if the request body is required in the request. Defaults to `false`.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"description": "user to add to the system",
@@ -78,4 +78,5 @@ class Config:
"content": {"text/plain": {"schema": {"type": "array", "items": {"type": "string"}}}},
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/response.py b/openapi_schema_pydantic/v3/v3_1_0/response.py
index 1f7042c..4a19135 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/response.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/response.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional, Union
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .header import Header
from .link import Link
@@ -14,7 +14,7 @@ class Response(BaseModel):
static `links` to operations based on the response.
"""
- description: str = ...
+ description: str
"""
**REQUIRED**. A short description of the response.
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
@@ -43,9 +43,9 @@ class Response(BaseModel):
following the naming constraints of the names for [Component Objects](#componentsObject).
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{
"description": "A complex object array response",
@@ -76,4 +76,5 @@ class Config:
},
{"description": "object created"},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/schema.py b/openapi_schema_pydantic/v3/v3_1_0/schema.py
index 1739f0e..7e104b0 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/schema.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/schema.py
@@ -1,6 +1,6 @@
from typing import Any, Dict, List, Optional, Union
-from pydantic import BaseModel, Extra, Field
+from pydantic import BaseModel, ConfigDict, Field
from .discriminator import Discriminator
from .external_documentation import ExternalDocumentation
from .reference import Reference
@@ -372,7 +372,7 @@ class Schema(BaseModel):
sets listed for this keyword.
"""
- enum: Optional[List[Any]] = Field(default=None, min_items=1)
+ enum: Optional[List[Any]] = Field(default=None, min_length=1)
"""
The value of this keyword MUST be an array. This array SHOULD have
at least one element. Elements in the array SHOULD be unique.
@@ -830,10 +830,10 @@ class Schema(BaseModel):
Use of example is discouraged, and later versions of this specification may remove it.
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{"type": "string", "format": "email"},
{
@@ -913,4 +913,5 @@ class Config:
],
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/security_scheme.py b/openapi_schema_pydantic/v3/v3_1_0/security_scheme.py
index e1761d0..0449b19 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/security_scheme.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/security_scheme.py
@@ -1,6 +1,6 @@
from typing import Optional, Union
-from pydantic import AnyUrl, BaseModel, Extra, Field
+from pydantic import AnyUrl, BaseModel, ConfigDict, Field
from .oauth_flows import OAuthFlows
@@ -21,7 +21,7 @@ class SecurityScheme(BaseModel):
Recommended for most use case is Authorization Code Grant flow with PKCE.
"""
- type: str = ...
+ type: str
"""
**REQUIRED**. The type of the security scheme.
Valid values are `"apiKey"`, `"http"`, "mutualTLS", `"oauth2"`, `"openIdConnect"`.
@@ -71,10 +71,10 @@ class SecurityScheme(BaseModel):
This MUST be in the form of a URL. The OpenID Connect standard requires the use of TLS.
"""
- class Config:
- extra = Extra.ignore
- allow_population_by_field_name = True
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ populate_by_name=True,
+ json_schema_extra={
"examples": [
{"type": "http", "scheme": "basic"},
{"type": "apiKey", "name": "api_key", "in": "header"},
@@ -91,4 +91,5 @@ class Config:
{"type": "openIdConnect", "openIdConnectUrl": "https://example.com/openIdConnect"},
{"type": "openIdConnect", "openIdConnectUrl": "openIdConnect"}, # issue #5: allow relative path
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/server.py b/openapi_schema_pydantic/v3/v3_1_0/server.py
index aeeefd5..1e3371e 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/server.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/server.py
@@ -1,6 +1,6 @@
from typing import Dict, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .server_variable import ServerVariable
@@ -8,7 +8,7 @@
class Server(BaseModel):
"""An object representing a Server."""
- url: str = ...
+ url: str
"""
**REQUIRED**. A URL to the target host.
@@ -30,9 +30,9 @@ class Server(BaseModel):
The value is used for substitution in the server's URL template.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"url": "https://development.gigantic-server.com/v1", "description": "Development server"},
{
@@ -49,4 +49,5 @@ class Config:
},
},
]
- }
+ },
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/server_variable.py b/openapi_schema_pydantic/v3/v3_1_0/server_variable.py
index ad3d989..ff62784 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/server_variable.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/server_variable.py
@@ -1,6 +1,6 @@
from typing import List, Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
class ServerVariable(BaseModel):
@@ -12,7 +12,7 @@ class ServerVariable(BaseModel):
The array SHOULD NOT be empty.
"""
- default: str = ...
+ default: str
"""
**REQUIRED**. The default value to use for substitution,
which SHALL be sent if an alternate value is _not_ supplied.
@@ -27,5 +27,4 @@ class ServerVariable(BaseModel):
[CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation.
"""
- class Config:
- extra = Extra.ignore
+ model_config = ConfigDict(extra="ignore")
diff --git a/openapi_schema_pydantic/v3/v3_1_0/tag.py b/openapi_schema_pydantic/v3/v3_1_0/tag.py
index 24e926a..d1e439f 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/tag.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/tag.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
from .external_documentation import ExternalDocumentation
@@ -11,7 +11,7 @@ class Tag(BaseModel):
It is not mandatory to have a Tag Object per tag defined in the Operation Object instances.
"""
- name: str = ...
+ name: str
"""
**REQUIRED**. The name of the tag.
"""
@@ -27,6 +27,7 @@ class Tag(BaseModel):
Additional external documentation for this tag.
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {"examples": [{"name": "pet", "description": "Pets operations"}]}
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={"examples": [{"name": "pet", "description": "Pets operations"}]},
+ )
diff --git a/openapi_schema_pydantic/v3/v3_1_0/xml.py b/openapi_schema_pydantic/v3/v3_1_0/xml.py
index c9ae224..1d5829f 100644
--- a/openapi_schema_pydantic/v3/v3_1_0/xml.py
+++ b/openapi_schema_pydantic/v3/v3_1_0/xml.py
@@ -1,6 +1,6 @@
from typing import Optional
-from pydantic import BaseModel, Extra
+from pydantic import BaseModel, ConfigDict
class XML(BaseModel):
@@ -47,9 +47,9 @@ class XML(BaseModel):
The definition takes effect only when defined alongside `type` being `array` (outside the `items`).
"""
- class Config:
- extra = Extra.ignore
- schema_extra = {
+ model_config = ConfigDict(
+ extra="ignore",
+ json_schema_extra={
"examples": [
{"name": "animal"},
{"attribute": True},
@@ -57,4 +57,5 @@ class Config:
{"namespace": "http://example.com/schema/sample", "prefix": "sample"},
{"name": "aliens", "wrapped": True},
]
- }
+ },
+ )
diff --git a/setup.py b/setup.py
index 19d0753..922ae57 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@
setuptools.setup(
name="openapi-schema-pydantic",
- version="1.2.4",
+ version="2.0",
author="Kuimono",
description="OpenAPI (v3) specification schema as pydantic class",
long_description=long_description,
@@ -13,12 +13,12 @@
url="https://github.com/kuimono/openapi-schema-pydantic",
packages=setuptools.find_packages(exclude=["tests"]),
package_data={"openapi_schema_pydantic": ["py.typed"]},
- install_requires=["pydantic>=1.8.2"],
+ install_requires=["pydantic>=2.0"],
tests_require=["pytest"],
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
],
- python_requires=">=3.6.1",
+ python_requires=">=3.8",
)
diff --git a/tests/schema_classes/test_schema.py b/tests/schema_classes/test_schema.py
index 0fd8b8e..f5b812d 100644
--- a/tests/schema_classes/test_schema.py
+++ b/tests/schema_classes/test_schema.py
@@ -1,24 +1,24 @@
import logging
-from pydantic import BaseModel, Extra
-from pydantic.schema import schema
+from pydantic import BaseModel, ConfigDict
+from pydantic.json_schema import models_json_schema
from openapi_schema_pydantic import Schema, Reference
def test_schema():
- schema = Schema.parse_obj(
+ schema = Schema.model_validate(
{
"title": "reference list",
"description": "schema for list of reference type",
- "allOf": [{"$ref": "#/definitions/TestType"}],
+ "allOf": [{"$ref": "#/components/schemas/TestType"}],
}
)
logging.debug(f"schema.allOf={schema.allOf}")
assert schema.allOf
assert isinstance(schema.allOf, list)
assert isinstance(schema.allOf[0], Reference)
- assert schema.allOf[0].ref == "#/definitions/TestType"
+ assert schema.allOf[0].ref == "#/components/schemas/TestType"
def test_issue_4():
@@ -27,12 +27,11 @@ def test_issue_4():
class TestModel(BaseModel):
test_field: str
- class Config:
- extra = Extra.forbid
+ model_config = ConfigDict(extra="forbid")
- schema_definition = schema([TestModel])
+ _key_map, schema_definition = models_json_schema([(TestModel, "validation")])
assert schema_definition == {
- "definitions": {
+ "$defs": {
"TestModel": {
"title": "TestModel",
"type": "object",
@@ -44,5 +43,5 @@ class Config:
}
# allow "additionalProperties" to have boolean value
- result = Schema.parse_obj(schema_definition["definitions"]["TestModel"])
+ result = Schema.model_validate(schema_definition["$defs"]["TestModel"])
assert result.additionalProperties is False
diff --git a/tests/schema_classes/test_security_scheme.py b/tests/schema_classes/test_security_scheme.py
index c768dd9..6ad22ba 100644
--- a/tests/schema_classes/test_security_scheme.py
+++ b/tests/schema_classes/test_security_scheme.py
@@ -8,18 +8,18 @@ def test_security_scheme_issue_5():
security_scheme_1 = SecurityScheme(type="openIdConnect", openIdConnectUrl="https://example.com/openIdConnect")
assert isinstance(security_scheme_1.openIdConnectUrl, AnyUrl) or isinstance(security_scheme_1.openIdConnectUrl, str)
- assert security_scheme_1.json(by_alias=True, exclude_none=True) == (
- '{"type": "openIdConnect", "openIdConnectUrl": "https://example.com/openIdConnect"}'
+ assert security_scheme_1.model_dump_json(by_alias=True, exclude_none=True) == (
+ '{"type":"openIdConnect","openIdConnectUrl":"https://example.com/openIdConnect"}'
)
security_scheme_2 = SecurityScheme(type="openIdConnect", openIdConnectUrl="/openIdConnect")
assert isinstance(security_scheme_2.openIdConnectUrl, str)
- assert security_scheme_2.json(by_alias=True, exclude_none=True) == (
- '{"type": "openIdConnect", "openIdConnectUrl": "/openIdConnect"}'
+ assert security_scheme_2.model_dump_json(by_alias=True, exclude_none=True) == (
+ '{"type":"openIdConnect","openIdConnectUrl":"/openIdConnect"}'
)
security_scheme_3 = SecurityScheme(type="openIdConnect", openIdConnectUrl="openIdConnect")
assert isinstance(security_scheme_3.openIdConnectUrl, str)
- assert security_scheme_3.json(by_alias=True, exclude_none=True) == (
- '{"type": "openIdConnect", "openIdConnectUrl": "openIdConnect"}'
+ assert security_scheme_3.model_dump_json(by_alias=True, exclude_none=True) == (
+ '{"type":"openIdConnect","openIdConnectUrl":"openIdConnect"}'
)
diff --git a/tests/test_alias.py b/tests/test_alias.py
index 2cd0b0f..c9a6de9 100644
--- a/tests/test_alias.py
+++ b/tests/test_alias.py
@@ -3,50 +3,50 @@
def test_header_alias():
header_1 = Header(param_in="header")
- header_2 = Header.parse_obj({"param_in": "header"})
- header_3 = Header.parse_obj({"in": "header"})
+ header_2 = Header.model_validate({"param_in": "header"})
+ header_3 = Header.model_validate({"in": "header"})
assert header_1 == header_2 == header_3
def test_media_type_alias():
media_type_1 = MediaType(media_type_schema=Schema())
media_type_2 = MediaType(schema=Schema())
- media_type_3 = MediaType.parse_obj({"media_type_schema": Schema()})
- media_type_4 = MediaType.parse_obj({"schema": Schema()})
+ media_type_3 = MediaType.model_validate({"media_type_schema": Schema()})
+ media_type_4 = MediaType.model_validate({"schema": Schema()})
assert media_type_1 == media_type_2 == media_type_3 == media_type_4
def test_parameter_alias():
parameter_1 = Parameter(name="test", param_in="path", param_schema=Schema())
parameter_2 = Parameter(name="test", param_in="path", schema=Schema())
- parameter_3 = Parameter.parse_obj({"name": "test", "param_in": "path", "param_schema": Schema()})
- parameter_4 = Parameter.parse_obj({"name": "test", "in": "path", "schema": Schema()})
+ parameter_3 = Parameter.model_validate({"name": "test", "param_in": "path", "param_schema": Schema()})
+ parameter_4 = Parameter.model_validate({"name": "test", "in": "path", "schema": Schema()})
assert parameter_1 == parameter_2 == parameter_3 == parameter_4
def test_path_item_alias():
path_item_1 = PathItem(ref="#/dummy")
- path_item_2 = PathItem.parse_obj({"ref": "#/dummy"})
- path_item_3 = PathItem.parse_obj({"$ref": "#/dummy"})
+ path_item_2 = PathItem.model_validate({"ref": "#/dummy"})
+ path_item_3 = PathItem.model_validate({"$ref": "#/dummy"})
assert path_item_1 == path_item_2 == path_item_3
def test_reference_alias():
reference_1 = Reference(ref="#/dummy")
- reference_2 = Reference.parse_obj({"ref": "#/dummy"})
- reference_3 = Reference.parse_obj({"$ref": "#/dummy"})
+ reference_2 = Reference.model_validate({"ref": "#/dummy"})
+ reference_3 = Reference.model_validate({"$ref": "#/dummy"})
assert reference_1 == reference_2 == reference_3
def test_security_scheme():
security_scheme_1 = SecurityScheme(type="apiKey", security_scheme_in="header")
- security_scheme_2 = SecurityScheme.parse_obj({"type": "apiKey", "security_scheme_in": "header"})
- security_scheme_3 = SecurityScheme.parse_obj({"type": "apiKey", "in": "header"})
+ security_scheme_2 = SecurityScheme.model_validate({"type": "apiKey", "security_scheme_in": "header"})
+ security_scheme_3 = SecurityScheme.model_validate({"type": "apiKey", "in": "header"})
assert security_scheme_1 == security_scheme_2 == security_scheme_3
def test_schema():
schema_1 = Schema(schema_not=Schema(), schema_format="email")
- schema_2 = Schema.parse_obj({"schema_not": Schema(), "schema_format": "email"})
- schema_3 = Schema.parse_obj({"not": Schema(), "format": "email"})
+ schema_2 = Schema.model_validate({"schema_not": Schema(), "schema_format": "email"})
+ schema_3 = Schema.model_validate({"not": Schema(), "format": "email"})
assert schema_1 == schema_2 == schema_3
diff --git a/tests/test_config_example.py b/tests/test_config_example.py
index 29394d4..3b3d9e3 100644
--- a/tests/test_config_example.py
+++ b/tests/test_config_example.py
@@ -70,8 +70,11 @@ def test_config_example():
def _assert_config_examples(schema_type):
- if getattr(schema_type, "Config", None) and getattr(schema_type.Config, "schema_extra", None):
- examples = schema_type.Config.schema_extra.get("examples")
+ if not hasattr(schema_type, "model_config"):
+ return
+ extra = schema_type.model_config.get("json_schema_extra")
+ if extra is not None:
+ examples = extra.get("examples")
for example_dict in examples:
- obj = schema_type(**example_dict)
- assert obj.__fields_set__
+ obj = schema_type.model_validate(example_dict)
+ assert obj.model_fields_set
diff --git a/tests/test_example.py b/tests/test_example.py
index 1a2e9a0..03f0d4a 100644
--- a/tests/test_example.py
+++ b/tests/test_example.py
@@ -6,7 +6,7 @@
def test_readme_example():
open_api_1 = readme_example_1()
assert open_api_1
- open_api_json_1 = open_api_1.json(by_alias=True, exclude_none=True, indent=2)
+ open_api_json_1 = open_api_1.model_dump_json(by_alias=True, exclude_none=True, indent=2)
logging.debug(open_api_json_1)
assert open_api_json_1
@@ -30,7 +30,7 @@ def readme_example_1() -> OpenAPI:
def readme_example_2() -> OpenAPI:
"""Construct OpenAPI from raw data object"""
- return OpenAPI.parse_obj(
+ return OpenAPI.model_validate(
{
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {"/ping": {"get": {"responses": {"200": {"description": "pong"}}}}},
@@ -40,7 +40,7 @@ def readme_example_2() -> OpenAPI:
def readme_example_3() -> OpenAPI:
"""Construct OpenAPI from mixed object"""
- return OpenAPI.parse_obj(
+ return OpenAPI.model_validate(
{
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {"/ping": PathItem(get={"responses": {"200": Response(description="pong")}})},
diff --git a/tests/test_swagger_openapi_v3.py b/tests/test_swagger_openapi_v3.py
index 86b70f8..aa4dd23 100644
--- a/tests/test_swagger_openapi_v3.py
+++ b/tests/test_swagger_openapi_v3.py
@@ -1,12 +1,13 @@
from typing import Dict, Optional
-from pydantic import Field
+from pydantic import Field, ConfigDict
from openapi_schema_pydantic import OpenAPI, Operation, PathItem
def test_swagger_openapi_v3():
- open_api = ExtendedOpenAPI.parse_file("tests/data/swagger_openapi_v3.0.1.json")
+ with open("tests/data/swagger_openapi_v3.0.1.json") as f:
+ open_api = ExtendedOpenAPI.model_validate_json(f.read())
assert open_api
@@ -15,8 +16,7 @@ class ExtendedOperation(Operation):
xCodegenRequestBodyName: Optional[str] = Field(default=None, alias="x-codegen-request-body-name")
- class Config:
- allow_population_by_field_name = True
+ model_config = ConfigDict(populate_by_name=True)
class ExtendedPathItem(PathItem):
@@ -31,4 +31,4 @@ class ExtendedPathItem(PathItem):
class ExtendedOpenAPI(OpenAPI):
- paths: Dict[str, ExtendedPathItem] = ...
+ paths: Dict[str, ExtendedPathItem]
diff --git a/tests/util/test_optional_and_computed.py b/tests/util/test_optional_and_computed.py
new file mode 100644
index 0000000..e1e3be5
--- /dev/null
+++ b/tests/util/test_optional_and_computed.py
@@ -0,0 +1,87 @@
+from pydantic import BaseModel, computed_field, ConfigDict
+from pydantic.json_schema import JsonSchemaMode
+
+from openapi_schema_pydantic import Info, MediaType, OpenAPI, Operation, PathItem, RequestBody, Response, Schema
+from openapi_schema_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
+
+
+def test_optional_and_computed_fields():
+ api = construct_sample_api()
+
+ result = construct_open_api_with_schema_class(api)
+ assert result.components is not None
+ assert result.components.schemas is not None
+
+ req_schema = result.components.schemas["SampleRequest"]
+ assert isinstance(req_schema, Schema)
+ assert req_schema.properties is not None
+ assert req_schema.required is not None
+
+ resp_schema = result.components.schemas["SampleResponse"]
+ assert isinstance(resp_schema, Schema)
+ assert resp_schema.properties is not None
+ assert resp_schema.required is not None
+
+ # When validating:
+ # - required fields are still required
+ # - optional fields are still optional
+ # - computed fields don't exist
+ assert "req" in req_schema.properties
+ assert "opt" in req_schema.properties
+ assert "comp" not in req_schema.properties
+ assert set(req_schema.required) == {"req"}
+
+ # When serializing:
+ # - required fields are still required
+ # - optional fields become required
+ # - computed fields are required
+ assert "req" in resp_schema.properties
+ assert "opt" in resp_schema.properties
+ assert "comp" in resp_schema.properties
+ assert set(resp_schema.required) == {"req", "comp", "opt"}
+
+
+def construct_sample_api() -> OpenAPI:
+ return OpenAPI(
+ info=Info(
+ title="Sample API",
+ version="v0.0.1",
+ ),
+ paths={
+ "/callme": PathItem(
+ post=Operation(
+ requestBody=RequestBody(
+ content={"application/json": MediaType(schema=PydanticSchema(schema_class=SampleRequest))}
+ ),
+ responses={
+ "200": Response(
+ description="resp",
+ content={"application/json": MediaType(schema=PydanticSchema(schema_class=SampleResponse))},
+ )
+ },
+ )
+ )
+ },
+ )
+
+
+class ConfigDictExt(ConfigDict, total=False):
+ json_schema_mode: JsonSchemaMode
+
+
+class SampleModel(BaseModel):
+ req: bool
+ opt: bool | None = None
+
+ @computed_field
+ @property
+ def comp(self) -> bool:
+ return True
+
+
+class SampleRequest(SampleModel):
+ model_config = ConfigDictExt(json_schema_mode="validation")
+
+
+class SampleResponse(SampleModel):
+ model_config = ConfigDictExt(json_schema_mode="serialization")
diff --git a/tests/util/test_pydantic_field.py b/tests/util/test_pydantic_field.py
index e5e72e9..eb297b3 100644
--- a/tests/util/test_pydantic_field.py
+++ b/tests/util/test_pydantic_field.py
@@ -2,7 +2,7 @@
from typing import Union
from pydantic import BaseModel, Field
-from pydantic.schema import schema
+from pydantic.json_schema import models_json_schema
from openapi_schema_pydantic import (
OpenAPI,
@@ -22,17 +22,17 @@
def test_pydantic_discriminator_schema_generation():
"""https://github.com/kuimono/openapi-schema-pydantic/issues/8"""
- json_schema = schema([RequestModel])
+ _key_map, json_schema = models_json_schema([(RequestModel, "validation")])
assert json_schema == {
- "definitions": {
+ "$defs": {
"DataAModel": {
- "properties": {"kind": {"enum": ["a"], "title": "Kind", "type": "string"}},
+ "properties": {"kind": {"const": "a", "title": "Kind"}},
"required": ["kind"],
"title": "DataAModel",
"type": "object",
},
"DataBModel": {
- "properties": {"kind": {"enum": ["b"], "title": "Kind", "type": "string"}},
+ "properties": {"kind": {"const": "b", "title": "Kind"}},
"required": ["kind"],
"title": "DataBModel",
"type": "object",
@@ -40,11 +40,11 @@ def test_pydantic_discriminator_schema_generation():
"RequestModel": {
"properties": {
"data": {
- "anyOf": [{"$ref": "#/definitions/DataAModel"}, {"$ref": "#/definitions/DataBModel"}],
"discriminator": {
- "mapping": {"a": "#/definitions/DataAModel", "b": "#/definitions/DataBModel"},
+ "mapping": {"a": "#/$defs/DataAModel", "b": "#/$defs/DataBModel"},
"propertyName": "kind",
},
+ "oneOf": [{"$ref": "#/$defs/DataAModel"}, {"$ref": "#/$defs/DataBModel"}],
"title": "Data",
}
},
@@ -60,10 +60,12 @@ def test_pydantic_discriminator_openapi_generation():
"""https://github.com/kuimono/openapi-schema-pydantic/issues/8"""
open_api = construct_open_api_with_schema_class(construct_base_open_api())
+ assert open_api.components is not None
+ assert open_api.components.schemas is not None
json_schema = open_api.components.schemas["RequestModel"]
assert json_schema.properties == {
"data": Schema(
- anyOf=[
+ oneOf=[
Reference(ref="#/components/schemas/DataAModel", summary=None, description=None),
Reference(ref="#/components/schemas/DataBModel", summary=None, description=None),
],
diff --git a/tests/util/test_util.py b/tests/util/test_util.py
index a280106..88f60a9 100644
--- a/tests/util/test_util.py
+++ b/tests/util/test_util.py
@@ -1,6 +1,6 @@
import logging
-from pydantic import BaseModel, Field
+from pydantic import BaseModel, Field, computed_field
from openapi_schema_pydantic import Info, MediaType, OpenAPI, Operation, PathItem, Reference, RequestBody, Response
from openapi_schema_pydantic.util import PydanticSchema, construct_open_api_with_schema_class
@@ -13,7 +13,7 @@ def test_construct_open_api_with_schema_class_1():
assert result_open_api_1.components == result_open_api_2.components
assert result_open_api_1 == result_open_api_2
- open_api_json = result_open_api_1.json(by_alias=True, exclude_none=True, indent=2)
+ open_api_json = result_open_api_1.model_dump_json(by_alias=True, exclude_none=True, indent=2)
logging.debug(open_api_json)
@@ -43,7 +43,7 @@ def test_construct_open_api_with_schema_class_3():
def construct_base_open_api_1() -> OpenAPI:
- return OpenAPI.parse_obj(
+ return OpenAPI.model_validate(
{
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {
@@ -146,3 +146,12 @@ class PongResponse(BaseModel):
resp_foo: str = Field(alias="pong_foo", description="foo value of the response")
resp_bar: str = Field(alias="pong_bar", description="bar value of the response")
+
+
+class ModelWithOptionalAndComputed(BaseModel):
+ enable: bool | None = None
+
+ @computed_field
+ @property
+ def comp(self) -> bool:
+ return True
diff --git a/tests/v3_0_3/test_config_example.py b/tests/v3_0_3/test_config_example.py
index 2fb3f16..3636369 100644
--- a/tests/v3_0_3/test_config_example.py
+++ b/tests/v3_0_3/test_config_example.py
@@ -1,3 +1,4 @@
+from pydantic import BaseModel
from openapi_schema_pydantic.v3.v3_0_3 import (
OpenAPI,
Info,
@@ -70,8 +71,11 @@ def test_config_example():
def _assert_config_examples(schema_type):
- if getattr(schema_type, "Config", None) and getattr(schema_type.Config, "schema_extra", None):
- examples = schema_type.Config.schema_extra.get("examples")
+ if not hasattr(schema_type, "model_config"):
+ return
+ extra = schema_type.model_config.get("json_schema_extra")
+ if extra is not None:
+ examples = extra.get("examples")
for example_dict in examples:
- obj = schema_type(**example_dict)
- assert obj.__fields_set__
+ obj = schema_type.model_validate(example_dict)
+ assert obj.model_fields_set
diff --git a/tests/v3_0_3/test_optional_and_computed.py b/tests/v3_0_3/test_optional_and_computed.py
new file mode 100644
index 0000000..fa68e6b
--- /dev/null
+++ b/tests/v3_0_3/test_optional_and_computed.py
@@ -0,0 +1,96 @@
+from pydantic import BaseModel, computed_field, ConfigDict
+from pydantic.json_schema import JsonSchemaMode
+
+from openapi_schema_pydantic.v3.v3_0_3 import (
+ Info,
+ MediaType,
+ OpenAPI,
+ Operation,
+ PathItem,
+ RequestBody,
+ Response,
+ Schema,
+)
+from openapi_schema_pydantic.v3.v3_0_3.util import PydanticSchema, construct_open_api_with_schema_class
+
+
+def test_optional_and_computed_fields():
+ api = construct_sample_api()
+
+ result = construct_open_api_with_schema_class(api)
+ assert result.components is not None
+ assert result.components.schemas is not None
+
+ req_schema = result.components.schemas["SampleRequest"]
+ assert isinstance(req_schema, Schema)
+ assert req_schema.properties is not None
+ assert req_schema.required is not None
+
+ resp_schema = result.components.schemas["SampleResponse"]
+ assert isinstance(resp_schema, Schema)
+ assert resp_schema.properties is not None
+ assert resp_schema.required is not None
+
+ # When validating:
+ # - required fields are still required
+ # - optional fields are still optional
+ # - computed fields don't exist
+ assert "req" in req_schema.properties
+ assert "opt" in req_schema.properties
+ assert "comp" not in req_schema.properties
+ assert set(req_schema.required) == {"req"}
+
+ # When serializing:
+ # - required fields are still required
+ # - optional fields become required
+ # - computed fields are required
+ assert "req" in resp_schema.properties
+ assert "opt" in resp_schema.properties
+ assert "comp" in resp_schema.properties
+ assert set(resp_schema.required) == {"req", "comp", "opt"}
+
+
+def construct_sample_api() -> OpenAPI:
+ return OpenAPI(
+ info=Info(
+ title="Sample API",
+ version="v0.0.1",
+ ),
+ paths={
+ "/callme": PathItem(
+ post=Operation(
+ requestBody=RequestBody(
+ content={"application/json": MediaType(schema=PydanticSchema(schema_class=SampleRequest))}
+ ),
+ responses={
+ "200": Response(
+ description="resp",
+ content={"application/json": MediaType(schema=PydanticSchema(schema_class=SampleResponse))},
+ )
+ },
+ )
+ )
+ },
+ )
+
+
+class ConfigDictExt(ConfigDict, total=False):
+ json_schema_mode: JsonSchemaMode
+
+
+class SampleModel(BaseModel):
+ req: bool
+ opt: bool | None = None
+
+ @computed_field
+ @property
+ def comp(self) -> bool:
+ return True
+
+
+class SampleRequest(SampleModel):
+ model_config = ConfigDictExt(json_schema_mode="validation")
+
+
+class SampleResponse(SampleModel):
+ model_config = ConfigDictExt(json_schema_mode="serialization")
diff --git a/tests/v3_0_3/test_util.py b/tests/v3_0_3/test_util.py
index 8193726..672341e 100644
--- a/tests/v3_0_3/test_util.py
+++ b/tests/v3_0_3/test_util.py
@@ -22,7 +22,7 @@ def test_construct_open_api_with_schema_class_1():
assert result_open_api_1.components == result_open_api_2.components
assert result_open_api_1 == result_open_api_2
- open_api_json = result_open_api_1.json(by_alias=True, exclude_none=True, indent=2)
+ open_api_json = result_open_api_1.model_dump_json(by_alias=True, exclude_none=True, indent=2)
logging.debug(open_api_json)
@@ -52,7 +52,7 @@ def test_construct_open_api_with_schema_class_3():
def construct_base_open_api_1() -> OpenAPI:
- return OpenAPI.parse_obj(
+ return OpenAPI.model_validate(
{
"info": {"title": "My own API", "version": "v0.0.1"},
"paths": {
diff --git a/tests/v3_1_0/__init__.py b/tests/v3_1_0/__init__.py
index df2a9c0..01d3dfa 100644
--- a/tests/v3_1_0/__init__.py
+++ b/tests/v3_1_0/__init__.py
@@ -2,5 +2,5 @@
def test_empty_schema():
- schema = Schema.parse_obj({})
+ schema = Schema.model_validate({})
assert schema == Schema()