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

shahar1 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 501805f89cb Add missing tests for common.compat.standard operators and 
triggers (#73262)
501805f89cb is described below

commit 501805f89cb506c69d4bba15e4735eb7847a3f41
Author: Zeeshan Chaudhry <[email protected]>
AuthorDate: Thu Sep 17 07:02:04 2026 -0400

    Add missing tests for common.compat.standard operators and triggers (#73262)
---
 .../tests/unit/always/test_project_structure.py    |  2 -
 .../providers/common/compat/standard/operators.py  |  6 +-
 .../tests/unit/common/compat/standard/__init__.py  | 16 ++++
 .../unit/common/compat/standard/test_operators.py  | 90 ++++++++++++++++++++++
 .../unit/common/compat/standard/test_triggers.py   | 32 ++++++++
 5 files changed, 141 insertions(+), 5 deletions(-)

diff --git a/airflow-core/tests/unit/always/test_project_structure.py 
b/airflow-core/tests/unit/always/test_project_structure.py
index 19c8620a984..f5d26dbae7e 100644
--- a/airflow-core/tests/unit/always/test_project_structure.py
+++ b/airflow-core/tests/unit/always/test_project_structure.py
@@ -90,8 +90,6 @@ class TestProjectStructure:
             
"providers/cncf/kubernetes/tests/unit/cncf/kubernetes/utils/test_delete_from.py",
             
"providers/cncf/kubernetes/tests/unit/cncf/kubernetes/utils/test_k8s_hashlib_wrapper.py",
             "providers/common/ai/tests/unit/common/ai/test_exceptions.py",
-            
"providers/common/compat/tests/unit/common/compat/standard/test_operators.py",
-            
"providers/common/compat/tests/unit/common/compat/standard/test_triggers.py",
             
"providers/common/compat/tests/unit/common/compat/standard/test_utils.py",
             
"providers/common/messaging/tests/unit/common/messaging/providers/test_sqs.py",
             
"providers/fab/tests/unit/fab/auth_manager/api_fastapi/datamodels/test_login.py",
diff --git 
a/providers/common/compat/src/airflow/providers/common/compat/standard/operators.py
 
b/providers/common/compat/src/airflow/providers/common/compat/standard/operators.py
index b916a9d5f9e..ab84e8f15eb 100644
--- 
a/providers/common/compat/src/airflow/providers/common/compat/standard/operators.py
+++ 
b/providers/common/compat/src/airflow/providers/common/compat/standard/operators.py
@@ -29,10 +29,8 @@ _IMPORT_MAP: dict[str, str | tuple[str, ...]] = {
     # Re-export from sdk (which handles Airflow 2.x/3.x fallbacks)
     "BaseBranchOperator": "airflow.providers.common.compat.sdk",
     "BaseOperator": "airflow.providers.common.compat.sdk",
-    "BaseAsyncOperator": "airflow.providers.common.compat.sdk",
     "BranchMixIn": "airflow.providers.common.compat.sdk",
     "get_current_context": "airflow.providers.common.compat.sdk",
-    "is_async_callable": "airflow.providers.common.compat.sdk",
     # Standard provider items with direct fallbacks
     "PythonOperator": ("airflow.providers.standard.operators.python", 
"airflow.operators.python"),
     "ShortCircuitOperator": ("airflow.providers.standard.operators.python", 
"airflow.operators.python"),
@@ -78,4 +76,6 @@ else:
 
 __getattr__ = create_module_getattr(import_map=_IMPORT_MAP)
 
-__all__ = sorted(_IMPORT_MAP.keys())
+# BaseAsyncOperator and is_async_callable are bound above by the version gate, 
so they never route
+# through _IMPORT_MAP. They are added here to keep them part of the public 
surface.
+__all__ = sorted([*_IMPORT_MAP, "BaseAsyncOperator", "is_async_callable"])
diff --git 
a/providers/common/compat/tests/unit/common/compat/standard/__init__.py 
b/providers/common/compat/tests/unit/common/compat/standard/__init__.py
new file mode 100644
index 00000000000..13a83393a91
--- /dev/null
+++ b/providers/common/compat/tests/unit/common/compat/standard/__init__.py
@@ -0,0 +1,16 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
diff --git 
a/providers/common/compat/tests/unit/common/compat/standard/test_operators.py 
b/providers/common/compat/tests/unit/common/compat/standard/test_operators.py
new file mode 100644
index 00000000000..fe90967881b
--- /dev/null
+++ 
b/providers/common/compat/tests/unit/common/compat/standard/test_operators.py
@@ -0,0 +1,90 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import functools
+
+import pytest
+
+from airflow.providers.common.compat.standard import operators
+
+from tests_common.test_utils.version_compat import AIRFLOW_V_3_2_PLUS
+
+EXPECTED_EXPORTS = (
+    "BaseAsyncOperator",
+    "BaseBranchOperator",
+    "BaseOperator",
+    "BranchMixIn",
+    "PythonOperator",
+    "ShortCircuitOperator",
+    "_SERIALIZERS",
+    "get_current_context",
+    "is_async_callable",
+)
+
+
+async def async_function():
+    """Sample coroutine function."""
+
+
+def sync_function():
+    """Sample plain function."""
+
+
+def test_public_exports():
+    assert set(operators.__all__) == set(EXPECTED_EXPORTS)
+
+
[email protected]("name", EXPECTED_EXPORTS)
+def test_all_compat_imports_work(name):
+    assert getattr(operators, name) is not None
+
+
[email protected](
+    ("func", "expected"),
+    [
+        pytest.param(async_function, True, id="coroutine-function"),
+        pytest.param(sync_function, False, id="plain-function"),
+        pytest.param(functools.partial(async_function), True, 
id="partial-of-coroutine-function"),
+        pytest.param(functools.partial(sync_function), False, 
id="partial-of-plain-function"),
+        pytest.param(
+            functools.partial(functools.partial(async_function)),
+            True,
+            id="nested-partial-of-coroutine-function",
+        ),
+    ],
+)
+def test_is_async_callable(func, expected):
+    """
+    Coroutine functions are detected through any number of 
``functools.partial`` wrappers.
+
+    These cases hold on both sides of the Airflow 3.2 fork: the local stub 
unwraps partials in a
+    loop, and the real implementation does the same through 
``unwrap_callable``.
+    """
+    assert operators.is_async_callable(func) is expected
+
+
[email protected](AIRFLOW_V_3_2_PLUS, reason="The BaseAsyncOperator stub 
only exists on Airflow < 3.2")
+class TestBaseAsyncOperatorStub:
+    def test_is_async(self):
+        operator = operators.BaseAsyncOperator(task_id="test_async")
+        assert operator.is_async is True
+
+    def test_execute_raises_runtime_error(self):
+        operator = operators.BaseAsyncOperator(task_id="test_async")
+        with pytest.raises(RuntimeError, match="Async operators require 
Airflow 3.2"):
+            operator.execute(None)
diff --git 
a/providers/common/compat/tests/unit/common/compat/standard/test_triggers.py 
b/providers/common/compat/tests/unit/common/compat/standard/test_triggers.py
new file mode 100644
index 00000000000..cd6940a035f
--- /dev/null
+++ b/providers/common/compat/tests/unit/common/compat/standard/test_triggers.py
@@ -0,0 +1,32 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+from __future__ import annotations
+
+import pytest
+
+from airflow.providers.common.compat.standard import triggers
+
+EXPECTED_EXPORTS = ("TimeDeltaTrigger",)
+
+
+def test_public_exports():
+    assert set(triggers.__all__) == set(EXPECTED_EXPORTS)
+
+
[email protected]("name", EXPECTED_EXPORTS)
+def test_all_compat_imports_work(name):
+    assert getattr(triggers, name) is not None

Reply via email to