Coverage for src/jquantstats/_plots/_render/__init__.py: 100%
12 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 04:11 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-23 04:11 +0000
1"""Renderers turning a `~jquantstats._plots._spec.FigureSpec` into a figure.
3One module per backend, and `render` is the only place that chooses between
4them. Because every chart is described as a spec, that one function serves every
5plot method — there is no per-method dispatch to write or to keep in step.
6"""
8from __future__ import annotations
10from typing import TYPE_CHECKING, TypeAlias
12from .._backend import Backend, require_backend, resolve
13from ._plotly import render_plotly as render_plotly
15if TYPE_CHECKING:
16 from matplotlib.figure import Figure as MplFigure
17 from plotly.graph_objects import Figure as PlotlyFigure
19 from .._spec import FigureSpec
21 #: What a renderer returns once the backend is only known at runtime.
22 #:
23 #: Type-checking-only, like the two imports above it: matplotlib is an
24 #: optional dependency, and every module here carries
25 #: ``from __future__ import annotations``, so an annotation naming these is
26 #: never evaluated.
27 #:
28 #: Public plot methods do not expose this union directly. They overload on
29 #: the literal ``backend`` argument instead, so ``data.plots.returns()``
30 #: still infers exactly ``go.Figure`` and no existing caller has to narrow
31 #: a union that was not there before.
32 Figure: TypeAlias = PlotlyFigure | MplFigure
34__all__ = ["render", "render_plotly"]
37def render(spec: FigureSpec, backend: Backend | None = None) -> Figure:
38 """Render *spec* with the selected backend.
40 Args:
41 spec: The chart to draw.
42 backend: An explicit choice, or None to use the ambient selection —
43 a `~jquantstats.plot_backend` block, else the process-wide default.
45 Returns:
46 Figure: A `plotly.graph_objects.Figure` or a
47 `matplotlib.figure.Figure`, according to the backend in effect.
49 Raises:
50 UnknownPlotBackendError: If *backend* names an unsupported backend.
51 MissingBackendError: If the selected backend's library is not installed.
53 """
54 selected = resolve(backend)
55 if selected == "plotly":
56 return render_plotly(spec)
58 # Imported here, not at module scope, so that `import jquantstats` never
59 # imports matplotlib and the extra stays genuinely optional. The
60 # availability check runs first so an absent library produces a message
61 # naming the extra rather than a bare ModuleNotFoundError from deep inside
62 # the import machinery.
63 require_backend(selected)
64 from ._mpl import render_mpl
66 return render_mpl(spec)