From b62d9d5d28ca4dd476a3d9c86ef3a0084d87d14e Mon Sep 17 00:00:00 2001 From: Jinwoo Date: Sun, 10 May 2026 14:55:34 +0900 Subject: [PATCH] fix: report duplicate plugin names as import errors --- airflow-core/src/airflow/plugins_manager.py | 5 +++- .../unit/plugins/test_plugins_manager.py | 29 ++++++++++++++++++- 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/airflow-core/src/airflow/plugins_manager.py b/airflow-core/src/airflow/plugins_manager.py index 76b478db66f24..86783c86d5f5a 100644 --- a/airflow-core/src/airflow/plugins_manager.py +++ b/airflow-core/src/airflow/plugins_manager.py @@ -99,7 +99,10 @@ def _get_plugins() -> tuple[list[AirflowPlugin], dict[str, str]]: def __register_plugins(plugin_instances: list[AirflowPlugin], errors: dict[str, str]) -> None: for plugin_instance in plugin_instances: if plugin_instance.name in loaded_plugins: - log.warning("Plugin %r already registered, skipping", plugin_instance.name) + message = f"Plugin {plugin_instance.name!r} already registered, skipping" + log.warning(message) + name = str(plugin_instance.source) if plugin_instance.source else plugin_instance.name or "" + import_errors[name] = message continue loaded_plugins.add(plugin_instance.name) diff --git a/airflow-core/tests/unit/plugins/test_plugins_manager.py b/airflow-core/tests/unit/plugins/test_plugins_manager.py index 7ffa84060f9a8..1163ac6338617 100644 --- a/airflow-core/tests/unit/plugins/test_plugins_manager.py +++ b/airflow-core/tests/unit/plugins/test_plugins_manager.py @@ -160,7 +160,34 @@ class PluginC(AirflowPlugin): assert "plugin_b" in plugin_names assert "plugin_c" in plugin_names assert len(plugins) == 3 - assert not import_errors + + def test_duplicate_plugin_name_is_reported_as_import_error(self): + from airflow import plugins_manager + + class PluginA(AirflowPlugin): + name = "plugin_a" + + class PluginADuplicateName(AirflowPlugin): + name = "plugin_a" + + plugin_a = PluginA() + plugin_a_dup = PluginADuplicateName() + + with ( + mock.patch( + "airflow.plugins_manager._load_plugins_from_plugin_directory", + return_value=([plugin_a], {}), + ), + mock.patch( + "airflow.plugins_manager._load_entrypoint_plugins", + return_value=([plugin_a_dup], {}), + ), + mock.patch("airflow.plugins_manager._load_providers_plugins", return_value=([], {})), + ): + plugins, import_errors = plugins_manager._get_plugins() + + assert [p.name for p in plugins] == ["plugin_a"] + assert len(import_errors) == 1 def test_should_warning_about_incompatible_plugins(self, caplog): class AirflowAdminViewsPlugin(AirflowPlugin):