|
7 | 7 | import json |
8 | 8 | import threading |
9 | 9 | import time |
| 10 | +import warnings |
10 | 11 | from decimal import Decimal |
11 | 12 | from enum import Enum |
12 | 13 | from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network |
@@ -417,6 +418,62 @@ def __init__(self) -> None: |
417 | 418 | assert schema_dump(AttrsLike()) == {"name": "Ada"} |
418 | 419 |
|
419 | 420 |
|
| 421 | +@pytest.mark.parametrize( |
| 422 | + "value", |
| 423 | + [ |
| 424 | + None, |
| 425 | + 1, |
| 426 | + [1, 2], |
| 427 | + {}, |
| 428 | + ], |
| 429 | +) |
| 430 | +def test_has_dict_attribute_rejects_values_without_instance_dict(value: Any) -> None: |
| 431 | + assert not serialization.has_dict_attribute(value) |
| 432 | + |
| 433 | + |
| 434 | +def test_has_dict_attribute_accepts_plain_object() -> None: |
| 435 | + class PlainObject: |
| 436 | + def __init__(self) -> None: |
| 437 | + self.name = "Ada" |
| 438 | + |
| 439 | + assert serialization.has_dict_attribute(PlainObject()) |
| 440 | + |
| 441 | + |
| 442 | +def test_has_dict_attribute_rejects_slots_only_object() -> None: |
| 443 | + class SlotsOnlyObject: |
| 444 | + __slots__ = ("name",) |
| 445 | + |
| 446 | + def __init__(self) -> None: |
| 447 | + self.name = "Ada" |
| 448 | + |
| 449 | + assert not serialization.has_dict_attribute(SlotsOnlyObject()) |
| 450 | + |
| 451 | + |
| 452 | +def test_schema_dump_uses_instance_dict_fallback_for_plain_objects() -> None: |
| 453 | + class PlainObject: |
| 454 | + def __init__(self) -> None: |
| 455 | + self.name = "Ada" |
| 456 | + |
| 457 | + assert schema_dump(PlainObject()) == {"name": "Ada"} |
| 458 | + |
| 459 | + |
| 460 | +def test_schema_dump_passes_through_slots_only_objects() -> None: |
| 461 | + class SlotsOnlyObject: |
| 462 | + __slots__ = ("name",) |
| 463 | + |
| 464 | + def __init__(self) -> None: |
| 465 | + self.name = "Ada" |
| 466 | + |
| 467 | + value = SlotsOnlyObject() |
| 468 | + result: Any = schema_dump(value) |
| 469 | + |
| 470 | + assert result is value |
| 471 | + |
| 472 | + |
| 473 | +def test_serialization_module_does_not_import_public_dict_protocol() -> None: |
| 474 | + assert "DictProtocol" not in vars(serialization) |
| 475 | + |
| 476 | + |
420 | 477 | @pytest.mark.parametrize( |
421 | 478 | ("kind", "value", "expected"), |
422 | 479 | [ |
@@ -978,14 +1035,34 @@ def test_legacy_service_typing_shim_emits_deprecation_warning(name: str, new_mod |
978 | 1035 | assert value is canonical |
979 | 1036 |
|
980 | 1037 |
|
| 1038 | +def test_typing_dict_protocol_emits_deprecation_warning() -> None: |
| 1039 | + with pytest.warns(DeprecationWarning, match=r"DictProtocol.*1\.11\.0.*removed in 2\.0\.0"): |
| 1040 | + value = _import_attr("advanced_alchemy.typing", "DictProtocol") |
| 1041 | + |
| 1042 | + assert value is _import_attr("advanced_alchemy._typing", "DictProtocol") |
| 1043 | + |
| 1044 | + |
| 1045 | +def test_typing_canonical_exports_do_not_emit_deprecation_warning() -> None: |
| 1046 | + typing_module = importlib.import_module("advanced_alchemy.typing") |
| 1047 | + |
| 1048 | + with warnings.catch_warnings(): |
| 1049 | + warnings.simplefilter("error") |
| 1050 | + assert getattr(typing_module, "PYDANTIC_INSTALLED") is PYDANTIC_INSTALLED |
| 1051 | + |
| 1052 | + |
| 1053 | +def test_legacy_service_typing_dict_protocol_remains_compatible() -> None: |
| 1054 | + with pytest.warns(DeprecationWarning, match="DictProtocol"): |
| 1055 | + value = _import_attr("advanced_alchemy.service.typing", "DictProtocol") |
| 1056 | + |
| 1057 | + assert value is _import_attr("advanced_alchemy._typing", "DictProtocol") |
| 1058 | + |
| 1059 | + |
981 | 1060 | def test_legacy_service_typing_shim_unknown_attribute_raises() -> None: |
982 | 1061 | with pytest.raises(AttributeError, match="not_a_real_name"): |
983 | 1062 | _import_attr("advanced_alchemy.service.typing", "not_a_real_name") |
984 | 1063 |
|
985 | 1064 |
|
986 | 1065 | def test_legacy_service_typing_shim_keeps_pydantic_use_failfast() -> None: |
987 | | - import warnings |
988 | | - |
989 | 1066 | import advanced_alchemy.service.typing as shim |
990 | 1067 |
|
991 | 1068 | with warnings.catch_warnings(): |
|
0 commit comments