This is an automated email from the ASF dual-hosted git repository.
rahulvats pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git
The following commit(s) were added to refs/heads/main by this push:
new 7531ca19788 Fix serde deserialization of old-format builtin types in
trigger kwargs (#64615)
7531ca19788 is described below
commit 7531ca197884108cb021c26afad1c0264534c5aa
Author: Jason(Zhe-You) Liu <[email protected]>
AuthorDate: Thu Apr 2 18:09:43 2026 +0800
Fix serde deserialization of old-format builtin types in trigger kwargs
(#64615)
* Fix serde deserialization of old-format builtin types in trigger kwargs
---
.../src/airflow_shared/serialization/__init__.py | 7 +++++
task-sdk/src/airflow/sdk/serde/__init__.py | 7 ++++-
task-sdk/tests/task_sdk/serde/test_serde.py | 32 ++++++++++++++++++++++
3 files changed, 45 insertions(+), 1 deletion(-)
diff --git a/shared/serialization/src/airflow_shared/serialization/__init__.py
b/shared/serialization/src/airflow_shared/serialization/__init__.py
index a326d330ee3..824848dc679 100644
--- a/shared/serialization/src/airflow_shared/serialization/__init__.py
+++ b/shared/serialization/src/airflow_shared/serialization/__init__.py
@@ -41,3 +41,10 @@ FORBIDDEN_XCOM_KEYS = frozenset(
OLD_DATA,
}
)
+
+OLD_TYPE_TO_FULL_QUALNAME: dict[str, str] = {
+ "tuple": "builtins.tuple",
+ "set": "builtins.set",
+ "frozenset": "builtins.frozenset",
+ "timedelta": "datetime.timedelta",
+}
diff --git a/task-sdk/src/airflow/sdk/serde/__init__.py
b/task-sdk/src/airflow/sdk/serde/__init__.py
index 9a216d2f11c..63ab513f341 100644
--- a/task-sdk/src/airflow/sdk/serde/__init__.py
+++ b/task-sdk/src/airflow/sdk/serde/__init__.py
@@ -37,6 +37,7 @@ from airflow.sdk._shared.serialization import (
OLD_DATA,
OLD_DICT,
OLD_TYPE,
+ OLD_TYPE_TO_FULL_QUALNAME,
SCHEMA_ID,
VERSION,
)
@@ -307,7 +308,11 @@ def _convert(old: dict) -> dict:
# Return old style dicts directly as they do not need wrapping
if old[OLD_TYPE] == OLD_DICT:
return old[OLD_DATA]
- return {CLASSNAME: old[OLD_TYPE], VERSION: DEFAULT_VERSION, DATA:
old[OLD_DATA]}
+ return {
+ CLASSNAME: OLD_TYPE_TO_FULL_QUALNAME.get(old[OLD_TYPE],
old[OLD_TYPE]),
+ VERSION: DEFAULT_VERSION,
+ DATA: old[OLD_DATA],
+ }
return old
diff --git a/task-sdk/tests/task_sdk/serde/test_serde.py
b/task-sdk/tests/task_sdk/serde/test_serde.py
index e74d1fd5d0e..f264164ffca 100644
--- a/task-sdk/tests/task_sdk/serde/test_serde.py
+++ b/task-sdk/tests/task_sdk/serde/test_serde.py
@@ -447,6 +447,38 @@ class TestSerDe:
e = deserialize(i)
assert e["extra"] == {"hi": "bye"}
+ @pytest.mark.parametrize(
+ ("old_type", "expected"),
+ [
+ ("tuple", (1, 2, 3)),
+ ("set", {1, 2, 3}),
+ ("frozenset", frozenset([1, 2, 3])),
+ ],
+ )
+ def test_backwards_compat_builtin_collections(self, old_type, expected):
+ """Verify deserialization of old-style builtin collections
(tuple/set/frozenset)."""
+ data = {"__type": old_type, "__var": [1, 2, 3]}
+ result = deserialize(data)
+ assert result == expected
+ assert type(result) is type(expected)
+
+ def test_backwards_compat_builtin_collection_nested(self):
+ """Verify deserialization of old-style tuple nested inside a dict."""
+ data = {
+ "arg1": "hello",
+ "arg2": {"__type": "tuple", "__var": [1, 2]},
+ }
+ result = deserialize(data)
+ assert result == {"arg1": "hello", "arg2": (1, 2)}
+
+ def test_backwards_compat_timedelta(self):
+ """Verify deserialization of old-style timedelta."""
+ import datetime
+
+ data = {"__type": "timedelta", "__var": 3600.0}
+ result = deserialize(data)
+ assert result == datetime.timedelta(seconds=3600)
+
def test_encode_asset(self):
asset = Asset(uri="mytest://asset", name="test")
obj = deserialize(serialize(asset))