From f1d82971053287c27c83e1b945b774a6a37a8552 Mon Sep 17 00:00:00 2001 From: Andrey Anshin Date: Thu, 11 Jan 2024 15:12:44 +0400 Subject: [PATCH] Fix check on subclass for `typing.Union` in `_infer_multiple_outputs` for Python 3.10+ (#36728) * Fix check on subclass for `typing.Union` in `_infer_multiple_outputs` for Python 3.10+ * Limit PEP 604 test by Python 3.10 --- airflow/decorators/base.py | 2 +- tests/decorators/test_python.py | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/airflow/decorators/base.py b/airflow/decorators/base.py index 119672dd427aa3..2518fe1b53a3ae 100644 --- a/airflow/decorators/base.py +++ b/airflow/decorators/base.py @@ -350,7 +350,7 @@ def fake(): except TypeError: # Can't evaluate return type. return False ttype = getattr(return_type, "__origin__", return_type) - return issubclass(ttype, Mapping) + return isinstance(ttype, type) and issubclass(ttype, Mapping) def __attrs_post_init__(self): if "self" in self.function_signature.parameters: diff --git a/tests/decorators/test_python.py b/tests/decorators/test_python.py index 98aab562b8ce19..69b52d77248ff0 100644 --- a/tests/decorators/test_python.py +++ b/tests/decorators/test_python.py @@ -97,7 +97,6 @@ def identity_dict_with_decorator_call(x: int, y: int) -> resolve(annotation): 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 @@ -110,6 +109,27 @@ def t1() -> TypeDictClass: assert t1().operator.multiple_outputs is True + # We do not enable `from __future__ import annotations` for particular this test module, + # that mean `str | None` annotation would raise TypeError in Python 3.9 and below + @pytest.mark.skipif(sys.version_info < (3, 10), reason="PEP 604 is implemented in Python 3.10") + def test_infer_multiple_outputs_pep_604_union_type(self): + @task_decorator + def t1() -> str | None: + # Before PEP 604 which are implemented in Python 3.10 `str | None` + # returns `types.UnionType` which are class and could be check in `issubclass()`. + # However in Python 3.10+ this construction returns object `typing.Union` + # which can not be used in `issubclass()` + return "foo" + + assert t1().operator.multiple_outputs is False + + def test_infer_multiple_outputs_union_type(self): + @task_decorator + def t1() -> Union[str, None]: + return "foo" + + assert t1().operator.multiple_outputs is False + def test_infer_multiple_outputs_forward_annotation(self): if TYPE_CHECKING: