This is an automated email from the ASF dual-hosted git repository.
potiuk 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 165b4956988 Distinguish empty plugin folder from plugin load failures
in logs (#72432)
165b4956988 is described below
commit 165b495698876d0c3aa36acb2ccabfa9f8ed01d7
Author: SreeCharan Desu <[email protected]>
AuthorDate: Tue Sep 8 16:48:15 2026 +0530
Distinguish empty plugin folder from plugin load failures in logs (#72432)
Empty plugins/ on a new install looked like a failure in the
summary line even when nothing was wrong; warn only when imports
actually fail.
---
airflow-core/src/airflow/plugins_manager.py | 11 ++++++++++-
.../tests/unit/plugins/test_plugins_manager.py | 23 ++++++++++++++++++++++
2 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/airflow-core/src/airflow/plugins_manager.py
b/airflow-core/src/airflow/plugins_manager.py
index ff3c620b934..ccc29fa8288 100644
--- a/airflow-core/src/airflow/plugins_manager.py
+++ b/airflow-core/src/airflow/plugins_manager.py
@@ -133,7 +133,16 @@ def _get_plugins() -> tuple[list[AirflowPlugin], dict[str,
str]]:
if not settings.LAZY_LOAD_PROVIDERS:
__register_plugins(*_load_providers_plugins())
- log.debug("Loading %d plugin(s) took %.2f ms", len(plugins),
timer.duration)
+ if import_errors:
+ log.warning(
+ "Failed to load %d plugin file(s): %s",
+ len(import_errors),
+ sorted(import_errors.keys()),
+ )
+ elif not plugins:
+ log.debug("No plugins loaded (plugins folder is empty or contains no
valid plugins)")
+ else:
+ log.debug("Loading %d plugin(s) took %.2f ms", len(plugins),
timer.duration)
return plugins, import_errors
diff --git a/airflow-core/tests/unit/plugins/test_plugins_manager.py
b/airflow-core/tests/unit/plugins/test_plugins_manager.py
index 7f458ef4d90..bef30bcd253 100644
--- a/airflow-core/tests/unit/plugins/test_plugins_manager.py
+++ b/airflow-core/tests/unit/plugins/test_plugins_manager.py
@@ -91,6 +91,28 @@ class TestPluginsManager:
assert [r for r in caplog.record_tuples if not
r[0].startswith("opentelemetry.")] == []
+ def test_empty_plugins_folder_logs_no_failure(self, caplog, tmp_path):
+ from airflow import plugins_manager
+
+ with (
+ caplog.at_level(logging.DEBUG, logger="airflow.plugins_manager"),
+ conf_vars(
+ {
+ ("core", "plugins_folder"): os.fspath(tmp_path),
+ ("core", "load_examples"): "False",
+ }
+ ),
+ mock.patch("airflow.plugins_manager._load_entrypoint_plugins",
return_value=([], [])),
+ mock.patch("airflow.plugins_manager._load_providers_plugins",
return_value=([], [])),
+ ):
+ plugins, import_errors = plugins_manager._get_plugins()
+
+ assert plugins == []
+ assert import_errors == {}
+ received_logs = caplog.text
+ assert "Failed to load" not in received_logs
+ assert "No plugins loaded" in received_logs
+
def test_loads_filesystem_plugins_exception(self, caplog, tmp_path):
from airflow import plugins_manager
@@ -108,6 +130,7 @@ class TestPluginsManager:
received_logs = caplog.text
assert "Failed to load plugin" in received_logs
+ assert "Failed to load 1 plugin file(s)" in received_logs
assert "testplugin.py" in received_logs
def
test_duplicate_plugin_name_does_not_prevent_loading_subsequent_plugins(self):