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))

Reply via email to