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/9] 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/9] 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/9] [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/9] 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/9] [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/9] 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/9] 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/9] [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) From 883737472ab626544f1657189686120aac1071cc Mon Sep 17 00:00:00 2001 From: Tim Treis Date: Sat, 28 Feb 2026 17:17:55 +0100 Subject: [PATCH 9/9] Refactor verbosity to use IntEnum levels mirroring scanpy's convention Replace bool-based verbosity with a Verbosity IntEnum (error/warning/info/debug) that accepts enum, int, or string input. This aligns with scanpy's established pattern across the scverse ecosystem and allows finer-grained control. Co-Authored-By: Claude Opus 4.6 --- src/spatialdata_plot/__init__.py | 3 +- src/spatialdata_plot/_logging.py | 41 +++++++++---- src/spatialdata_plot/_settings.py | 34 +++++++++-- tests/pl/test_logging.py | 98 +++++++++++++++++-------------- 4 files changed, 112 insertions(+), 64 deletions(-) diff --git a/src/spatialdata_plot/__init__.py b/src/spatialdata_plot/__init__.py index f336f11c..76c7d18c 100644 --- a/src/spatialdata_plot/__init__.py +++ b/src/spatialdata_plot/__init__.py @@ -2,7 +2,8 @@ from . import pl from ._logging import set_verbosity +from ._settings import Verbosity -__all__ = ["pl", "set_verbosity"] +__all__ = ["pl", "set_verbosity", "Verbosity"] __version__ = version("spatialdata-plot") diff --git a/src/spatialdata_plot/_logging.py b/src/spatialdata_plot/_logging.py index e73fd949..eba0877f 100644 --- a/src/spatialdata_plot/_logging.py +++ b/src/spatialdata_plot/_logging.py @@ -1,30 +1,28 @@ # from https://github.com/scverse/spatialdata/blob/main/src/spatialdata/_logging.py +from __future__ import annotations + import logging import re from collections.abc import Iterator from contextlib import contextmanager from typing import TYPE_CHECKING -from ._settings import settings +from ._settings import _VERBOSITY_TO_LOGLEVEL, Verbosity if TYPE_CHECKING: # pragma: no cover from _pytest.logging import LogCaptureFixture -def _setup_logger() -> "logging.Logger": +def _setup_logger() -> logging.Logger: from rich.console import Console from rich.logging import RichHandler logger = logging.getLogger(__name__) - - level = logging.INFO if settings.verbose else logging.WARNING - logger.setLevel(level) - + logger.setLevel(logging.WARNING) 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) @@ -36,9 +34,31 @@ def _setup_logger() -> "logging.Logger": logger = _setup_logger() +def set_verbosity(verbosity: Verbosity | int | str) -> None: + """Set the verbosity level of the spatialdata-plot logger. + + Mirrors scanpy's verbosity convention. + + Parameters + ---------- + verbosity + The verbosity level. Accepts a :class:`Verbosity` enum member, + an ``int`` (0–3), or a ``str`` (e.g. ``"warning"``, ``"info"``). + """ + if isinstance(verbosity, str): + try: + verbosity = Verbosity[verbosity.lower()] + except KeyError: + msg = f"Cannot set verbosity to {verbosity!r}. Accepted string values are: {list(Verbosity.__members__)}" + raise ValueError(msg) from None + else: + verbosity = Verbosity(verbosity) + logger.setLevel(_VERBOSITY_TO_LOGLEVEL[verbosity]) + + @contextmanager def logger_warns( - caplog: "LogCaptureFixture", + caplog: LogCaptureFixture, logger: logging.Logger, match: str | None = None, level: int = logging.WARNING, @@ -75,8 +95,3 @@ 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 index ff6555f0..2618c49a 100644 --- a/src/spatialdata_plot/_settings.py +++ b/src/spatialdata_plot/_settings.py @@ -1,9 +1,33 @@ -from dataclasses import dataclass +"""Settings for spatialdata-plot, mirroring scanpy's verbosity pattern.""" +from __future__ import annotations -@dataclass -class Settings: - verbose: bool = False +import logging +from enum import IntEnum -settings = Settings() +class Verbosity(IntEnum): + """Verbosity levels, mirroring scanpy's convention. + + ======== ===== ================= + Level Value Logging level + ======== ===== ================= + error 0 ``logging.ERROR`` + warning 1 ``logging.WARNING`` + info 2 ``logging.INFO`` + debug 3 ``logging.DEBUG`` + ======== ===== ================= + """ + + error = 0 + warning = 1 + info = 2 + debug = 3 + + +_VERBOSITY_TO_LOGLEVEL: dict[Verbosity, int] = { + Verbosity.error: logging.ERROR, + Verbosity.warning: logging.WARNING, + Verbosity.info: logging.INFO, + Verbosity.debug: logging.DEBUG, +} diff --git a/tests/pl/test_logging.py b/tests/pl/test_logging.py index d07a717e..cac19ce3 100644 --- a/tests/pl/test_logging.py +++ b/tests/pl/test_logging.py @@ -1,57 +1,65 @@ -import io import logging -from spatialdata import SpatialData +import pytest import spatialdata_plot -from tests.conftest import PlotTester, PlotTesterMeta +from spatialdata_plot._logging import logger +from spatialdata_plot._settings import Verbosity -class TestLogging(PlotTester, metaclass=PlotTesterMeta): - def test_default_verbosity_hides_info(self, sdata_blobs: SpatialData): - """INFO logs should be hidden by default.""" - spatialdata_plot.set_verbosity(False) # ensure default verbosity +class TestSetVerbosity: + @pytest.fixture(autouse=True) + def _restore_verbosity(self): + """Restore default verbosity after each test.""" + yield + spatialdata_plot.set_verbosity(Verbosity.warning) - # Replace all handlers temporarily - logger = spatialdata_plot._logging.logger - original_handlers = logger.handlers[:] - logger.handlers = [] + def test_default_level_is_warning(self): + assert logger.level == logging.WARNING - log_stream = io.StringIO() - handler = logging.StreamHandler(log_stream) - handler.setLevel(logging.INFO) - logger.addHandler(handler) + @pytest.mark.parametrize( + ("input_value", "expected_level"), + [ + (Verbosity.error, logging.ERROR), + (Verbosity.warning, logging.WARNING), + (Verbosity.info, logging.INFO), + (Verbosity.debug, logging.DEBUG), + ], + ) + def test_set_verbosity_with_enum(self, input_value, expected_level): + spatialdata_plot.set_verbosity(input_value) + assert logger.level == expected_level - # Run the function - sdata_blobs.pl.render_shapes("blobs_circles", method="datashader").pl.show() + @pytest.mark.parametrize( + ("input_value", "expected_level"), + [ + (0, logging.ERROR), + (1, logging.WARNING), + (2, logging.INFO), + (3, logging.DEBUG), + ], + ) + def test_set_verbosity_with_int(self, input_value, expected_level): + spatialdata_plot.set_verbosity(input_value) + assert logger.level == expected_level - # Check captured logs — should NOT contain the datashader info message - logs = log_stream.getvalue() - assert "Using 'datashader' backend" not in logs + @pytest.mark.parametrize( + ("input_value", "expected_level"), + [ + ("error", logging.ERROR), + ("WARNING", logging.WARNING), + ("Info", logging.INFO), + ("debug", logging.DEBUG), + ], + ) + def test_set_verbosity_with_string(self, input_value, expected_level): + spatialdata_plot.set_verbosity(input_value) + assert logger.level == expected_level - # Restore original handlers - logger.handlers = original_handlers + def test_set_verbosity_invalid_string_raises(self): + with pytest.raises(ValueError, match="Cannot set verbosity"): + spatialdata_plot.set_verbosity("verbose") - 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[:] - 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 - logs = log_stream.getvalue() - assert "Using 'datashader' backend" in logs - - # Restore original handlers - logger.handlers = original_handlers - spatialdata_plot.set_verbosity(False) + def test_set_verbosity_invalid_int_raises(self): + with pytest.raises(ValueError): + spatialdata_plot.set_verbosity(99)