Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions task-sdk/src/airflow/sdk/serde/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,12 @@ def serialize(o: object, depth: int = 0) -> U | None:
dct[DATA] = serialize(data, depth + 1)
return dct

raise TypeError(f"cannot serialize object of type {cls}")
raise TypeError(
f"Cannot serialize object of type {cls}. Give it a `serialize()` method and a "
"`deserialize(data, version)` staticmethod, or decorate the class with @dataclass or "
"@attr.define. "
"See: https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/serializers.html"
)


def deserialize(o: T | None, full=True, type_hint: Any = None) -> object:
Expand Down Expand Up @@ -387,7 +392,11 @@ def deserialize(o: T | None, full=True, type_hint: Any = None) -> object:
return cls(**deserialize_value) # type: ignore[operator]

# no deserializer available
raise TypeError(f"No deserializer found for {classname}")
raise TypeError(
f"No deserializer found for {classname}. It must provide a `deserialize(data, version)` "
"staticmethod (matching how it serializes), or be decorated with @dataclass or @attr.define. "
"See: https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/serializers.html"
)


def _convert(old: dict) -> dict:
Expand Down
18 changes: 14 additions & 4 deletions task-sdk/tests/task_sdk/serde/test_serde.py
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ def test_ser_namedtuple(self):

def test_no_serializer(self):
i = Exception
with pytest.raises(TypeError, match="^cannot serialize"):
with pytest.raises(TypeError, match="^Cannot serialize"):
serialize(i)

def test_ser_registered(self):
Expand Down Expand Up @@ -523,8 +523,13 @@ def test_raise_undeserializable(self):
"__version__": 0,
}
)
with pytest.raises(TypeError, match="No deserializer"):
with pytest.raises(TypeError, match="No deserializer") as exc_info:
deserialize(data)
assert (
"It must provide a `deserialize(data, version)` staticmethod (matching how it "
"serializes), or be decorated with @dataclass or @attr.define." in str(exc_info.value)
)
assert "authoring-and-scheduling/serializers.html" in str(exc_info.value)

def test_backwards_compat(self):
"""
Expand Down Expand Up @@ -645,6 +650,11 @@ def test_error_when_serializing_callable_without_name(self):
i = C()
with pytest.raises(
TypeError,
match="cannot serialize object of type <class 'tests.task_sdk.serde.test_serde.C'>",
):
match="Cannot serialize object of type <class 'tests.task_sdk.serde.test_serde.C'>",
) as exc_info:
serialize(i)
assert (
"Give it a `serialize()` method and a `deserialize(data, version)` staticmethod, or "
"decorate the class with @dataclass or @attr.define." in str(exc_info.value)
)
assert "authoring-and-scheduling/serializers.html" in str(exc_info.value)
Loading