From b43185962471d468f205e158c4e2fb71125d9019 Mon Sep 17 00:00:00 2001 From: Matthias Fabian Meyer-Bender Date: Fri, 19 Dec 2025 09:58:06 +0100 Subject: [PATCH 1/8] created a settings object allowing users to set the verbosity level, defaults to False (less verbose) --- src/spatialdata_plot/__init__.py | 3 ++- src/spatialdata_plot/_logging.py | 12 +++++++++++- src/spatialdata_plot/_settings.py | 7 +++++++ 3 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 src/spatialdata_plot/_settings.py diff --git a/src/spatialdata_plot/__init__.py b/src/spatialdata_plot/__init__.py index fd8c82c0..f336f11c 100644 --- a/src/spatialdata_plot/__init__.py +++ b/src/spatialdata_plot/__init__.py @@ -1,7 +1,8 @@ from importlib.metadata import version from . import pl +from ._logging import set_verbosity -__all__ = ["pl"] +__all__ = ["pl", "set_verbosity"] __version__ = version("spatialdata-plot") diff --git a/src/spatialdata_plot/_logging.py b/src/spatialdata_plot/_logging.py index 364cba27..fbbaf955 100644 --- a/src/spatialdata_plot/_logging.py +++ b/src/spatialdata_plot/_logging.py @@ -5,6 +5,7 @@ from collections.abc import Iterator from contextlib import contextmanager from typing import TYPE_CHECKING +from ._settings import settings if TYPE_CHECKING: # pragma: no cover from _pytest.logging import LogCaptureFixture @@ -15,10 +16,14 @@ def _setup_logger() -> "logging.Logger": from rich.logging import RichHandler logger = logging.getLogger(__name__) - logger.setLevel(logging.INFO) + + level = logging.INFO if settings.verbose else logging.WARNING + logger.setLevel(level) + console = Console(force_terminal=True) if console.is_jupyter is True: console.is_jupyter = False + ch = RichHandler(show_path=False, console=console, show_time=False) logger.addHandler(ch) @@ -69,3 +74,8 @@ def logger_warns( if not any(pattern.search(r.getMessage()) for r in records): msgs = [r.getMessage() for r in records] raise AssertionError(f"Did not find log matching {match!r} in records: {msgs!r}") + + +def set_verbosity(verbose: bool = True) -> None: + level = logging.INFO if verbose else logging.WARNING + logger.setLevel(level) diff --git a/src/spatialdata_plot/_settings.py b/src/spatialdata_plot/_settings.py new file mode 100644 index 00000000..74838fc1 --- /dev/null +++ b/src/spatialdata_plot/_settings.py @@ -0,0 +1,7 @@ +from dataclasses import dataclass + +@dataclass +class Settings: + verbose: bool = False + +settings = Settings() From ceb59340316609027ca98b4dd2288bcf2e3a71e6 Mon Sep 17 00:00:00 2001 From: Matthias Fabian Meyer-Bender Date: Fri, 19 Dec 2025 10:06:44 +0100 Subject: [PATCH 2/8] added test for logging level --- tests/pl/test_logging.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/pl/test_logging.py diff --git a/tests/pl/test_logging.py b/tests/pl/test_logging.py new file mode 100644 index 00000000..5af026fb --- /dev/null +++ b/tests/pl/test_logging.py @@ -0,0 +1,28 @@ +import logging +import pytest +import spatialdata_plot + +class TestLogging(PlotTester, metaclass=PlotTesterMeta): + + def test_default_verbosity_hides_info(self, sdata_blobs: "SpatialData", caplog): + """INFO logs should be hidden by default.""" + caplog.set_level(logging.INFO, logger=spatialdata_plot._logging.logger.name) + + # default is verbose=False + sdata_blobs.pl.render_shapes("blobs_circles", method="datashader").pl.show() + + # make sure no INFO messages were recorded + assert all(record.levelno != logging.INFO for record in caplog.records) + + def test_verbose_verbosity_shows_info(self, sdata_blobs: "SpatialData", caplog): + """INFO logs should appear when verbose=True.""" + spatialdata_plot.set_verbosity(True) + caplog.set_level(logging.INFO, logger=spatialdata_plot._logging.logger.name) + + sdata_blobs.pl.render_shapes("blobs_circles", method="datashader").pl.show() + + # at least one INFO record should exist + assert any(record.levelno == logging.INFO for record in caplog.records) + + # reset verbosity for other tests + spatialdata_plot.set_verbosity(False) From 6b5f5f0e2f594bf9c9610255a824ef26abe59181 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:09:15 +0000 Subject: [PATCH 3/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/spatialdata_plot/_logging.py | 7 ++++--- src/spatialdata_plot/_settings.py | 2 ++ tests/pl/test_logging.py | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/spatialdata_plot/_logging.py b/src/spatialdata_plot/_logging.py index fbbaf955..e73fd949 100644 --- a/src/spatialdata_plot/_logging.py +++ b/src/spatialdata_plot/_logging.py @@ -5,6 +5,7 @@ from collections.abc import Iterator from contextlib import contextmanager from typing import TYPE_CHECKING + from ._settings import settings if TYPE_CHECKING: # pragma: no cover @@ -16,14 +17,14 @@ def _setup_logger() -> "logging.Logger": from rich.logging import RichHandler logger = logging.getLogger(__name__) - + level = logging.INFO if settings.verbose else logging.WARNING logger.setLevel(level) - + console = Console(force_terminal=True) if console.is_jupyter is True: console.is_jupyter = False - + ch = RichHandler(show_path=False, console=console, show_time=False) logger.addHandler(ch) diff --git a/src/spatialdata_plot/_settings.py b/src/spatialdata_plot/_settings.py index 74838fc1..ff6555f0 100644 --- a/src/spatialdata_plot/_settings.py +++ b/src/spatialdata_plot/_settings.py @@ -1,7 +1,9 @@ from dataclasses import dataclass + @dataclass class Settings: verbose: bool = False + settings = Settings() diff --git a/tests/pl/test_logging.py b/tests/pl/test_logging.py index 5af026fb..c1b761ad 100644 --- a/tests/pl/test_logging.py +++ b/tests/pl/test_logging.py @@ -1,9 +1,9 @@ import logging -import pytest + import spatialdata_plot -class TestLogging(PlotTester, metaclass=PlotTesterMeta): +class TestLogging(PlotTester, metaclass=PlotTesterMeta): def test_default_verbosity_hides_info(self, sdata_blobs: "SpatialData", caplog): """INFO logs should be hidden by default.""" caplog.set_level(logging.INFO, logger=spatialdata_plot._logging.logger.name) From 19283ff48e8a66214d6f6b96476acd841eb2e86c Mon Sep 17 00:00:00 2001 From: Matthias Fabian Meyer-Bender Date: Fri, 19 Dec 2025 10:12:51 +0100 Subject: [PATCH 4/8] fix test imports --- tests/pl/test_logging.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/pl/test_logging.py b/tests/pl/test_logging.py index c1b761ad..1ad077bf 100644 --- a/tests/pl/test_logging.py +++ b/tests/pl/test_logging.py @@ -1,10 +1,11 @@ import logging import spatialdata_plot - +from spatialdata import SpatialData +from tests.conftest import PlotTester, PlotTesterMeta class TestLogging(PlotTester, metaclass=PlotTesterMeta): - def test_default_verbosity_hides_info(self, sdata_blobs: "SpatialData", caplog): + def test_default_verbosity_hides_info(self, sdata_blobs: SpatialData, caplog): """INFO logs should be hidden by default.""" caplog.set_level(logging.INFO, logger=spatialdata_plot._logging.logger.name) @@ -14,7 +15,7 @@ def test_default_verbosity_hides_info(self, sdata_blobs: "SpatialData", caplog): # make sure no INFO messages were recorded assert all(record.levelno != logging.INFO for record in caplog.records) - def test_verbose_verbosity_shows_info(self, sdata_blobs: "SpatialData", caplog): + def test_verbose_verbosity_shows_info(self, sdata_blobs: SpatialData, caplog): """INFO logs should appear when verbose=True.""" spatialdata_plot.set_verbosity(True) caplog.set_level(logging.INFO, logger=spatialdata_plot._logging.logger.name) From f5ed1f95ad4ace9356bf4fbbb2e2b83a6d1f9418 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:13:09 +0000 Subject: [PATCH 5/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/pl/test_logging.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/pl/test_logging.py b/tests/pl/test_logging.py index 1ad077bf..6e81460b 100644 --- a/tests/pl/test_logging.py +++ b/tests/pl/test_logging.py @@ -1,9 +1,11 @@ import logging -import spatialdata_plot from spatialdata import SpatialData + +import spatialdata_plot from tests.conftest import PlotTester, PlotTesterMeta + class TestLogging(PlotTester, metaclass=PlotTesterMeta): def test_default_verbosity_hides_info(self, sdata_blobs: SpatialData, caplog): """INFO logs should be hidden by default.""" From c2517347b13bffe076ccf1cabe237ee1cbeabfa5 Mon Sep 17 00:00:00 2001 From: Matthias Fabian Meyer-Bender Date: Fri, 19 Dec 2025 10:22:53 +0100 Subject: [PATCH 6/8] update tests --- tests/pl/test_logging.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/pl/test_logging.py b/tests/pl/test_logging.py index 1ad077bf..fd965911 100644 --- a/tests/pl/test_logging.py +++ b/tests/pl/test_logging.py @@ -4,10 +4,12 @@ from spatialdata import SpatialData from tests.conftest import PlotTester, PlotTesterMeta + class TestLogging(PlotTester, metaclass=PlotTesterMeta): def test_default_verbosity_hides_info(self, sdata_blobs: SpatialData, caplog): """INFO logs should be hidden by default.""" - caplog.set_level(logging.INFO, logger=spatialdata_plot._logging.logger.name) + # Capture all logs under the 'spatialdata_plot' hierarchy + caplog.set_level(logging.INFO, logger="spatialdata_plot") # default is verbose=False sdata_blobs.pl.render_shapes("blobs_circles", method="datashader").pl.show() @@ -18,11 +20,11 @@ def test_default_verbosity_hides_info(self, sdata_blobs: SpatialData, caplog): def test_verbose_verbosity_shows_info(self, sdata_blobs: SpatialData, caplog): """INFO logs should appear when verbose=True.""" spatialdata_plot.set_verbosity(True) - caplog.set_level(logging.INFO, logger=spatialdata_plot._logging.logger.name) + caplog.set_level(logging.INFO, logger="spatialdata_plot") sdata_blobs.pl.render_shapes("blobs_circles", method="datashader").pl.show() - # at least one INFO record should exist + # at least one INFO record should exist anywhere under spatialdata_plot assert any(record.levelno == logging.INFO for record in caplog.records) # reset verbosity for other tests From 3d31dee3c513fd5bc23fb672c0f27c99fea71ac7 Mon Sep 17 00:00:00 2001 From: Matthias Fabian Meyer-Bender Date: Fri, 19 Dec 2025 10:46:10 +0100 Subject: [PATCH 7/8] temporary handler to capture logging output correctly --- tests/pl/test_logging.py | 59 ++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/tests/pl/test_logging.py b/tests/pl/test_logging.py index 4283540c..647af7e8 100644 --- a/tests/pl/test_logging.py +++ b/tests/pl/test_logging.py @@ -1,3 +1,4 @@ +import io import logging from spatialdata import SpatialData @@ -7,26 +8,50 @@ class TestLogging(PlotTester, metaclass=PlotTesterMeta): - def test_default_verbosity_hides_info(self, sdata_blobs: SpatialData, caplog): + def test_default_verbosity_hides_info(self, sdata_blobs: SpatialData): """INFO logs should be hidden by default.""" - # Capture all logs under the 'spatialdata_plot' hierarchy - caplog.set_level(logging.INFO, logger="spatialdata_plot") - - # default is verbose=False + spatialdata_plot.set_verbosity(False) # ensure default verbosity + + # Replace all handlers temporarily + logger = spatialdata_plot._logging.logger + original_handlers = logger.handlers[:] + logger.handlers = [] + + log_stream = io.StringIO() + handler = logging.StreamHandler(log_stream) + handler.setLevel(logging.INFO) + logger.addHandler(handler) + + # Run the function sdata_blobs.pl.render_shapes("blobs_circles", method="datashader").pl.show() + + # Check captured logs — should NOT contain the datashader info message + logs = log_stream.getvalue() + assert "Using 'datashader' backend" not in logs - # make sure no INFO messages were recorded - assert all(record.levelno != logging.INFO for record in caplog.records) + # Restore original handlers + logger.handlers = original_handlers - def test_verbose_verbosity_shows_info(self, sdata_blobs: SpatialData, caplog): - """INFO logs should appear when verbose=True.""" + def test_verbose_verbosity_shows_info(self, sdata_blobs): spatialdata_plot.set_verbosity(True) - caplog.set_level(logging.INFO, logger="spatialdata_plot") - + + # Replace all handlers temporarily + logger = spatialdata_plot._logging.logger + original_handlers = logger.handlers[:] + logger.handlers = [] + + log_stream = io.StringIO() + handler = logging.StreamHandler(log_stream) + handler.setLevel(logging.INFO) + logger.addHandler(handler) + + # Run the function sdata_blobs.pl.render_shapes("blobs_circles", method="datashader").pl.show() - - # at least one INFO record should exist anywhere under spatialdata_plot - assert any(record.levelno == logging.INFO for record in caplog.records) - - # reset verbosity for other tests - spatialdata_plot.set_verbosity(False) + + # Check captured logs + logs = log_stream.getvalue() + assert "Using 'datashader' backend" in logs + + # Restore original handlers + logger.handlers = original_handlers + spatialdata_plot.set_verbosity(False) \ No newline at end of file From d3884fca18270e4b7638916246bd178cb0a38f90 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 19 Dec 2025 09:46:27 +0000 Subject: [PATCH 8/8] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/pl/test_logging.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/pl/test_logging.py b/tests/pl/test_logging.py index 647af7e8..d07a717e 100644 --- a/tests/pl/test_logging.py +++ b/tests/pl/test_logging.py @@ -21,10 +21,10 @@ def test_default_verbosity_hides_info(self, sdata_blobs: SpatialData): handler = logging.StreamHandler(log_stream) handler.setLevel(logging.INFO) logger.addHandler(handler) - + # Run the function sdata_blobs.pl.render_shapes("blobs_circles", method="datashader").pl.show() - + # Check captured logs — should NOT contain the datashader info message logs = log_stream.getvalue() assert "Using 'datashader' backend" not in logs @@ -34,7 +34,7 @@ def test_default_verbosity_hides_info(self, sdata_blobs: SpatialData): def test_verbose_verbosity_shows_info(self, sdata_blobs): spatialdata_plot.set_verbosity(True) - + # Replace all handlers temporarily logger = spatialdata_plot._logging.logger original_handlers = logger.handlers[:] @@ -44,14 +44,14 @@ def test_verbose_verbosity_shows_info(self, sdata_blobs): handler = logging.StreamHandler(log_stream) handler.setLevel(logging.INFO) logger.addHandler(handler) - + # Run the function sdata_blobs.pl.render_shapes("blobs_circles", method="datashader").pl.show() - + # Check captured logs logs = log_stream.getvalue() assert "Using 'datashader' backend" in logs # Restore original handlers logger.handlers = original_handlers - spatialdata_plot.set_verbosity(False) \ No newline at end of file + spatialdata_plot.set_verbosity(False)