diff --git a/ampel/base/AmpelBaseModel.py b/ampel/base/AmpelBaseModel.py index 40c5e682..f4ab0387 100644 --- a/ampel/base/AmpelBaseModel.py +++ b/ampel/base/AmpelBaseModel.py @@ -90,6 +90,7 @@ def dict( exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, + warnings: bool = True, ) -> dict[str, Any]: return self.model_dump( include=include, @@ -97,5 +98,6 @@ def dict( by_alias=by_alias, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, - exclude_none=exclude_none + exclude_none=exclude_none, + warnings=warnings, ) diff --git a/pyproject.toml b/pyproject.toml index 1cafb692..42c73577 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,8 +45,6 @@ profile = "black" minversion = "6.0" filterwarnings = [ "error", - # Expected `list[definition-ref]` but got `tuple` - serialized value may not be as expected - "ignore:Pydantic serializer warnings:UserWarning", ] [tool.mypy] diff --git a/tests/test_AmpelBaseModel.py b/tests/test_AmpelBaseModel.py index 915f44a7..38d58617 100644 --- a/tests/test_AmpelBaseModel.py +++ b/tests/test_AmpelBaseModel.py @@ -1,3 +1,5 @@ +import re +from collections.abc import Sequence from types import UnionType from typing import Generic, TypeVar, Union, get_origin @@ -122,3 +124,31 @@ class Base(AmpelUnit): class Derived(Base, AmpelBaseModel): ... assert Base().dict() == {"base": 1} + + +def test_serialize_sequence_field_with_tuple(): + class M(AmpelBaseModel): + a: Sequence[int] + + model = M(a=(1,)) + assert model.dict(warnings=False) == {"a": (1,)} + + # We sometimes use `Sequence[T]` as shorthand for `list[T] | tuple[T]`, but + # pydantic uses a list serializer for all Sequence types and complains when + # it sees a tuple. We suppress these warnings by setting warnings=False by + # default, and rely on validators to ensure that our model fields have the + # correct types. See https://github.com/pydantic/pydantic-core/issues/133 + # for a discussion of why pydantic doesn't directly support Sequence. + with pytest.warns( + UserWarning, + match=re.escape( + "Pydantic serializer warnings:\n Expected `list[int]` but got `tuple` - serialized value may not be as expected" + ), + ): + assert model.dict(warnings=True) == {"a": (1,)} + + class M(AmpelBaseModel): + a: list[int] | tuple[int, ...] + + model = M(a=(1,)) + assert model.dict(warnings=True) == {"a": (1,)}