From de015ae49d333f112b5cd03f7945832c7754ca17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 26 Dec 2025 20:14:39 +0100 Subject: [PATCH] =?UTF-8?q?=F0=9F=94=A5=20Remove=20and=20update=20Pydantic?= =?UTF-8?q?=20v1=20tests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/test_compat.py | 49 +---------------------- tests/test_inherited_custom_class.py | 45 --------------------- tests/test_jsonable_encoder.py | 58 ++++------------------------ tests/test_pydantic_v1_error.py | 2 +- 4 files changed, 10 insertions(+), 144 deletions(-) diff --git a/tests/test_compat.py b/tests/test_compat.py index 8d2071030..944977f18 100644 --- a/tests/test_compat.py +++ b/tests/test_compat.py @@ -1,20 +1,17 @@ -from typing import Any, Union +from typing import Union from fastapi import FastAPI, UploadFile from fastapi._compat import ( Undefined, _get_model_config, - get_cached_model_fields, - is_scalar_field, is_uploadfile_sequence_annotation, - may_v1, ) from fastapi._compat.shared import is_bytes_sequence_annotation from fastapi.testclient import TestClient from pydantic import BaseModel, ConfigDict 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(): @@ -26,18 +23,6 @@ def test_model_field_default_required(): 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(): # For coverage 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"]) assert result == ["x", "y"] 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 diff --git a/tests/test_inherited_custom_class.py b/tests/test_inherited_custom_class.py index 7f29fe33e..8cf8952f9 100644 --- a/tests/test_inherited_custom_class.py +++ b/tests/test_inherited_custom_class.py @@ -5,8 +5,6 @@ from fastapi import FastAPI from fastapi.testclient import TestClient from pydantic import BaseModel -from .utils import needs_pydanticv1 - class MyUuid: def __init__(self, uuid_string: str): @@ -67,46 +65,3 @@ def test_pydanticv2(): assert response_pydantic.json() == { "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" - } diff --git a/tests/test_jsonable_encoder.py b/tests/test_jsonable_encoder.py index 81bf94ece..c61dede8e 100644 --- a/tests/test_jsonable_encoder.py +++ b/tests/test_jsonable_encoder.py @@ -10,6 +10,7 @@ from typing import Optional import pytest from fastapi._compat import Undefined from fastapi.encoders import jsonable_encoder +from fastapi.exceptions import PydanticV1NotSupportedError from pydantic import BaseModel, Field, ValidationError 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"} -# TODO: remove when deprecating Pydantic v1 @needs_pydanticv1 -def test_encode_custom_json_encoders_model_pydanticv1(): +def test_json_encoder_error_with_pydanticv1(): from pydantic import v1 - class ModelWithCustomEncoder(v1.BaseModel): - dt_field: datetime - - class Config: - json_encoders = { - datetime: lambda dt: dt.replace( - microsecond=0, tzinfo=timezone.utc - ).isoformat() - } + class ModelV1(v1.BaseModel): + name: str - class ModelWithCustomEncoderSubclass(ModelWithCustomEncoder): - class Config: - pass - - 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"} + data = ModelV1(name="test") + with pytest.raises(PydanticV1NotSupportedError): + jsonable_encoder(data) 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 custom_enum_encoder(v: Enum): return v.value.lower() @@ -287,17 +254,6 @@ def test_encode_pure_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(): data = {"value": Decimal(1.23)} assert jsonable_encoder(data) == {"value": 1.23} diff --git a/tests/test_pydantic_v1_error.py b/tests/test_pydantic_v1_error.py index 89f20cafb..2b574bac7 100644 --- a/tests/test_pydantic_v1_error.py +++ b/tests/test_pydantic_v1_error.py @@ -8,7 +8,7 @@ if sys.version_info >= (3, 14): skip_module_if_py_gte_314() from fastapi import FastAPI -from fastapi._compat.v1 import BaseModel +from fastapi._compat.may_v1 import BaseModel from fastapi.exceptions import PydanticV1NotSupportedError