Browse Source

🔥 Remove and update Pydantic v1 tests

pull/14609/head
Sebastián Ramírez 7 months ago
parent
commit
de015ae49d
  1. 49
      tests/test_compat.py
  2. 45
      tests/test_inherited_custom_class.py
  3. 58
      tests/test_jsonable_encoder.py
  4. 2
      tests/test_pydantic_v1_error.py

49
tests/test_compat.py

@ -1,20 +1,17 @@
from typing import Any, Union from typing import Union
from fastapi import FastAPI, UploadFile from fastapi import FastAPI, UploadFile
from fastapi._compat import ( from fastapi._compat import (
Undefined, Undefined,
_get_model_config, _get_model_config,
get_cached_model_fields,
is_scalar_field,
is_uploadfile_sequence_annotation, is_uploadfile_sequence_annotation,
may_v1,
) )
from fastapi._compat.shared import is_bytes_sequence_annotation from fastapi._compat.shared import is_bytes_sequence_annotation
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pydantic import BaseModel, ConfigDict from pydantic import BaseModel, ConfigDict
from pydantic.fields import FieldInfo from pydantic.fields import FieldInfo
from .utils import needs_py310, needs_py_lt_314 from .utils import needs_py310
def test_model_field_default_required(): def test_model_field_default_required():
@ -26,18 +23,6 @@ def test_model_field_default_required():
assert field.default is Undefined assert field.default is Undefined
@needs_py_lt_314
def test_v1_plain_validator_function():
from fastapi._compat import v1
# For coverage
def func(v): # pragma: no cover
return v
result = v1.with_info_plain_validator_function(func)
assert result == {}
def test_is_model_field(): def test_is_model_field():
# For coverage # For coverage
from fastapi._compat import _is_model_field from fastapi._compat import _is_model_field
@ -165,33 +150,3 @@ def test_serialize_sequence_value_with_none_first_in_union():
result = v2.serialize_sequence_value(field=field, value=["x", "y"]) result = v2.serialize_sequence_value(field=field, value=["x", "y"])
assert result == ["x", "y"] assert result == ["x", "y"]
assert isinstance(result, list) assert isinstance(result, list)
@needs_py_lt_314
def test_is_pv1_scalar_field():
from fastapi._compat import v1
# For coverage
class Model(v1.BaseModel):
foo: Union[str, dict[str, Any]]
fields = v1.get_model_fields(Model)
assert not is_scalar_field(fields[0])
@needs_py_lt_314
def test_get_model_fields_cached():
from fastapi._compat import v1
class Model(may_v1.BaseModel):
foo: str
non_cached_fields = v1.get_model_fields(Model)
non_cached_fields2 = v1.get_model_fields(Model)
cached_fields = get_cached_model_fields(Model)
cached_fields2 = get_cached_model_fields(Model)
for f1, f2 in zip(cached_fields, cached_fields2):
assert f1 is f2
assert non_cached_fields is not non_cached_fields2
assert cached_fields is cached_fields2

45
tests/test_inherited_custom_class.py

@ -5,8 +5,6 @@ from fastapi import FastAPI
from fastapi.testclient import TestClient from fastapi.testclient import TestClient
from pydantic import BaseModel from pydantic import BaseModel
from .utils import needs_pydanticv1
class MyUuid: class MyUuid:
def __init__(self, uuid_string: str): def __init__(self, uuid_string: str):
@ -67,46 +65,3 @@ def test_pydanticv2():
assert response_pydantic.json() == { assert response_pydantic.json() == {
"a_uuid": "b8799909-f914-42de-91bc-95c819218d01" "a_uuid": "b8799909-f914-42de-91bc-95c819218d01"
} }
# TODO: remove when deprecating Pydantic v1
@needs_pydanticv1
def test_pydanticv1():
from pydantic import v1
app = FastAPI()
@app.get("/fast_uuid")
def return_fast_uuid():
asyncpg_uuid = MyUuid("a10ff360-3b1e-4984-a26f-d3ab460bdb51")
assert isinstance(asyncpg_uuid, uuid.UUID)
assert type(asyncpg_uuid) is not uuid.UUID
with pytest.raises(TypeError):
vars(asyncpg_uuid)
return {"fast_uuid": asyncpg_uuid}
class SomeCustomClass(v1.BaseModel):
class Config:
arbitrary_types_allowed = True
json_encoders = {uuid.UUID: str}
a_uuid: MyUuid
@app.get("/get_custom_class")
def return_some_user():
# Test that the fix also works for custom pydantic classes
return SomeCustomClass(a_uuid=MyUuid("b8799909-f914-42de-91bc-95c819218d01"))
client = TestClient(app)
with client:
response_simple = client.get("/fast_uuid")
response_pydantic = client.get("/get_custom_class")
assert response_simple.json() == {
"fast_uuid": "a10ff360-3b1e-4984-a26f-d3ab460bdb51"
}
assert response_pydantic.json() == {
"a_uuid": "b8799909-f914-42de-91bc-95c819218d01"
}

58
tests/test_jsonable_encoder.py

@ -10,6 +10,7 @@ from typing import Optional
import pytest import pytest
from fastapi._compat import Undefined from fastapi._compat import Undefined
from fastapi.encoders import jsonable_encoder from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import PydanticV1NotSupportedError
from pydantic import BaseModel, Field, ValidationError from pydantic import BaseModel, Field, ValidationError
from .utils import needs_pydanticv1 from .utils import needs_pydanticv1
@ -156,29 +157,16 @@ def test_encode_custom_json_encoders_model_pydanticv2():
assert jsonable_encoder(subclass_model) == {"dt_field": "2019-01-01T08:00:00+00:00"} assert jsonable_encoder(subclass_model) == {"dt_field": "2019-01-01T08:00:00+00:00"}
# TODO: remove when deprecating Pydantic v1
@needs_pydanticv1 @needs_pydanticv1
def test_encode_custom_json_encoders_model_pydanticv1(): def test_json_encoder_error_with_pydanticv1():
from pydantic import v1 from pydantic import v1
class ModelWithCustomEncoder(v1.BaseModel): class ModelV1(v1.BaseModel):
dt_field: datetime name: str
class Config:
json_encoders = {
datetime: lambda dt: dt.replace(
microsecond=0, tzinfo=timezone.utc
).isoformat()
}
class ModelWithCustomEncoderSubclass(ModelWithCustomEncoder): data = ModelV1(name="test")
class Config: with pytest.raises(PydanticV1NotSupportedError):
pass jsonable_encoder(data)
model = ModelWithCustomEncoder(dt_field=datetime(2019, 1, 1, 8))
assert jsonable_encoder(model) == {"dt_field": "2019-01-01T08:00:00+00:00"}
subclass_model = ModelWithCustomEncoderSubclass(dt_field=datetime(2019, 1, 1, 8))
assert jsonable_encoder(subclass_model) == {"dt_field": "2019-01-01T08:00:00+00:00"}
def test_encode_model_with_config(): def test_encode_model_with_config():
@ -214,27 +202,6 @@ def test_encode_model_with_default():
} }
@needs_pydanticv1
def test_custom_encoders():
from pydantic import v1
class safe_datetime(datetime):
pass
class MyModel(v1.BaseModel):
dt_field: safe_datetime
instance = MyModel(dt_field=safe_datetime.now())
encoded_instance = jsonable_encoder(
instance, custom_encoder={safe_datetime: lambda o: o.strftime("%H:%M:%S")}
)
assert encoded_instance["dt_field"] == instance.dt_field.strftime("%H:%M:%S")
encoded_instance2 = jsonable_encoder(instance)
assert encoded_instance2["dt_field"] == instance.dt_field.isoformat()
def test_custom_enum_encoders(): def test_custom_enum_encoders():
def custom_enum_encoder(v: Enum): def custom_enum_encoder(v: Enum):
return v.value.lower() return v.value.lower()
@ -287,17 +254,6 @@ def test_encode_pure_path():
assert jsonable_encoder({"path": test_path}) == {"path": str(test_path)} assert jsonable_encoder({"path": test_path}) == {"path": str(test_path)}
@needs_pydanticv1
def test_encode_root():
from pydantic import v1
class ModelWithRoot(v1.BaseModel):
__root__: str
model = ModelWithRoot(__root__="Foo")
assert jsonable_encoder(model) == "Foo"
def test_decimal_encoder_float(): def test_decimal_encoder_float():
data = {"value": Decimal(1.23)} data = {"value": Decimal(1.23)}
assert jsonable_encoder(data) == {"value": 1.23} assert jsonable_encoder(data) == {"value": 1.23}

2
tests/test_pydantic_v1_error.py

@ -8,7 +8,7 @@ if sys.version_info >= (3, 14):
skip_module_if_py_gte_314() skip_module_if_py_gte_314()
from fastapi import FastAPI from fastapi import FastAPI
from fastapi._compat.v1 import BaseModel from fastapi._compat.may_v1 import BaseModel
from fastapi.exceptions import PydanticV1NotSupportedError from fastapi.exceptions import PydanticV1NotSupportedError

Loading…
Cancel
Save