Browse Source

🔥 Remove conditionals checking if Pydantic v1 was installed, first pass

pull/14575/head
Sebastián Ramírez 7 months ago
parent
commit
00c8b73493
  1. 91
      fastapi/_compat/main.py
  2. 63
      fastapi/_compat/v1.py
  3. 2
      fastapi/dependencies/utils.py
  4. 13
      fastapi/openapi/models.py
  5. 11
      fastapi/params.py
  6. 4
      fastapi/routing.py
  7. 10
      fastapi/utils.py
  8. 6
      tests/test_additional_properties_bool.py
  9. 2
      tests/test_ambiguous_params.py
  10. 16
      tests/test_custom_schema_fields.py
  11. 6
      tests/test_forms_single_model.py
  12. 25
      tests/test_jsonable_encoder.py
  13. 11
      tests/test_openapi_separate_input_output_schemas.py
  14. 6
      tests/test_query_cookie_header_model_extra_params.py
  15. 4
      tests/test_request_param_model_by_alias.py
  16. 11
      tests/test_response_by_alias.py
  17. 6
      tests/test_schema_extra_examples.py
  18. 6
      tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial001.py
  19. 5
      tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial002.py
  20. 4
      tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial003.py
  21. 4
      tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial004.py
  22. 5
      tests/test_validate_response_recursive/app.py

91
fastapi/_compat/main.py

@ -6,14 +6,13 @@ from typing import (
)
from fastapi._compat import may_v1
from fastapi._compat.shared import PYDANTIC_V2, lenient_issubclass
from fastapi._compat.shared import lenient_issubclass
from fastapi.types import ModelNameMap
from pydantic import BaseModel
from typing_extensions import Literal
from . import v2
from .model_field import ModelField
if PYDANTIC_V2:
from .v2 import BaseConfig as BaseConfig
from .v2 import FieldInfo as FieldInfo
from .v2 import PydanticSchemaGenerationError as PydanticSchemaGenerationError
@ -27,22 +26,6 @@ if PYDANTIC_V2:
from .v2 import (
with_info_plain_validator_function as with_info_plain_validator_function,
)
else:
from .v1 import BaseConfig as BaseConfig # type: ignore[assignment]
from .v1 import FieldInfo as FieldInfo
from .v1 import ( # type: ignore[assignment]
PydanticSchemaGenerationError as PydanticSchemaGenerationError,
)
from .v1 import RequiredParam as RequiredParam
from .v1 import Undefined as Undefined
from .v1 import UndefinedType as UndefinedType
from .v1 import Url as Url # type: ignore[assignment]
from .v1 import Validator as Validator
from .v1 import evaluate_forwardref as evaluate_forwardref
from .v1 import get_missing_field_error as get_missing_field_error
from .v1 import ( # type: ignore[assignment]
with_info_plain_validator_function as with_info_plain_validator_function,
)
@lru_cache
@ -60,11 +43,8 @@ def get_cached_model_fields(model: type[BaseModel]) -> list[ModelField]:
def _is_undefined(value: object) -> bool:
if isinstance(value, may_v1.UndefinedType):
return True
elif PYDANTIC_V2:
from . import v2
return isinstance(value, v2.UndefinedType)
return False
def _get_model_config(model: BaseModel) -> Any:
@ -72,8 +52,6 @@ def _get_model_config(model: BaseModel) -> Any:
from fastapi._compat import v1
return v1._get_model_config(model)
elif PYDANTIC_V2:
from . import v2
return v2._get_model_config(model)
@ -85,8 +63,6 @@ def _model_dump(
from fastapi._compat import v1
return v1._model_dump(model, mode=mode, **kwargs)
elif PYDANTIC_V2:
from . import v2
return v2._model_dump(model, mode=mode, **kwargs)
@ -94,11 +70,8 @@ def _model_dump(
def _is_error_wrapper(exc: Exception) -> bool:
if isinstance(exc, may_v1.ErrorWrapper):
return True
elif PYDANTIC_V2:
from . import v2
return isinstance(exc, v2.ErrorWrapper)
return False
def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
@ -106,9 +79,6 @@ def copy_field_info(*, field_info: FieldInfo, annotation: Any) -> FieldInfo:
from fastapi._compat import v1
return v1.copy_field_info(field_info=field_info, annotation=annotation)
else:
assert PYDANTIC_V2
from . import v2
return v2.copy_field_info(field_info=field_info, annotation=annotation)
@ -120,9 +90,6 @@ def create_body_model(
from fastapi._compat import v1
return v1.create_body_model(fields=fields, model_name=model_name)
else:
assert PYDANTIC_V2
from . import v2
return v2.create_body_model(fields=fields, model_name=model_name) # type: ignore[arg-type]
@ -136,9 +103,6 @@ def get_annotation_from_field_info(
return v1.get_annotation_from_field_info(
annotation=annotation, field_info=field_info, field_name=field_name
)
else:
assert PYDANTIC_V2
from . import v2
return v2.get_annotation_from_field_info(
annotation=annotation, field_info=field_info, field_name=field_name
@ -150,9 +114,6 @@ def is_bytes_field(field: ModelField) -> bool:
from fastapi._compat import v1
return v1.is_bytes_field(field)
else:
assert PYDANTIC_V2
from . import v2
return v2.is_bytes_field(field) # type: ignore[arg-type]
@ -162,9 +123,6 @@ def is_bytes_sequence_field(field: ModelField) -> bool:
from fastapi._compat import v1
return v1.is_bytes_sequence_field(field)
else:
assert PYDANTIC_V2
from . import v2
return v2.is_bytes_sequence_field(field) # type: ignore[arg-type]
@ -174,9 +132,6 @@ def is_scalar_field(field: ModelField) -> bool:
from fastapi._compat import v1
return v1.is_scalar_field(field)
else:
assert PYDANTIC_V2
from . import v2
return v2.is_scalar_field(field) # type: ignore[arg-type]
@ -186,9 +141,6 @@ def is_scalar_sequence_field(field: ModelField) -> bool:
from fastapi._compat import v1
return v1.is_scalar_sequence_field(field)
else:
assert PYDANTIC_V2
from . import v2
return v2.is_scalar_sequence_field(field) # type: ignore[arg-type]
@ -198,9 +150,6 @@ def is_sequence_field(field: ModelField) -> bool:
from fastapi._compat import v1
return v1.is_sequence_field(field)
else:
assert PYDANTIC_V2
from . import v2
return v2.is_sequence_field(field) # type: ignore[arg-type]
@ -210,9 +159,6 @@ def serialize_sequence_value(*, field: ModelField, value: Any) -> Sequence[Any]:
from fastapi._compat import v1
return v1.serialize_sequence_value(field=field, value=value)
else:
assert PYDANTIC_V2
from . import v2
return v2.serialize_sequence_value(field=field, value=value) # type: ignore[arg-type]
@ -222,9 +168,7 @@ def _model_rebuild(model: type[BaseModel]) -> None:
from fastapi._compat import v1
v1._model_rebuild(model)
elif PYDANTIC_V2:
from . import v2
else:
v2._model_rebuild(model)
@ -241,23 +185,13 @@ def get_compat_model_name_map(fields: list[ModelField]) -> ModelNameMap:
all_flat_models = v1_flat_models
else:
all_flat_models = set()
if PYDANTIC_V2:
from . import v2
v2_model_fields = [
field for field in fields if isinstance(field, v2.ModelField)
]
v2_flat_models = v2.get_flat_models_from_fields(
v2_model_fields, known_models=set()
)
v2_model_fields = [field for field in fields if isinstance(field, v2.ModelField)]
v2_flat_models = v2.get_flat_models_from_fields(v2_model_fields, known_models=set())
all_flat_models = all_flat_models.union(v2_flat_models)
model_name_map = v2.get_model_name_map(all_flat_models)
return model_name_map
from fastapi._compat import v1
model_name_map = v1.get_model_name_map(all_flat_models)
return model_name_map
def get_definitions(
@ -279,10 +213,6 @@ def get_definitions(
model_name_map=model_name_map,
separate_input_output_schemas=separate_input_output_schemas,
)
if not PYDANTIC_V2:
return v1_field_maps, v1_definitions
else:
from . import v2
v2_fields = [field for field in fields if isinstance(field, v2.ModelField)]
v2_field_maps, v2_definitions = v2.get_definitions(
@ -296,8 +226,6 @@ def get_definitions(
# Pydantic v1 is not supported since Python 3.14
else:
from . import v2
v2_fields = [field for field in fields if isinstance(field, v2.ModelField)]
v2_field_maps, v2_definitions = v2.get_definitions(
fields=v2_fields,
@ -326,9 +254,6 @@ def get_schema_from_model_field(
field_mapping=field_mapping,
separate_input_output_schemas=separate_input_output_schemas,
)
else:
assert PYDANTIC_V2
from . import v2
return v2.get_schema_from_model_field(
field=field, # type: ignore[arg-type]
@ -341,18 +266,12 @@ def get_schema_from_model_field(
def _is_model_field(value: Any) -> bool:
if isinstance(value, may_v1.ModelField):
return True
elif PYDANTIC_V2:
from . import v2
return isinstance(value, v2.ModelField)
return False
def _is_model_class(value: Any) -> bool:
if lenient_issubclass(value, may_v1.BaseModel):
return True
elif PYDANTIC_V2:
from . import v2
return lenient_issubclass(value, v2.BaseModel) # type: ignore[attr-defined]
return False

63
fastapi/_compat/v1.py

@ -11,61 +11,6 @@ from typing import (
from fastapi._compat import shared
from fastapi.openapi.constants import REF_PREFIX as REF_PREFIX
from fastapi.types import ModelNameMap
from pydantic.version import VERSION as PYDANTIC_VERSION
from typing_extensions import Literal
PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2])
PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2
# Keeping old "Required" functionality from Pydantic V1, without
# shadowing typing.Required.
RequiredParam: Any = Ellipsis
if not PYDANTIC_V2:
from pydantic import BaseConfig as BaseConfig
from pydantic import BaseModel as BaseModel
from pydantic import ValidationError as ValidationError
from pydantic import create_model as create_model
from pydantic.class_validators import Validator as Validator
from pydantic.color import Color as Color
from pydantic.error_wrappers import ErrorWrapper as ErrorWrapper
from pydantic.errors import MissingError
from pydantic.fields import ( # type: ignore[attr-defined]
SHAPE_FROZENSET,
SHAPE_LIST,
SHAPE_SEQUENCE,
SHAPE_SET,
SHAPE_SINGLETON,
SHAPE_TUPLE,
SHAPE_TUPLE_ELLIPSIS,
)
from pydantic.fields import FieldInfo as FieldInfo
from pydantic.fields import ModelField as ModelField # type: ignore[attr-defined]
from pydantic.fields import Undefined as Undefined # type: ignore[attr-defined]
from pydantic.fields import ( # type: ignore[attr-defined]
UndefinedType as UndefinedType,
)
from pydantic.networks import AnyUrl as AnyUrl
from pydantic.networks import NameEmail as NameEmail
from pydantic.schema import TypeModelSet as TypeModelSet
from pydantic.schema import (
field_schema,
model_process_schema,
)
from pydantic.schema import (
get_annotation_from_field_info as get_annotation_from_field_info,
)
from pydantic.schema import get_flat_models_from_field as get_flat_models_from_field
from pydantic.schema import (
get_flat_models_from_fields as get_flat_models_from_fields,
)
from pydantic.schema import get_model_name_map as get_model_name_map
from pydantic.types import SecretBytes as SecretBytes
from pydantic.types import SecretStr as SecretStr
from pydantic.typing import evaluate_forwardref as evaluate_forwardref
from pydantic.utils import lenient_issubclass as lenient_issubclass
else:
from pydantic.v1 import BaseConfig as BaseConfig # type: ignore[assignment]
from pydantic.v1 import BaseModel as BaseModel # type: ignore[assignment]
from pydantic.v1 import ( # type: ignore[assignment]
@ -116,6 +61,14 @@ else:
)
from pydantic.v1.typing import evaluate_forwardref as evaluate_forwardref
from pydantic.v1.utils import lenient_issubclass as lenient_issubclass
from pydantic.version import VERSION as PYDANTIC_VERSION
from typing_extensions import Literal
PYDANTIC_VERSION_MINOR_TUPLE = tuple(int(x) for x in PYDANTIC_VERSION.split(".")[:2])
PYDANTIC_V2 = PYDANTIC_VERSION_MINOR_TUPLE[0] == 2
# Keeping old "Required" functionality from Pydantic V1, without
# shadowing typing.Required.
RequiredParam: Any = Ellipsis
GetJsonSchemaHandler = Any

2
fastapi/dependencies/utils.py

@ -18,7 +18,6 @@ from typing import (
import anyio
from fastapi import params
from fastapi._compat import (
PYDANTIC_V2,
ModelField,
RequiredParam,
Undefined,
@ -445,7 +444,6 @@ def analyze_param(
f" together for {param_name!r}"
)
field_info = value
if PYDANTIC_V2:
if isinstance(field_info, FieldInfo):
field_info.annotation = type_annotation

13
fastapi/openapi/models.py

@ -3,7 +3,6 @@ from enum import Enum
from typing import Annotated, Any, Callable, Optional, Union
from fastapi._compat import (
PYDANTIC_V2,
CoreSchema,
GetJsonSchemaHandler,
JsonSchemaValue,
@ -57,14 +56,8 @@ except ImportError: # pragma: no cover
class BaseModelWithConfig(BaseModel):
if PYDANTIC_V2:
model_config = {"extra": "allow"}
else:
class Config:
extra = "allow"
class Contact(BaseModelWithConfig):
name: Optional[str] = None
@ -226,14 +219,8 @@ class Example(TypedDict, total=False):
value: Optional[Any]
externalValue: Optional[AnyUrl]
if PYDANTIC_V2: # type: ignore [misc]
__pydantic_config__ = {"extra": "allow"}
else:
class Config:
extra = "allow"
class ParameterInType(Enum):
query = "query"

11
fastapi/params.py

@ -9,7 +9,6 @@ from pydantic.fields import FieldInfo
from typing_extensions import Literal, deprecated
from ._compat import (
PYDANTIC_V2,
PYDANTIC_VERSION_MINOR_TUPLE,
Undefined,
)
@ -115,7 +114,7 @@ class Param(FieldInfo): # type: ignore[misc]
self.deprecated = deprecated
else:
kwargs["deprecated"] = deprecated
if PYDANTIC_V2:
if serialization_alias in (_Unset, None) and isinstance(alias, str):
serialization_alias = alias
if validation_alias in (_Unset, None):
@ -131,9 +130,7 @@ class Param(FieldInfo): # type: ignore[misc]
}
)
kwargs["pattern"] = pattern or regex
else:
kwargs["regex"] = pattern or regex
kwargs.update(**current_json_schema_extra)
use_kwargs = {k: v for k, v in kwargs.items() if v is not _Unset}
super().__init__(**use_kwargs)
@ -575,7 +572,6 @@ class Body(FieldInfo): # type: ignore[misc]
self.deprecated = deprecated
else:
kwargs["deprecated"] = deprecated
if PYDANTIC_V2:
if serialization_alias in (_Unset, None) and isinstance(alias, str):
serialization_alias = alias
if validation_alias in (_Unset, None):
@ -591,9 +587,6 @@ class Body(FieldInfo): # type: ignore[misc]
}
)
kwargs["pattern"] = pattern or regex
else:
kwargs["regex"] = pattern or regex
kwargs.update(**current_json_schema_extra)
use_kwargs = {k: v for k, v in kwargs.items() if v is not _Unset}

4
fastapi/routing.py

@ -30,6 +30,7 @@ from fastapi._compat import (
_model_dump,
_normalize_errors,
lenient_issubclass,
may_v1,
)
from fastapi.datastructures import Default, DefaultPlaceholder
from fastapi.dependencies.models import Dependant
@ -58,7 +59,6 @@ from fastapi.utils import (
get_value_or_default,
is_body_allowed_for_status_code,
)
from pydantic import BaseModel
from starlette import routing
from starlette._exception_handler import wrap_app_handling_exceptions
from starlette._utils import is_async_callable
@ -153,7 +153,7 @@ def _prepare_response_content(
exclude_defaults: bool = False,
exclude_none: bool = False,
) -> Any:
if isinstance(res, BaseModel):
if isinstance(res, may_v1.BaseModel):
read_with_orm_mode = getattr(_get_model_config(res), "read_with_orm_mode", None)
if read_with_orm_mode:
# Let from_orm extract the data from this model instead of converting

10
fastapi/utils.py

@ -13,7 +13,6 @@ from weakref import WeakKeyDictionary
import fastapi
from fastapi._compat import (
PYDANTIC_V2,
BaseConfig,
ModelField,
PydanticSchemaGenerationError,
@ -29,6 +28,8 @@ from pydantic import BaseModel
from pydantic.fields import FieldInfo
from typing_extensions import Literal
from ._compat import v2
if TYPE_CHECKING: # pragma: nocover
from .routing import APIRoute
@ -110,9 +111,7 @@ def create_model_field(
raise fastapi.exceptions.FastAPIError(
_invalid_args_message.format(type_=type_)
) from None
elif PYDANTIC_V2:
from ._compat import v2
else:
field_info = field_info or FieldInfo(
annotation=type_, default=default, alias=alias
)
@ -140,9 +139,6 @@ def create_cloned_field(
*,
cloned_types: Optional[MutableMapping[type[BaseModel], type[BaseModel]]] = None,
) -> ModelField:
if PYDANTIC_V2:
from ._compat import v2
if isinstance(field, v2.ModelField):
return field

6
tests/test_additional_properties_bool.py

@ -2,18 +2,12 @@ from typing import Union
from dirty_equals import IsDict
from fastapi import FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, ConfigDict
class FooBaseModel(BaseModel):
if PYDANTIC_V2:
model_config = ConfigDict(extra="forbid")
else:
class Config:
extra = "forbid"
class Foo(FooBaseModel):

2
tests/test_ambiguous_params.py

@ -4,7 +4,6 @@ import pytest
from fastapi import Depends, FastAPI, Path
from fastapi.param_functions import Query
from fastapi.testclient import TestClient
from fastapi.utils import PYDANTIC_V2
app = FastAPI()
@ -71,6 +70,5 @@ def test_multiple_annotations():
response = client.get("/multi-query", params={"foo": "123"})
assert response.status_code == 422
if PYDANTIC_V2:
response = client.get("/multi-query", params={"foo": "1"})
assert response.status_code == 422

16
tests/test_custom_schema_fields.py

@ -1,12 +1,8 @@
from typing import Annotated, Optional
from fastapi import FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel
if PYDANTIC_V2:
from pydantic import WithJsonSchema
from pydantic import BaseModel, WithJsonSchema
app = FastAPI()
@ -14,7 +10,6 @@ app = FastAPI()
class Item(BaseModel):
name: str
if PYDANTIC_V2:
description: Annotated[
Optional[str], WithJsonSchema({"type": ["string", "null"]})
] = None
@ -24,13 +19,6 @@ class Item(BaseModel):
"x-something-internal": {"level": 4},
}
}
else:
description: Optional[str] = None # type: ignore[no-redef]
class Config:
schema_extra = {
"x-something-internal": {"level": 4},
}
@app.get("/foo", response_model=Item)
@ -55,7 +43,7 @@ item_schema = {
},
"description": {
"title": "Description",
"type": ["string", "null"] if PYDANTIC_V2 else "string",
"type": ["string", "null"],
},
},
}

6
tests/test_forms_single_model.py

@ -2,7 +2,6 @@ from typing import Annotated, Optional
from dirty_equals import IsDict
from fastapi import FastAPI, Form
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
@ -20,12 +19,7 @@ class FormModel(BaseModel):
class FormModelExtraAllow(BaseModel):
param: str
if PYDANTIC_V2:
model_config = {"extra": "allow"}
else:
class Config:
extra = "allow"
@app.post("/form/")

25
tests/test_jsonable_encoder.py

@ -8,7 +8,7 @@ from pathlib import PurePath, PurePosixPath, PureWindowsPath
from typing import Optional
import pytest
from fastapi._compat import PYDANTIC_V2, Undefined
from fastapi._compat import Undefined
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel, Field, ValidationError
@ -59,12 +59,7 @@ class RoleEnum(Enum):
class ModelWithConfig(BaseModel):
role: Optional[RoleEnum] = None
if PYDANTIC_V2:
model_config = {"use_enum_values": True}
else:
class Config:
use_enum_values = True
class ModelWithAlias(BaseModel):
@ -154,6 +149,7 @@ def test_encode_custom_json_encoders_model_pydanticv2():
@needs_pydanticv1
def test_encode_custom_json_encoders_model_pydanticv1():
from pydantic import v1
class ModelWithCustomEncoder(v1.BaseModel):
dt_field: datetime
@ -210,6 +206,7 @@ def test_encode_model_with_default():
@needs_pydanticv1
def test_custom_encoders():
from pydantic import v1
class safe_datetime(datetime):
pass
@ -246,12 +243,7 @@ def test_encode_model_with_pure_path():
class ModelWithPath(BaseModel):
path: PurePath
if PYDANTIC_V2:
model_config = {"arbitrary_types_allowed": True}
else:
class Config:
arbitrary_types_allowed = True
test_path = PurePath("/foo", "bar")
obj = ModelWithPath(path=test_path)
@ -262,12 +254,7 @@ def test_encode_model_with_pure_posix_path():
class ModelWithPath(BaseModel):
path: PurePosixPath
if PYDANTIC_V2:
model_config = {"arbitrary_types_allowed": True}
else:
class Config:
arbitrary_types_allowed = True
obj = ModelWithPath(path=PurePosixPath("/foo", "bar"))
assert jsonable_encoder(obj) == {"path": "/foo/bar"}
@ -277,12 +264,7 @@ def test_encode_model_with_pure_windows_path():
class ModelWithPath(BaseModel):
path: PureWindowsPath
if PYDANTIC_V2:
model_config = {"arbitrary_types_allowed": True}
else:
class Config:
arbitrary_types_allowed = True
obj = ModelWithPath(path=PureWindowsPath("/foo", "bar"))
assert jsonable_encoder(obj) == {"path": "\\foo\\bar"}
@ -291,6 +273,7 @@ def test_encode_model_with_pure_windows_path():
@needs_pydanticv1
def test_encode_root():
from pydantic import v1
class ModelWithRoot(v1.BaseModel):
__root__: str

11
tests/test_openapi_separate_input_output_schemas.py

@ -3,16 +3,15 @@ from typing import Optional
from fastapi import FastAPI
from fastapi.testclient import TestClient
from inline_snapshot import snapshot
from pydantic import BaseModel
from pydantic import BaseModel, computed_field
from .utils import PYDANTIC_V2, needs_pydanticv2
from .utils import needs_pydanticv2
class SubItem(BaseModel):
subname: str
sub_description: Optional[str] = None
tags: list[str] = []
if PYDANTIC_V2:
model_config = {"json_schema_serialization_defaults_required": True}
@ -20,13 +19,9 @@ class Item(BaseModel):
name: str
description: Optional[str] = None
sub: Optional[SubItem] = None
if PYDANTIC_V2:
model_config = {"json_schema_serialization_defaults_required": True}
if PYDANTIC_V2:
from pydantic import computed_field
class WithComputedField(BaseModel):
name: str
@ -58,8 +53,6 @@ def get_app_client(separate_input_output_schemas: bool = True) -> TestClient:
Item(name="Plumbus"),
]
if PYDANTIC_V2:
@app.post("/with-computed-field/")
def create_with_computed_field(
with_computed_field: WithComputedField,

6
tests/test_query_cookie_header_model_extra_params.py

@ -1,5 +1,4 @@
from fastapi import Cookie, FastAPI, Header, Query
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel
@ -9,12 +8,7 @@ app = FastAPI()
class Model(BaseModel):
param: str
if PYDANTIC_V2:
model_config = {"extra": "allow"}
else:
class Config:
extra = "allow"
@app.get("/query")

4
tests/test_request_param_model_by_alias.py

@ -1,6 +1,5 @@
from dirty_equals import IsPartialDict
from fastapi import Cookie, FastAPI, Header, Query
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
@ -53,7 +52,6 @@ def test_query_model_with_alias_by_name():
response = client.get("/query", params={"param": "value"})
assert response.status_code == 422, response.text
details = response.json()
if PYDANTIC_V2:
assert details["detail"][0]["input"] == {"param": "value"}
@ -62,7 +60,6 @@ def test_header_model_with_alias_by_name():
response = client.get("/header", headers={"param": "value"})
assert response.status_code == 422, response.text
details = response.json()
if PYDANTIC_V2:
assert details["detail"][0]["input"] == IsPartialDict({"param": "value"})
@ -72,5 +69,4 @@ def test_cookie_model_with_alias_by_name():
response = client.get("/cookie")
assert response.status_code == 422, response.text
details = response.json()
if PYDANTIC_V2:
assert details["detail"][0]["input"] == {"param": "value"}

11
tests/test_response_by_alias.py

@ -1,5 +1,4 @@
from fastapi import FastAPI
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, ConfigDict, Field
@ -13,7 +12,6 @@ class Model(BaseModel):
class ModelNoAlias(BaseModel):
name: str
if PYDANTIC_V2:
model_config = ConfigDict(
json_schema_extra={
"description": (
@ -22,15 +20,6 @@ class ModelNoAlias(BaseModel):
)
}
)
else:
class Config:
schema_extra = {
"description": (
"response_model_by_alias=False is basically a quick hack, to support "
"proper OpenAPI use another model with the correct field names"
)
}
@app.get("/dict", response_model=Model, response_model_by_alias=False)

6
tests/test_schema_extra_examples.py

@ -3,7 +3,6 @@ from typing import Union
import pytest
from dirty_equals import IsDict
from fastapi import Body, Cookie, FastAPI, Header, Path, Query
from fastapi._compat import PYDANTIC_V2
from fastapi.testclient import TestClient
from pydantic import BaseModel, ConfigDict
@ -14,14 +13,9 @@ def create_app():
class Item(BaseModel):
data: str
if PYDANTIC_V2:
model_config = ConfigDict(
json_schema_extra={"example": {"data": "Data in schema_extra"}}
)
else:
class Config:
schema_extra = {"example": {"data": "Data in schema_extra"}}
@app.post("/schema_extra/")
def schema_extra(item: Item):

6
tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial001.py

@ -2,7 +2,6 @@ import sys
from typing import Any
import pytest
from fastapi._compat import PYDANTIC_V2
from tests.utils import skip_module_if_py_gte_314
@ -10,13 +9,8 @@ if sys.version_info >= (3, 14):
skip_module_if_py_gte_314()
if not PYDANTIC_V2:
pytest.skip("This test is only for Pydantic v2", allow_module_level=True)
import importlib
import pytest
from ...utils import needs_py310

5
tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial002.py

@ -1,7 +1,6 @@
import sys
import pytest
from fastapi._compat import PYDANTIC_V2
from inline_snapshot import snapshot
from tests.utils import skip_module_if_py_gte_314
@ -10,12 +9,8 @@ if sys.version_info >= (3, 14):
skip_module_if_py_gte_314()
if not PYDANTIC_V2:
pytest.skip("This test is only for Pydantic v2", allow_module_level=True)
import importlib
import pytest
from fastapi.testclient import TestClient
from ...utils import needs_py310

4
tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial003.py

@ -1,7 +1,6 @@
import sys
import pytest
from fastapi._compat import PYDANTIC_V2
from inline_snapshot import snapshot
from tests.utils import skip_module_if_py_gte_314
@ -9,9 +8,6 @@ from tests.utils import skip_module_if_py_gte_314
if sys.version_info >= (3, 14):
skip_module_if_py_gte_314()
if not PYDANTIC_V2:
pytest.skip("This test is only for Pydantic v2", allow_module_level=True)
import importlib

4
tests/test_tutorial/test_pydantic_v1_in_v2/test_tutorial004.py

@ -1,7 +1,6 @@
import sys
import pytest
from fastapi._compat import PYDANTIC_V2
from inline_snapshot import snapshot
from tests.utils import skip_module_if_py_gte_314
@ -9,9 +8,6 @@ from tests.utils import skip_module_if_py_gte_314
if sys.version_info >= (3, 14):
skip_module_if_py_gte_314()
if not PYDANTIC_V2:
pytest.skip("This test is only for Pydantic v2", allow_module_level=True)
import importlib

5
tests/test_validate_response_recursive/app.py

@ -1,5 +1,4 @@
from fastapi import FastAPI
from fastapi._compat import PYDANTIC_V2
from pydantic import BaseModel
app = FastAPI()
@ -20,13 +19,9 @@ class RecursiveItemViaSubmodel(BaseModel):
name: str
if PYDANTIC_V2:
RecursiveItem.model_rebuild()
RecursiveSubitemInSubmodel.model_rebuild()
RecursiveItemViaSubmodel.model_rebuild()
else:
RecursiveItem.update_forward_refs()
RecursiveSubitemInSubmodel.update_forward_refs()
@app.get("/items/recursive", response_model=RecursiveItem)

Loading…
Cancel
Save