This is an automated email from the ASF dual-hosted git repository.

jscheffl 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 e11b91c8e0 Make sure `multiple_outputs` is inferred correctly even 
when using `TypedDict` (#36652)
e11b91c8e0 is described below

commit e11b91c8e01b38023f209983a81aee23439a34a3
Author: Noam Cohen <[email protected]>
AuthorDate: Wed Jan 10 23:21:21 2024 +0200

    Make sure `multiple_outputs` is inferred correctly even when using 
`TypedDict` (#36652)
    
    * Use `issubclass()` to check if return type is a dictionary
    
    * Compare type to `typing.Mapping` instead of `typing.Dict`
    
    * Add documentation
---
 airflow/decorators/base.py                |  3 +--
 docs/apache-airflow/tutorial/taskflow.rst |  4 ++--
 tests/decorators/test_python.py           | 13 +++++++++++++
 3 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/airflow/decorators/base.py b/airflow/decorators/base.py
index d3ec556f05..119672dd42 100644
--- a/airflow/decorators/base.py
+++ b/airflow/decorators/base.py
@@ -27,7 +27,6 @@ from typing import (
     Callable,
     ClassVar,
     Collection,
-    Dict,
     Generic,
     Iterator,
     Mapping,
@@ -351,7 +350,7 @@ class _TaskDecorator(ExpandableFactory, Generic[FParams, 
FReturn, OperatorSubcla
         except TypeError:  # Can't evaluate return type.
             return False
         ttype = getattr(return_type, "__origin__", return_type)
-        return ttype is dict or ttype is Dict
+        return issubclass(ttype, Mapping)
 
     def __attrs_post_init__(self):
         if "self" in self.function_signature.parameters:
diff --git a/docs/apache-airflow/tutorial/taskflow.rst 
b/docs/apache-airflow/tutorial/taskflow.rst
index 5d71576b59..6d5b3bed25 100644
--- a/docs/apache-airflow/tutorial/taskflow.rst
+++ b/docs/apache-airflow/tutorial/taskflow.rst
@@ -428,8 +428,8 @@ Tasks can also infer multiple outputs by using dict Python 
typing.
    def identity_dict(x: int, y: int) -> dict[str, int]:
        return {"x": x, "y": y}
 
-By using the typing ``Dict`` for the function return type, the 
``multiple_outputs`` parameter
-is automatically set to true.
+By using the typing ``dict``, or any other class that conforms to the 
``typing.Mapping`` protocol,
+for the function return type, the ``multiple_outputs`` parameter is 
automatically set to true.
 
 Note, If you manually set the ``multiple_outputs`` parameter the inference is 
disabled and
 the parameter value is used.
diff --git a/tests/decorators/test_python.py b/tests/decorators/test_python.py
index 78203ca6ef..98aab562b8 100644
--- a/tests/decorators/test_python.py
+++ b/tests/decorators/test_python.py
@@ -97,6 +97,19 @@ class TestAirflowTaskDecorator(BasePythonTest):
 
         assert identity_dict_with_decorator_call(5, 
5).operator.multiple_outputs is True
 
+    @pytest.mark.skipif(sys.version_info < (3, 8), reason="PEP 589 is 
implemented in Python 3.8")
+    def test_infer_multiple_outputs_typed_dict(self):
+        from typing import TypedDict
+
+        class TypeDictClass(TypedDict):
+            pass
+
+        @task_decorator
+        def t1() -> TypeDictClass:
+            return {}
+
+        assert t1().operator.multiple_outputs is True
+
     def test_infer_multiple_outputs_forward_annotation(self):
         if TYPE_CHECKING:
 

Reply via email to