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