diff --git a/openapi_schema_pydantic/v3/v3_1_0/_config.py b/openapi_schema_pydantic/v3/v3_1_0/_config.py new file mode 100644 index 0000000..549ec89 --- /dev/null +++ b/openapi_schema_pydantic/v3/v3_1_0/_config.py @@ -0,0 +1,5 @@ +from pydantic import Extra + + +class DefaultConfig: + extra = Extra.ignore diff --git a/openapi_schema_pydantic/v3/v3_1_0/components.py b/openapi_schema_pydantic/v3/v3_1_0/components.py index 899d333..a31e611 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/components.py +++ b/openapi_schema_pydantic/v3/v3_1_0/components.py @@ -1,7 +1,8 @@ from typing import Dict, Optional, Union -from pydantic import BaseModel, Extra +from pydantic import BaseModel +from ._config import DefaultConfig from .callback import Callback from .example import Example from .header import Header @@ -52,8 +53,7 @@ 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 + class Config(DefaultConfig): schema_extra = { "examples": [ { diff --git a/openapi_schema_pydantic/v3/v3_1_0/contact.py b/openapi_schema_pydantic/v3/v3_1_0/contact.py index 26be261..f0579ea 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/contact.py +++ b/openapi_schema_pydantic/v3/v3_1_0/contact.py @@ -2,6 +2,8 @@ from pydantic import AnyUrl, BaseModel, Extra +from ._config import DefaultConfig + class Contact(BaseModel): """ @@ -25,8 +27,7 @@ class Contact(BaseModel): MUST be in the form of an email address. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): 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..124f216 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/discriminator.py +++ b/openapi_schema_pydantic/v3/v3_1_0/discriminator.py @@ -2,6 +2,8 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig + class Discriminator(BaseModel): """ @@ -24,8 +26,7 @@ class Discriminator(BaseModel): An object to hold mappings between payload values and schema names or references. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ { diff --git a/openapi_schema_pydantic/v3/v3_1_0/encoding.py b/openapi_schema_pydantic/v3/v3_1_0/encoding.py index 7dc6220..60b7cd3 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/encoding.py +++ b/openapi_schema_pydantic/v3/v3_1_0/encoding.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig from .reference import Reference @@ -67,8 +68,7 @@ class Encoding(BaseModel): then the value of [`contentType`](#encodingContentType) (implicit or explicit) SHALL be ignored. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ { diff --git a/openapi_schema_pydantic/v3/v3_1_0/example.py b/openapi_schema_pydantic/v3/v3_1_0/example.py index a8c8bce..e909492 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/example.py +++ b/openapi_schema_pydantic/v3/v3_1_0/example.py @@ -2,9 +2,10 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig -class Example(BaseModel): +class Example(BaseModel): summary: Optional[str] = None """ Short description for the example. @@ -33,8 +34,7 @@ class Example(BaseModel): See the rules for resolving [Relative References](#relativeReferencesURI). """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ {"summary": "A foo example", "value": {"foo": "bar"}}, 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..8586388 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/external_documentation.py +++ b/openapi_schema_pydantic/v3/v3_1_0/external_documentation.py @@ -1,5 +1,6 @@ from typing import Optional +from ._config import DefaultConfig from pydantic import AnyUrl, BaseModel, Extra @@ -18,6 +19,5 @@ class ExternalDocumentation(BaseModel): Value MUST be in the form of a URL. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): 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..18dca5c 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/header.py +++ b/openapi_schema_pydantic/v3/v3_1_0/header.py @@ -1,5 +1,6 @@ from pydantic import Extra, Field +from ._config import DefaultConfig from .parameter import Parameter @@ -16,8 +17,7 @@ class Header(Parameter): name = Field(default="", const=True) param_in = Field(default="header", const=True, alias="in") - class Config: - extra = Extra.ignore + class Config(DefaultConfig): allow_population_by_field_name = True schema_extra = { "examples": [ diff --git a/openapi_schema_pydantic/v3/v3_1_0/info.py b/openapi_schema_pydantic/v3/v3_1_0/info.py index 5e8955b..efd5db7 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/info.py +++ b/openapi_schema_pydantic/v3/v3_1_0/info.py @@ -2,6 +2,7 @@ from pydantic import AnyUrl, BaseModel, Extra +from ._config import DefaultConfig from .contact import Contact from .license import License @@ -51,8 +52,7 @@ class Info(BaseModel): (which is distinct from the [OpenAPI Specification version](#oasVersion) or the API implementation version). """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ { diff --git a/openapi_schema_pydantic/v3/v3_1_0/license.py b/openapi_schema_pydantic/v3/v3_1_0/license.py index f0ba1bb..5c2c4d9 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/license.py +++ b/openapi_schema_pydantic/v3/v3_1_0/license.py @@ -1,6 +1,7 @@ from typing import Optional from pydantic import AnyUrl, BaseModel, Extra +from ._config import DefaultConfig class License(BaseModel): @@ -26,8 +27,7 @@ class License(BaseModel): The `url` field is mutually exclusive of the `identifier` field. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ {"name": "Apache 2.0", "identifier": "Apache-2.0"}, diff --git a/openapi_schema_pydantic/v3/v3_1_0/link.py b/openapi_schema_pydantic/v3/v3_1_0/link.py index df82633..2cc659d 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/link.py +++ b/openapi_schema_pydantic/v3/v3_1_0/link.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig from .server import Server @@ -62,8 +63,7 @@ class Link(BaseModel): A server object to be used by the target operation. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ {"operationId": "getUserAddressByUUID", "parameters": {"userUuid": "$response.body#/uuid"}}, 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..9672abe 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/media_type.py +++ b/openapi_schema_pydantic/v3/v3_1_0/media_type.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra, Field +from ._config import DefaultConfig from .encoding import Encoding from .example import Example from .reference import Reference @@ -48,8 +49,7 @@ class MediaType(BaseModel): when the media type is `multipart` or `application/x-www-form-urlencoded`. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): allow_population_by_field_name = True schema_extra = { "examples": [ 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..4ef8141 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/oauth_flow.py +++ b/openapi_schema_pydantic/v3/v3_1_0/oauth_flow.py @@ -2,6 +2,8 @@ from pydantic import AnyUrl, BaseModel, Extra +from ._config import DefaultConfig + class OAuthFlow(BaseModel): """ @@ -38,8 +40,7 @@ class OAuthFlow(BaseModel): The map MAY be empty. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ { 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..1113dc8 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/oauth_flows.py +++ b/openapi_schema_pydantic/v3/v3_1_0/oauth_flows.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig from .oauth_flow import OAuthFlow @@ -34,5 +35,5 @@ class OAuthFlows(BaseModel): Previously called `accessCode` in OpenAPI 2.0. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): + pass 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..f0bfe99 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/open_api.py +++ b/openapi_schema_pydantic/v3/v3_1_0/open_api.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig from .components import Components from .external_documentation import ExternalDocumentation from .info import Info @@ -86,5 +87,5 @@ class OpenAPI(BaseModel): Additional external documentation. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): + pass diff --git a/openapi_schema_pydantic/v3/v3_1_0/operation.py b/openapi_schema_pydantic/v3/v3_1_0/operation.py index 7b5cde3..3292987 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/operation.py +++ b/openapi_schema_pydantic/v3/v3_1_0/operation.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig from .callback import Callback from .external_documentation import ExternalDocumentation from .parameter import Parameter @@ -106,8 +107,7 @@ class Operation(BaseModel): it will be overridden by this value. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ { diff --git a/openapi_schema_pydantic/v3/v3_1_0/parameter.py b/openapi_schema_pydantic/v3/v3_1_0/parameter.py index e4f69c4..b9fac7d 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/parameter.py +++ b/openapi_schema_pydantic/v3/v3_1_0/parameter.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Field, Extra +from ._config import DefaultConfig from .example import Example from .media_type import MediaType from .reference import Reference @@ -140,8 +141,7 @@ class Parameter(BaseModel): The map MUST only contain one entry. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): allow_population_by_field_name = True schema_extra = { "examples": [ 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..e501a00 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/path_item.py +++ b/openapi_schema_pydantic/v3/v3_1_0/path_item.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra, Field +from ._config import DefaultConfig from .operation import Operation from .parameter import Parameter from .reference import Reference @@ -92,8 +93,7 @@ class PathItem(BaseModel): [OpenAPI Object's components/parameters](#componentsParameters). """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): allow_population_by_field_name = True schema_extra = { "examples": [ diff --git a/openapi_schema_pydantic/v3/v3_1_0/reference.py b/openapi_schema_pydantic/v3/v3_1_0/reference.py index 76e16c3..77a1bab 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/reference.py +++ b/openapi_schema_pydantic/v3/v3_1_0/reference.py @@ -2,6 +2,8 @@ from pydantic import BaseModel, Extra, Field +from ._config import DefaultConfig + class Reference(BaseModel): """ @@ -29,8 +31,7 @@ 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 + class Config(DefaultConfig): allow_population_by_field_name = True 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..464c02a 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/request_body.py +++ b/openapi_schema_pydantic/v3/v3_1_0/request_body.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig from .media_type import MediaType @@ -30,8 +31,7 @@ class RequestBody(BaseModel): Determines if the request body is required in the request. Defaults to `false`. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ { diff --git a/openapi_schema_pydantic/v3/v3_1_0/response.py b/openapi_schema_pydantic/v3/v3_1_0/response.py index 1f7042c..ee12469 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/response.py +++ b/openapi_schema_pydantic/v3/v3_1_0/response.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig from .header import Header from .link import Link from .media_type import MediaType @@ -43,8 +44,7 @@ class Response(BaseModel): following the naming constraints of the names for [Component Objects](#componentsObject). """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ { diff --git a/openapi_schema_pydantic/v3/v3_1_0/responses.py b/openapi_schema_pydantic/v3/v3_1_0/responses.py index 569e08b..317fc40 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/responses.py +++ b/openapi_schema_pydantic/v3/v3_1_0/responses.py @@ -1,5 +1,6 @@ from typing import Dict, Union +from ._config import DefaultConfig from .response import Response from .reference import Reference diff --git a/openapi_schema_pydantic/v3/v3_1_0/schema.py b/openapi_schema_pydantic/v3/v3_1_0/schema.py index 1739f0e..2b3f0f6 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/schema.py +++ b/openapi_schema_pydantic/v3/v3_1_0/schema.py @@ -1,6 +1,8 @@ from typing import Any, Dict, List, Optional, Union from pydantic import BaseModel, Extra, Field + +from ._config import DefaultConfig from .discriminator import Discriminator from .external_documentation import ExternalDocumentation from .reference import Reference @@ -830,8 +832,7 @@ class Schema(BaseModel): Use of example is discouraged, and later versions of this specification may remove it. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): allow_population_by_field_name = True schema_extra = { "examples": [ diff --git a/openapi_schema_pydantic/v3/v3_1_0/security_requirement.py b/openapi_schema_pydantic/v3/v3_1_0/security_requirement.py index 0fb703f..6f77fae 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/security_requirement.py +++ b/openapi_schema_pydantic/v3/v3_1_0/security_requirement.py @@ -1,5 +1,6 @@ from typing import Dict, List +from ._config import DefaultConfig SecurityRequirement = Dict[str, List[str]] """ 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..c72208b 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/security_scheme.py +++ b/openapi_schema_pydantic/v3/v3_1_0/security_scheme.py @@ -2,6 +2,7 @@ from pydantic import AnyUrl, BaseModel, Extra, Field +from ._config import DefaultConfig from .oauth_flows import OAuthFlows @@ -71,8 +72,7 @@ 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 + class Config(DefaultConfig): allow_population_by_field_name = True schema_extra = { "examples": [ diff --git a/openapi_schema_pydantic/v3/v3_1_0/server.py b/openapi_schema_pydantic/v3/v3_1_0/server.py index aeeefd5..d011128 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/server.py +++ b/openapi_schema_pydantic/v3/v3_1_0/server.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig from .server_variable import ServerVariable @@ -30,8 +31,7 @@ class Server(BaseModel): The value is used for substitution in the server's URL template. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ {"url": "https://development.gigantic-server.com/v1", "description": "Development server"}, 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..fa72290 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/server_variable.py +++ b/openapi_schema_pydantic/v3/v3_1_0/server_variable.py @@ -1,5 +1,6 @@ from typing import List, Optional +from ._config import DefaultConfig from pydantic import BaseModel, Extra @@ -27,5 +28,5 @@ class ServerVariable(BaseModel): [CommonMark syntax](https://spec.commonmark.org/) MAY be used for rich text representation. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): + pass diff --git a/openapi_schema_pydantic/v3/v3_1_0/tag.py b/openapi_schema_pydantic/v3/v3_1_0/tag.py index 24e926a..ca23efd 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/tag.py +++ b/openapi_schema_pydantic/v3/v3_1_0/tag.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig from .external_documentation import ExternalDocumentation @@ -27,6 +28,5 @@ class Tag(BaseModel): Additional external documentation for this tag. """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): 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..f2b1363 100644 --- a/openapi_schema_pydantic/v3/v3_1_0/xml.py +++ b/openapi_schema_pydantic/v3/v3_1_0/xml.py @@ -2,6 +2,8 @@ from pydantic import BaseModel, Extra +from ._config import DefaultConfig + class XML(BaseModel): """ @@ -47,8 +49,7 @@ class XML(BaseModel): The definition takes effect only when defined alongside `type` being `array` (outside the `items`). """ - class Config: - extra = Extra.ignore + class Config(DefaultConfig): schema_extra = { "examples": [ {"name": "animal"}, diff --git a/tests/test_global_config.py b/tests/test_global_config.py new file mode 100644 index 0000000..8e4d967 --- /dev/null +++ b/tests/test_global_config.py @@ -0,0 +1,59 @@ +from typing import ContextManager +import pytest +from contextlib import nullcontext as does_not_raise +from pydantic import Extra, ValidationError + +from openapi_schema_pydantic import OpenAPI, Operation, PathItem, Info +from openapi_schema_pydantic.v3.v3_1_0._config import DefaultConfig + + +@pytest.mark.parametrize( + "cm,extra", + [ + (pytest.raises(AttributeError), Extra.ignore), + (pytest.raises(ValidationError), Extra.forbid), + (does_not_raise(), Extra.allow), + ], +) +def test_dumb_key( + monkeypatch: pytest.MonkeyPatch, + cm: ContextManager, + extra: Extra, +): + with monkeypatch.context() as m: + m.setattr(DefaultConfig, "extra", extra) + with cm: + api = OpenAPI( + info=Info( + title="dumb test", + version="3", + ), + dumb="DUMB", + ) + assert api.dumb == "DUMB" + +@pytest.mark.parametrize( + "cm,extra", + [ + (pytest.raises(AttributeError), Extra.ignore), + (pytest.raises(ValidationError), Extra.forbid), + (does_not_raise(), Extra.allow), + ], +) +def test_dumb_key_manual_assignment_of_config_value( + monkeypatch: pytest.MonkeyPatch, + cm: ContextManager, + extra: Extra, +): + with monkeypatch.context() as m: + DefaultConfig.extra = extra + #m.setattr(DefaultConfig, "extra", extra) + with cm: + api = OpenAPI( + info=Info( + title="dumb test", + version="3", + ), + dumb="DUMB", + ) + assert api.dumb == "DUMB"