This is an automated email from the ASF dual-hosted git repository. shahar1 pushed a commit to branch check-core-shim-targets-exist in repository https://gitbox.apache.org/repos/asf/airflow.git
commit 9a4cc1ce2e30556d6c052d28e3a4fca774781761 Author: Shahar Epstein <[email protected]> AuthorDate: Sun Nov 8 15:06:45 2026 +0200 Treat the else branch of "if not TYPE_CHECKING" as typing-only The guard counted names bound under the else branch of `if not TYPE_CHECKING` as present at runtime, so a shim target defined only there would pass the check although it cannot be imported. That branch is now skipped and only the body is collected. --- scripts/ci/prek/check_core_shim_targets_exist.py | 7 +++++++ scripts/tests/ci/prek/test_check_core_shim_targets_exist.py | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/scripts/ci/prek/check_core_shim_targets_exist.py b/scripts/ci/prek/check_core_shim_targets_exist.py index 12f204bc338..ff967ff7f15 100755 --- a/scripts/ci/prek/check_core_shim_targets_exist.py +++ b/scripts/ci/prek/check_core_shim_targets_exist.py @@ -106,6 +106,13 @@ def get_module_names(path: Path) -> set[str]: names.update(alias.asname or alias.name.split(".")[0] for alias in node.names) elif isinstance(node, ast.If): # Version-gated branches cannot be resolved statically, so either branch counts. + if ( + isinstance(node.test, ast.UnaryOp) + and isinstance(node.test.op, ast.Not) + and is_type_checking(node.test.operand) + ): + collect(node.body) + continue if not is_type_checking(node.test): collect(node.body) collect(node.orelse) diff --git a/scripts/tests/ci/prek/test_check_core_shim_targets_exist.py b/scripts/tests/ci/prek/test_check_core_shim_targets_exist.py index 8baf8397611..3ed6fc38be9 100644 --- a/scripts/tests/ci/prek/test_check_core_shim_targets_exist.py +++ b/scripts/tests/ci/prek/test_check_core_shim_targets_exist.py @@ -54,6 +54,11 @@ def providers_root(tmp_path): class WaitSensor(Base): ... def helper(): ... + + if not TYPE_CHECKING: + class Runtime: ... + else: + class TypingOnly: ... """ ) ) @@ -91,6 +96,8 @@ def test_find_provider_targets_skips_non_provider_targets(write_python_file): pytest.param("WaitSensorAsync", f"{MODULE}.WaitSensorAsync", False, id="removed-class"), pytest.param("*", "airflow.providers.acme.sensors.gone", False, id="removed-module"), pytest.param("TypeOnly", f"{MODULE}.TypeOnly", False, id="type-checking-only"), + pytest.param("Runtime", f"{MODULE}.Runtime", True, id="not-type-checking-body"), + pytest.param("TypingOnly", f"{MODULE}.TypingOnly", False, id="not-type-checking-else"), pytest.param("annotated_only", f"{MODULE}.annotated_only", False, id="annotation-without-value"), pytest.param("X", "airflow.providers.missing.hooks.X", False, id="missing-provider"), ],
