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

1"""Renderers turning a `~jquantstats._plots._spec.FigureSpec` into a figure. 

2 

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""" 

7 

8from __future__ import annotations 

9 

10from typing import TYPE_CHECKING, TypeAlias 

11 

12from .._backend import Backend, require_backend, resolve 

13from ._plotly import render_plotly as render_plotly 

14 

15if TYPE_CHECKING: 

16 from matplotlib.figure import Figure as MplFigure 

17 from plotly.graph_objects import Figure as PlotlyFigure 

18 

19 from .._spec import FigureSpec 

20 

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 

33 

34__all__ = ["render", "render_plotly"] 

35 

36 

37def render(spec: FigureSpec, backend: Backend | None = None) -> Figure: 

38 """Render *spec* with the selected backend. 

39 

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. 

44 

45 Returns: 

46 Figure: A `plotly.graph_objects.Figure` or a 

47 `matplotlib.figure.Figure`, according to the backend in effect. 

48 

49 Raises: 

50 UnknownPlotBackendError: If *backend* names an unsupported backend. 

51 MissingBackendError: If the selected backend's library is not installed. 

52 

53 """ 

54 selected = resolve(backend) 

55 if selected == "plotly": 

56 return render_plotly(spec) 

57 

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 

65 

66 return render_mpl(spec)