Skip to content
3 changes: 2 additions & 1 deletion src/spatialdata_plot/__init__.py
Original file line number Diff line number Diff line change
@@ -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")
13 changes: 12 additions & 1 deletion src/spatialdata_plot/_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
from contextlib import contextmanager
from typing import TYPE_CHECKING

from ._settings import settings

if TYPE_CHECKING: # pragma: no cover
from _pytest.logging import LogCaptureFixture

Expand All @@ -15,10 +17,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)

Expand Down Expand Up @@ -69,3 +75,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)
9 changes: 9 additions & 0 deletions src/spatialdata_plot/_settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from dataclasses import dataclass


@dataclass
class Settings:
verbose: bool = False


settings = Settings()
57 changes: 57 additions & 0 deletions tests/pl/test_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import io
import logging

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):
"""INFO logs should be hidden by default."""
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

# Restore original handlers
logger.handlers = original_handlers

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)