mplbed 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
mplbed/__init__.py ADDED
@@ -0,0 +1,15 @@
1
+ from .asgi import MplbedMiddleware
2
+ from .html import raw as raw_html
3
+ from .html import safe as safe_html
4
+ from .integration import quart as mplbed_quart
5
+ from .integration import starlette as mplbed_starlette
6
+ from .server import mplbed_app_factory
7
+
8
+ __all__ = [
9
+ "mplbed_quart",
10
+ "mplbed_starlette",
11
+ "mplbed_app_factory",
12
+ "raw_html",
13
+ "safe_html",
14
+ "MplbedMiddleware",
15
+ ]
mplbed/_doc_helpers.py ADDED
@@ -0,0 +1,52 @@
1
+ from typing import Any
2
+
3
+ from .consts import DEFAULT_PREFIX
4
+
5
+
6
+ def doc(s):
7
+ def decorator(func):
8
+ func.__doc__ = s
9
+ return func
10
+
11
+ return decorator
12
+
13
+
14
+ def fdf(s):
15
+ """_F_ix _D_ocstring _F_ragment for inclusion in a docstring template."""
16
+ from inspect import cleandoc
17
+ from textwrap import indent
18
+
19
+ return indent(cleandoc(s), " ").strip()
20
+
21
+
22
+ class DotAccessDict(dict[str, Any]):
23
+ __getattr__ = dict.get
24
+
25
+
26
+ PARAMS_DS: Any = DotAccessDict(
27
+ prefix=fdf(f"""
28
+ prefix : str, optional
29
+ The URL prefix for the routes handleded by `mplbed`. Default is '{DEFAULT_PREFIX}'.
30
+ """),
31
+ mplbed_starlette_app=lambda name="mplbed_starlette_app": fdf(f"""
32
+ {name} : Starlette, optional
33
+ The Starlette app to use for the `mplbed` routes, as returned by `mplbed_starlette_app_factory`.
34
+ If not provided, a new app will be created using the provided `mplbed_starlette_app_kwargs`.
35
+ """),
36
+ mplbed_starlette_app_kwargs=lambda name="mplbed_starlette_app_kwargs": fdf(f"""
37
+ {name} : dict, optional
38
+ Keyword arguments to pass to the Mplbed app factory if `mplbed_starlette_app` is not provided.
39
+ """),
40
+ manage_routing=fdf("""
41
+ manage_routing : bool, optional
42
+ Whether the ASGI middleware should manage routing. Default is True.
43
+ If you set this to False, you are responsible for routing requests to
44
+ the Mplbed app under the given `prefix`.
45
+ """),
46
+ native_app=fdf("""
47
+ native_app : Any, optional
48
+ The native app, e.g. the `Starlette` or `Quart` instance, which will
49
+ typically be saved in cased it is needed by the specific integration,
50
+ e.g. for rendering templates.
51
+ """),
52
+ )
mplbed/asgi.py ADDED
@@ -0,0 +1,121 @@
1
+ from contextvars import ContextVar
2
+ from typing import Any
3
+
4
+ from starlette.applications import Starlette
5
+ from starlette.routing import Match, Mount
6
+
7
+ from mplbed._doc_helpers import PARAMS_DS as D
8
+ from mplbed._doc_helpers import doc
9
+
10
+ _native_app: ContextVar[Any] = ContextVar("_native_app", default=None)
11
+ _prefix_and_app: ContextVar[tuple[str, Starlette] | None] = ContextVar("_prefix_and_app", default=None)
12
+
13
+
14
+ class MplbedMiddleware:
15
+ """
16
+ ASGI middleware to sets up routing and ensures context the correct context is
17
+ available so that the integrations work correctly.
18
+ """
19
+
20
+ @doc(
21
+ f"""
22
+ Initialize the MplbedMiddleware.
23
+
24
+ Parameters
25
+ ----------
26
+ default_app : ASGI3Application
27
+ The default ASGI app to use (i.e. the main app, typically yours).
28
+ {D.prefix}
29
+ {D.mplbed_starlette_app("app")}
30
+ {D.mplbed_starlette_app_kwargs("app_kwargs")}
31
+ {D.manage_routing}
32
+ {D.native_app}
33
+ """
34
+ )
35
+ def __init__(
36
+ self,
37
+ default_app,
38
+ *,
39
+ prefix: str,
40
+ app=None,
41
+ app_kwargs=None,
42
+ manage_routing=True,
43
+ native_app=None,
44
+ ):
45
+ from mplbed.server import mplbed_app_factory
46
+
47
+ self.main_app = default_app
48
+ if app is None and not manage_routing:
49
+ raise ValueError(
50
+ "If manage_routing is False, you must construct and provide the app yourself "
51
+ "(otherwise how do you plan to route to it?)"
52
+ )
53
+ if app is not None:
54
+ self.mplbed_app = app
55
+ elif app_kwargs is not None:
56
+ self.mplbed_app = mplbed_app_factory(**app_kwargs)
57
+ else:
58
+ self.mplbed_app = mplbed_app_factory()
59
+ self.prefix = prefix
60
+ self.manage_routing = manage_routing
61
+ self.native_app = native_app
62
+ if manage_routing:
63
+ self.mount = Mount(prefix, self.mplbed_app)
64
+
65
+ async def __call__(self, scope, receive, send):
66
+ """ASGI entry point for the middleware."""
67
+ with (
68
+ _native_app.set(self.native_app),
69
+ _prefix_and_app.set((self.prefix, self.mplbed_app)),
70
+ ):
71
+ if self.manage_routing:
72
+ assert self.mount
73
+ match, child_scope = self.mount.matches(scope)
74
+ if match != Match.NONE:
75
+ scope.update(child_scope)
76
+ await self.mplbed_app(scope, receive, send)
77
+ return
78
+ await self.main_app(scope, receive, send)
79
+
80
+
81
+ def get_native_app():
82
+ """Get the native app from the current context.
83
+
84
+ Returns
85
+ -------
86
+ Any
87
+ The native app, or None if not set.
88
+
89
+ """
90
+ return _native_app.get()
91
+
92
+
93
+ def get_asgi_app():
94
+ """Get the ASGI app from the current context.
95
+
96
+ Returns
97
+ -------
98
+ ASGI3Application | None
99
+ The ASGI app, or None if not set.
100
+
101
+ """
102
+ val = _prefix_and_app.get()
103
+ if val is not None:
104
+ _, app = val
105
+ return app
106
+ return None
107
+
108
+
109
+ def url_path_for(name, **path_params):
110
+ """Get the URL path for a given route name and parameters."""
111
+ prefix_and_app = path_params.pop("_prefix_and_app", None)
112
+ if prefix_and_app is None:
113
+ prefix_and_app = _prefix_and_app.get()
114
+ if prefix_and_app is None:
115
+ raise RuntimeError(
116
+ "Missing current prefix_and_app in context! "
117
+ "Did you install the MlpbedMiddleware? (_prefix_and_app was not passed)"
118
+ )
119
+ prefix, app = prefix_and_app
120
+ path = app.url_path_for(name, **path_params)
121
+ return prefix + path
mplbed/consts.py ADDED
@@ -0,0 +1,2 @@
1
+ DEFAULT_PREFIX = "/webagg"
2
+ HEAD_TEMPLATE_VARIABLE_NAME = "mplbed_head"
File without changes
mplbed/html/_impl.py ADDED
@@ -0,0 +1,149 @@
1
+ from mplbed.asgi import url_path_for
2
+
3
+
4
+ def figure_html_from_id(fig_id, *, target="inline", on_close="msg_discrete", prefix_and_app=None):
5
+ from json import dumps
6
+
7
+ ws_uri = url_path_for("websocket", fig_id=fig_id, _prefix_and_app=prefix_and_app)
8
+ ws_uri_str = dumps(ws_uri)
9
+ download_fig_uri = url_path_for("download_fig", fig_id=fig_id, fmt="{fmt}", _prefix_and_app=prefix_and_app)
10
+ download_fig_uri_str = dumps(download_fig_uri)
11
+ container = ""
12
+ setup_container = ""
13
+ if target == "inline":
14
+ container = "<div></div>"
15
+ target_js = "document.currentScript.previousElementSibling"
16
+ elif target == "body":
17
+ target_js = "document.body"
18
+ elif target == "modal":
19
+ container = """
20
+ <dialog closedby="any" style="padding: 1em; margin: 0 auto;"></dialog>
21
+ """.strip()
22
+ target_js = "document.currentScript.previousElementSibling"
23
+ setup_container = """
24
+ _mpl_webaggext.mk_modal(document.currentScript.previousElementSibling, fig);
25
+ """.strip()
26
+ else:
27
+ raise ValueError(f"Invalid target: {target}")
28
+ if on_close == "remove_dialog":
29
+ on_close = ["remove_parent", "dialog"]
30
+ on_close_js = dumps(on_close)
31
+ create_figure = f"""
32
+ let fig = _mpl_webaggext.new_fig(
33
+ {target_js},
34
+ {fig_id},
35
+ {ws_uri_str},
36
+ {download_fig_uri_str},
37
+ {on_close_js}
38
+ );
39
+ """.strip()
40
+ bits = (
41
+ container,
42
+ """
43
+ <script>
44
+ (function() {
45
+ """.strip(),
46
+ create_figure,
47
+ setup_container,
48
+ """
49
+ })();
50
+ </script>
51
+ """.strip(),
52
+ )
53
+ return "\n".join(bits)
54
+
55
+
56
+ def figure_html(figure, *retains, target="inline", on_close="msg_discrete"):
57
+ from matplotlib.pyplot import _get_backend_mod as get_backend_mod
58
+
59
+ from mplbed.server._impl import add_manager
60
+
61
+ manager = get_backend_mod().new_figure_manager_given_figure(id(figure), figure)
62
+ if hasattr(manager, "add_retains"):
63
+ manager.add_retains(*retains) # ty: ignore
64
+ else:
65
+ if not hasattr(figure, "_retains"):
66
+ figure._retains = []
67
+ figure._retains.extend(retains)
68
+ add_manager(manager)
69
+ return figure_html_from_id(manager.num, target=target, on_close=on_close)
70
+
71
+
72
+ def default_figure_page_template(*, head, fig, title):
73
+ return f"""<!DOCTYPE html>
74
+ <html lang="en">
75
+ <head>
76
+ {head}
77
+ <title>{title}</title>
78
+ </head>
79
+ <body>
80
+ {fig}
81
+ </body>
82
+ </html>
83
+ """
84
+
85
+
86
+ _template_preview = default_figure_page_template(head="{head}", fig="{fig}", title="{title}")
87
+ _indented = "\n".join(" " + line for line in _template_preview.splitlines())
88
+ default_figure_page_template.__doc__ = (
89
+ "Applies the following template to the given ``head``, ``fig`` and ``title``:\n\n"
90
+ ".. code-block:: html\n\n" + _indented + "\n"
91
+ )
92
+ del _template_preview, _indented
93
+
94
+
95
+ def head_content(*, core=False, prefix_and_app=None):
96
+ css_files = []
97
+ if not core:
98
+ css_files.extend(["page", "boilerplate", "fbm"])
99
+ css_files.append("mpl")
100
+ head_bits = []
101
+ for css_file in css_files:
102
+ static_url = url_path_for("static", path=f"css/{css_file}.css", _prefix_and_app=prefix_and_app)
103
+ head_bits.append(
104
+ f"""
105
+ <link rel="stylesheet" href="{static_url}" type="text/css">
106
+ """.strip()
107
+ )
108
+ mpl_js_uri = url_path_for("mpl_js", _prefix_and_app=prefix_and_app)
109
+ head_bits.append(
110
+ f"""
111
+ <script src="{mpl_js_uri}"></script>
112
+ """.strip()
113
+ )
114
+ webaggext_js_uri = url_path_for("webaggext_js", _prefix_and_app=prefix_and_app)
115
+ head_bits.append(
116
+ f"""
117
+ <script src="{webaggext_js_uri}"></script>
118
+ """.strip()
119
+ )
120
+ head_bits.append(
121
+ """
122
+ <style>
123
+ .mpl-toolbar {
124
+ position: relative;
125
+ padding-bottom: 2em;
126
+ }
127
+ .mpl-message {
128
+ position: absolute;
129
+ left: 0;
130
+ bottom: 0;
131
+ white-space: nowrap;
132
+ overflow-x: auto;
133
+ width: 100%;
134
+ }
135
+ .mpl-figure-root {
136
+ display: inline-flex !important;
137
+ flex-direction: column;
138
+ }
139
+ </style>
140
+ """.strip()
141
+ )
142
+ return "\n".join(head_bits)
143
+
144
+
145
+ def figure_page_html(fig, *, template=default_figure_page_template):
146
+ fig_html = figure_html(fig, target="body")
147
+ head = head_content(core=True)
148
+ resp_html = template(head=head, title="figure", fig=fig_html)
149
+ return resp_html
mplbed/html/raw.py ADDED
@@ -0,0 +1,15 @@
1
+ from mplbed.html._impl import (
2
+ default_figure_page_template,
3
+ figure_html,
4
+ figure_html_from_id,
5
+ figure_page_html,
6
+ head_content,
7
+ )
8
+
9
+ __all__ = [
10
+ "figure_html_from_id",
11
+ "figure_html",
12
+ "head_content",
13
+ "figure_page_html",
14
+ "default_figure_page_template",
15
+ ]
mplbed/html/safe.py ADDED
@@ -0,0 +1,22 @@
1
+ from mplbed.html import _impl
2
+
3
+
4
+ def _wrap_markupsafe(func):
5
+ def wrapper(*args, **kwargs):
6
+ from markupsafe import Markup
7
+
8
+ return Markup(func(*args, **kwargs))
9
+
10
+ return wrapper
11
+
12
+
13
+ figure_html_from_id = _wrap_markupsafe(_impl.figure_html_from_id)
14
+ head_content = _wrap_markupsafe(_impl.head_content)
15
+ figure_html = _wrap_markupsafe(_impl.figure_html)
16
+
17
+
18
+ __all__ = [
19
+ "figure_html_from_id",
20
+ "figure_html",
21
+ "head_content",
22
+ ]
@@ -0,0 +1,291 @@
1
+ import inspect
2
+ from functools import wraps
3
+ from typing import Any
4
+
5
+ from frozendict import frozendict
6
+
7
+ EMPTY_DICT = frozendict()
8
+
9
+
10
+ def figure_standalone_docstring_factory(*, response_name, template_comment, is_async, params_extra):
11
+ async_str = "async " if is_async else ""
12
+ await_str = "await " if is_async else ""
13
+ return f"""
14
+ Create a {response_name} object containing the HTML for the given figure using {template_comment}.
15
+
16
+ Example
17
+ -------
18
+
19
+ {async_str}def my_handler(request):
20
+ fig = Figure()
21
+ return {await_str}figure_page(fig)
22
+
23
+ Parameters
24
+ ----------
25
+ fig : matplotlib.figure.Figure or callable
26
+ A matplotlib Figure object or a callable that returns a Figure object.
27
+ {params_extra}
28
+ """
29
+
30
+
31
+ def figure_page_docstring_factory(*, response_name, template_comment, is_generic, wraps, params_extra):
32
+ async_str = "(async) " if is_generic else ""
33
+ await_str = "(await) " if is_generic else ""
34
+ generic_note = "All usages are generic with regards to async/sync" if is_generic else ""
35
+ return f"""
36
+ Decorator to create a {response_name} object containing the HTML for the given figure using {template_comment}.
37
+
38
+ This function can be called in two ways:
39
+
40
+ 1. As a decorator for a function taking any number of arguments and
41
+ returning a matplotlib Figure object, making it appropriate to use as a
42
+ decorator for view/handler function.
43
+ 2. As a decorator for a function taking no arguments and returning a
44
+ matplotlib Figure object, making it appropriate to use as a decorator for a
45
+ figure closure within a view/handler.
46
+
47
+ {generic_note}
48
+
49
+ Internally this decorator uses the function {wraps} to generate the HTML and create the {response_name}.
50
+
51
+ Example
52
+ -------
53
+
54
+ # Usage 1
55
+ @figure_page
56
+ {async_str} def my_handler(request):
57
+ return Figure()
58
+
59
+ # Usage 2
60
+ {async_str} def my_handler(request):
61
+ # ...
62
+ @figure_page
63
+ {async_str} def create_figure():
64
+ return Figure()
65
+ return {await_str} create_figure()
66
+
67
+ Parameters
68
+ ----------
69
+ {params_extra}
70
+ """
71
+
72
+
73
+ def _has_parameters(func):
74
+ return len(inspect.signature(func).parameters) > 0
75
+
76
+
77
+ def figure_standalone_factory(
78
+ name,
79
+ generate_html,
80
+ response_factory,
81
+ *,
82
+ kwarg_defaults=None,
83
+ response_factory_kwarg_keys=(),
84
+ make_async=None,
85
+ ds_response_name="response",
86
+ ds_template_comment="a template",
87
+ ds_params_extra,
88
+ ):
89
+ need_async = inspect.iscoroutinefunction(generate_html) or inspect.iscoroutinefunction(response_factory)
90
+ if make_async is None:
91
+ make_async = need_async
92
+ if need_async and not make_async:
93
+ raise ValueError(
94
+ "generate_html or response_factory is async, but make_async is False. "
95
+ "Set make_async to True to allow async usage."
96
+ )
97
+
98
+ def process_kwargs(kwargs):
99
+ kwargs = {**(kwarg_defaults or {}), **kwargs}
100
+ response_factory_kwargs = {}
101
+ for k in response_factory_kwarg_keys:
102
+ if k in kwargs:
103
+ response_factory_kwargs[k] = kwargs.pop(k)
104
+ return kwargs, response_factory_kwargs
105
+
106
+ if make_async:
107
+
108
+ async def figure_standalone(fig, **kwargs):
109
+ generate_html_kwargs, response_factory_kwargs = process_kwargs(kwargs)
110
+ if inspect.iscoroutinefunction(generate_html):
111
+ html = await generate_html(fig, **generate_html_kwargs)
112
+ else:
113
+ html = generate_html(fig, **generate_html_kwargs)
114
+ if inspect.iscoroutinefunction(response_factory):
115
+ return await response_factory(html, **response_factory_kwargs)
116
+ else:
117
+ return response_factory(html, **response_factory_kwargs)
118
+ else:
119
+
120
+ def figure_standalone(fig, **kwargs):
121
+ generate_html_kwargs, response_factory_kwargs = process_kwargs(kwargs)
122
+ return response_factory(generate_html(fig, **generate_html_kwargs), **response_factory_kwargs)
123
+
124
+ figure_standalone.__name__ = name
125
+ figure_standalone.__doc__ = figure_standalone_docstring_factory(
126
+ response_name=ds_response_name,
127
+ template_comment=ds_template_comment,
128
+ is_async=make_async,
129
+ params_extra=ds_params_extra,
130
+ )
131
+ return figure_standalone
132
+
133
+
134
+ def figure_page_factory(
135
+ name,
136
+ figure_standalone,
137
+ *,
138
+ ds_response_name="response",
139
+ ds_template_comment="a template",
140
+ ds_params_extra,
141
+ ):
142
+
143
+ needs_async = inspect.iscoroutinefunction(figure_standalone)
144
+
145
+ def figure_page(inner, **figure_page_kwargs):
146
+ if inspect.iscoroutinefunction(inner):
147
+ if _has_parameters(inner):
148
+ # Async view style figure creating function
149
+ @wraps(inner)
150
+ async def async_view_wrapper(*args, **kwargs):
151
+ actual_fig = await inner(*args, **kwargs)
152
+ figure_standalone_kwargs: dict[str, Any] = kwargs.pop("figure_standalone_kwargs", {})
153
+ if needs_async:
154
+ return await figure_standalone(
155
+ actual_fig,
156
+ **{**figure_page_kwargs, **figure_standalone_kwargs},
157
+ )
158
+ else:
159
+ return figure_standalone(
160
+ actual_fig,
161
+ **{**figure_page_kwargs, **figure_standalone_kwargs},
162
+ )
163
+
164
+ wrapper = async_view_wrapper
165
+ else:
166
+ # Async closure style figure creating function
167
+ @wraps(inner)
168
+ async def async_closure_wrapper(*, figure_standalone_kwargs=EMPTY_DICT):
169
+ actual_fig = await inner()
170
+ if needs_async:
171
+ return await figure_standalone(
172
+ actual_fig,
173
+ **{**figure_page_kwargs, **figure_standalone_kwargs},
174
+ )
175
+ else:
176
+ return figure_standalone(
177
+ actual_fig,
178
+ **{**figure_page_kwargs, **figure_standalone_kwargs},
179
+ )
180
+
181
+ wrapper = async_closure_wrapper
182
+ else:
183
+ if needs_async:
184
+ raise ValueError(f"Decorated function must be async for {name} (wrapping {figure_standalone.__name__})")
185
+ if _has_parameters(inner):
186
+ # View style figure creating function
187
+ @wraps(inner)
188
+ def view_wrapper(*args, **kwargs):
189
+ actual_fig = figure_standalone(*args, **kwargs)
190
+ figure_standalone_kwargs: dict[str, Any] = kwargs.pop("figure_standalone_kwargs", {})
191
+ return figure_page(actual_fig, **{**figure_page_kwargs, **figure_standalone_kwargs})
192
+
193
+ wrapper = view_wrapper
194
+ else:
195
+ # Closure style figure creating function
196
+ @wraps(inner)
197
+ def closure_wrapper(*, figure_standalone_kwargs=EMPTY_DICT):
198
+ actual_fig = figure_standalone()
199
+ return figure_page(actual_fig, **{**figure_page_kwargs, **figure_standalone_kwargs})
200
+
201
+ wrapper = closure_wrapper
202
+ wrapper.__name__ = name
203
+ return wrapper
204
+
205
+ figure_page.__doc__ = figure_page_docstring_factory(
206
+ response_name=ds_response_name,
207
+ template_comment=ds_template_comment,
208
+ wraps=figure_standalone.__name__,
209
+ is_generic=not needs_async,
210
+ params_extra=ds_params_extra,
211
+ )
212
+ return figure_page
213
+
214
+
215
+ def mk_figure_page_variants(
216
+ suffix,
217
+ generate_html,
218
+ response_factory,
219
+ *,
220
+ kwarg_defaults=None,
221
+ response_factory_kwarg_keys=(),
222
+ ds_response_name="response",
223
+ ds_template_comment="a template",
224
+ ds_params_extra,
225
+ ):
226
+ need_async = inspect.iscoroutinefunction(generate_html) or inspect.iscoroutinefunction(response_factory)
227
+ results = {}
228
+ standalone_args = dict(
229
+ generate_html=generate_html,
230
+ response_factory=response_factory,
231
+ kwarg_defaults=kwarg_defaults,
232
+ response_factory_kwarg_keys=response_factory_kwarg_keys,
233
+ ds_response_name=ds_response_name,
234
+ ds_template_comment=ds_template_comment,
235
+ ds_params_extra=ds_params_extra,
236
+ )
237
+ page_args = dict(
238
+ ds_response_name=ds_response_name,
239
+ ds_template_comment=ds_template_comment,
240
+ ds_params_extra=ds_params_extra,
241
+ )
242
+ if need_async:
243
+ figure_standalone = figure_standalone_factory(f"figure_standalone{suffix}", **standalone_args, make_async=True)
244
+ results[f"figure_standalone{suffix}"] = figure_standalone
245
+ results[f"figure_page{suffix}"] = figure_page_factory(f"figure_page{suffix}", figure_standalone, **page_args)
246
+ else:
247
+ figure_standalone = figure_standalone_factory(
248
+ f"figure_standalone{suffix}",
249
+ **standalone_args,
250
+ make_async=False,
251
+ )
252
+ results[f"figure_standalone{suffix}"] = figure_standalone
253
+ results[f"figure_page{suffix}"] = figure_page_factory(f"figure_page{suffix}", figure_standalone, **page_args)
254
+ figure_standalone_async = figure_standalone_factory(
255
+ f"figure_standalone{suffix}_async", **standalone_args, make_async=True
256
+ )
257
+ results[f"figure_standalone{suffix}_async"] = figure_standalone_async
258
+ results[f"figure_page{suffix}_async"] = figure_page_factory(
259
+ f"figure_page{suffix}_async", figure_standalone_async, **page_args
260
+ )
261
+ return results
262
+
263
+
264
+ def setup_page_docstring(template_func):
265
+ from mplbed._doc_helpers import PARAMS_DS, DotAccessDict, fdf
266
+
267
+ def decorator(wrapped):
268
+ wrapped.__doc__ = template_func(
269
+ DotAccessDict(
270
+ **PARAMS_DS,
271
+ do_install_middleware=fdf("""
272
+ do_install_middleware : bool, optional
273
+ Whether to install the mplbed middleware on the given app. Default is True.
274
+ """),
275
+ do_register_context_processor=fdf("""
276
+ do_register_context_processor : bool, optional
277
+ Whether to register the mplbed context processor on the given app. Default is True.
278
+ """),
279
+ do_use_mpl_backend=fdf("""
280
+ do_use_mpl_backend : bool, optional
281
+ Whether to setup the matplotlib backend for rendering figures. Default is True.
282
+ """),
283
+ use_webaggext_backend=fdf("""
284
+ use_webaggext_backend : bool, optional
285
+ Whether to use webaggext rather than the basic webagg backend for rendering figures. Default is True.
286
+ """),
287
+ )
288
+ )
289
+ return wrapped
290
+
291
+ return decorator