From aff2a6d6950a58513811ba0173dec8eb94a5c907 Mon Sep 17 00:00:00 2001 From: Taran Mamidala Date: Sun, 6 Sep 2026 12:19:35 -0400 Subject: [PATCH 1/4] fix(app): avoid reconfiguring root logging in test runs to preserve caplog Calling create() unconditionally invoked configure_logging(), which executed loggingDictConfig and replaced the root logger handlers. When a test constructed an ad-hoc application, this stripped pytest LogCaptureHandler and broke subsequent caplog assertions across the test session. Add do_configure_logging to create(), skip configure_logging() during active pytest test execution unless explicitly requested, and set disable_existing_loggers to False in flexmeasures_logging_config. Signed-off-by: Taran Mamidala --- documentation/changelog.rst | 2 ++ flexmeasures/app.py | 4 ++- flexmeasures/utils/config_utils.py | 1 + flexmeasures/utils/tests/test_config_utils.py | 33 +++++++++++++++++++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/documentation/changelog.rst b/documentation/changelog.rst index 21087adce7..ac8d63f8ec 100644 --- a/documentation/changelog.rst +++ b/documentation/changelog.rst @@ -42,6 +42,8 @@ Bugfixes * Avoid crashing on startup when the database is stamped with an Alembic revision unknown to this FlexMeasures checkout [see `PR #2465 `_] * The "module not installed" error for an unresolved ``FLEXMEASURES_PLUGINS`` entry now hints at the expected comma-separated format, which helps people who accidentally use an incorrect format like a JSON-array [see `PR #2473 `_] * ``flexmeasures jobs run-job`` ran each job twice, and always as if it were a scheduling job, which lost the queue-specific reporting of why a job failed [see `PR #2480 `_] +* Building a custom application in a test or with ``do_configure_logging=False`` no longer reconfigures root logger handlers and strips pytest's ``caplog`` handler + diff --git a/flexmeasures/app.py b/flexmeasures/app.py index 05023fd3b2..b8a6501cad 100644 --- a/flexmeasures/app.py +++ b/flexmeasures/app.py @@ -27,6 +27,7 @@ def create( # noqa C901 env: str | None = None, path_to_config: str | None = None, plugins: list[str] | None = None, + do_configure_logging: bool = True, ) -> Flask: """ Create a Flask app and configure it. @@ -53,7 +54,8 @@ def create( # noqa C901 from flexmeasures.utils.error_utils import add_basic_error_handlers from flexmeasures.utils.secrets_utils import set_secret_key, set_totp_secrets - configure_logging() # do this first, see https://flask.palletsprojects.com/en/2.0.x/logging + if do_configure_logging and not os.getenv("PYTEST_CURRENT_TEST"): + configure_logging() # do this first, see https://flask.palletsprojects.com/en/2.0.x/logging cfg_location = find_flexmeasures_cfg() # Find flexmeasures.cfg location # Create app app = Flask("flexmeasures") diff --git a/flexmeasures/utils/config_utils.py b/flexmeasures/utils/config_utils.py index a6e1a5b74f..22d4d93898 100644 --- a/flexmeasures/utils/config_utils.py +++ b/flexmeasures/utils/config_utils.py @@ -24,6 +24,7 @@ flexmeasures_logging_config = { "version": 1, + "disable_existing_loggers": False, "formatters": { "default": {"format": "[FLEXMEASURES][%(asctime)s] %(levelname)s: %(message)s"}, "detail": { diff --git a/flexmeasures/utils/tests/test_config_utils.py b/flexmeasures/utils/tests/test_config_utils.py index 98e965d216..63ebbc031d 100644 --- a/flexmeasures/utils/tests/test_config_utils.py +++ b/flexmeasures/utils/tests/test_config_utils.py @@ -1,3 +1,4 @@ +import logging import pytest from flask import Flask from werkzeug.sansio.utils import host_is_trusted @@ -171,3 +172,35 @@ def test_config_warnings_silent_when_trusted_hosts_is_set(): missing_settings, config_warnings = get_config_warnings(app) assert "TRUSTED_HOSTS" not in missing_settings assert not any("TRUSTED_HOSTS" in warning for warning in config_warnings) + + +def test_create_app_in_test_does_not_break_caplog(monkeypatch, caplog): + """Building a custom app inside a test does not overwrite root logger handlers or break caplog.""" + import flexmeasures.ui + from flexmeasures.app import create as create_app + + monkeypatch.setattr(flexmeasures.ui, "register_at", lambda app: None) + custom_app = create_app(env="testing") + assert custom_app.testing is True + + with caplog.at_level(logging.WARNING): + logging.getLogger().warning("test warning after create_app") + + assert any( + record.message == "test warning after create_app" for record in caplog.records + ) + + +def test_create_app_do_configure_logging_flag(monkeypatch): + """create_app respects do_configure_logging.""" + import flexmeasures.ui + from flexmeasures.app import create as create_app + + monkeypatch.setattr(flexmeasures.ui, "register_at", lambda app: None) + called = [] + monkeypatch.setattr( + "flexmeasures.utils.config_utils.configure_logging", + lambda: called.append(True), + ) + create_app(env="testing", do_configure_logging=False) + assert not called From be7b210f15093c302e25037ecb7d05845f2319b5 Mon Sep 17 00:00:00 2001 From: Taran Mamidala Date: Sun, 6 Sep 2026 16:02:46 -0400 Subject: [PATCH 2/4] docs(changelog): add PR reference link for PR 2484 Signed-off-by: Taran Mamidala --- documentation/changelog.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/documentation/changelog.rst b/documentation/changelog.rst index ac8d63f8ec..ff5983d21b 100644 --- a/documentation/changelog.rst +++ b/documentation/changelog.rst @@ -42,7 +42,7 @@ Bugfixes * Avoid crashing on startup when the database is stamped with an Alembic revision unknown to this FlexMeasures checkout [see `PR #2465 `_] * The "module not installed" error for an unresolved ``FLEXMEASURES_PLUGINS`` entry now hints at the expected comma-separated format, which helps people who accidentally use an incorrect format like a JSON-array [see `PR #2473 `_] * ``flexmeasures jobs run-job`` ran each job twice, and always as if it were a scheduling job, which lost the queue-specific reporting of why a job failed [see `PR #2480 `_] -* Building a custom application in a test or with ``do_configure_logging=False`` no longer reconfigures root logger handlers and strips pytest's ``caplog`` handler +* Building a custom application in a test or with ``do_configure_logging=False`` no longer reconfigures root logger handlers and strips pytest's ``caplog`` handler [see `PR #2484 `_] From 5c16aa2eec994fe07406c2a0d6b594fd79cf5298 Mon Sep 17 00:00:00 2001 From: Taran Mamidala Date: Tue, 15 Sep 2026 08:29:12 -0400 Subject: [PATCH 3/4] fix: leave disable_existing_loggers unchanged in logging config Signed-off-by: Taran Mamidala --- flexmeasures/utils/config_utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/flexmeasures/utils/config_utils.py b/flexmeasures/utils/config_utils.py index 22d4d93898..a6e1a5b74f 100644 --- a/flexmeasures/utils/config_utils.py +++ b/flexmeasures/utils/config_utils.py @@ -24,7 +24,6 @@ flexmeasures_logging_config = { "version": 1, - "disable_existing_loggers": False, "formatters": { "default": {"format": "[FLEXMEASURES][%(asctime)s] %(levelname)s: %(message)s"}, "detail": { From b9ffd014fed6e53a51dff58a73f10ef5f4efb635 Mon Sep 17 00:00:00 2001 From: Taran Mamidala Date: Tue, 15 Sep 2026 08:33:10 -0400 Subject: [PATCH 4/4] docs(app): document do_configure_logging in create docstring Signed-off-by: Taran Mamidala --- flexmeasures/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flexmeasures/app.py b/flexmeasures/app.py index b8a6501cad..8a1914533a 100644 --- a/flexmeasures/app.py +++ b/flexmeasures/app.py @@ -38,6 +38,8 @@ def create( # noqa C901 A path to a config file can be passed in (otherwise a config file will be searched in the home or instance directories). Also, a list of plugins can be set. Usually this works as a config setting, but this is useful for automated testing. + + Pass ``do_configure_logging=False`` to prevent reconfiguring root logger handlers (e.g. during custom test setup). """ from flexmeasures.utils import config_defaults