bencher.plugins =============== .. py:module:: bencher.plugins .. autoapi-nested-parse:: Plot plugin infrastructure for bencher. The public surface a plugin author depends on: - BenchData: frozen value handed to render(), defines what plugins receive. - PlotPlugin: protocol all plugins satisfy. - plot_plugin: decorator for the function form. - register_plugin / unregister_plugin / get_registry: explicit registration API. - PlotFilter / VarRange (re-exported): for declaring match rules. Bencher's internal call sites do not yet depend on this package. Migration of the built-in chart types onto the plugin mechanism happens in subsequent PRs. Submodules ---------- .. toctree:: :maxdepth: 1 /autoapi/bencher/plugins/bench_data/index /autoapi/bencher/plugins/builtins/index /autoapi/bencher/plugins/plugin/index /autoapi/bencher/plugins/registry/index Attributes ---------- .. autoapisummary:: bencher.plugins.ENTRY_POINT_GROUP Classes ------- .. autoapisummary:: bencher.plugins.PlotFilter bencher.plugins.VarRange bencher.plugins.BenchData bencher.plugins.CacheHandle bencher.plugins.RunMeta bencher.plugins.PlotPlugin bencher.plugins.PluginRegistry Functions --------- .. autoapisummary:: bencher.plugins.plot_plugin bencher.plugins.get_registry bencher.plugins.register_plugin bencher.plugins.unregister_plugin Package Contents ---------------- .. py:class:: PlotFilter A class for representing the types of results a plot is able to represent. .. py:attribute:: float_range :type: VarRange .. py:attribute:: cat_range :type: VarRange .. py:attribute:: vector_len :type: VarRange .. py:attribute:: result_vars :type: VarRange .. py:attribute:: panel_range :type: VarRange .. py:attribute:: repeats_range :type: VarRange .. py:attribute:: input_range :type: VarRange .. py:method:: match_all() -> PlotFilter :classmethod: A filter that matches every sweep shape. The default ``PlotFilter()`` ranges are restrictive (``VarRange()`` matches nothing), which suits plots that opt in to specific shapes. Plugins that do their own internal shape handling should use this instead. .. py:method:: matches_result(plt_cnt_cfg: bencher.plotting.plt_cnt_cfg.PltCntCfg, plot_name: str, override: bool) -> PlotMatchesResult Checks if the result data signature matches the type of data the plot is able to display. :param plt_cnt_cfg: Configuration containing counts of different plot elements :type plt_cnt_cfg: PltCntCfg :param plot_name: Name of the plot being checked :type plot_name: str :param override: Whether to override filter matching rules :type override: bool :returns: Object containing match results and information :rtype: PlotMatchesResult .. py:class:: VarRange(lower_bound: int = 0, upper_bound: int = -1) A VarRange represents the bounded and unbounded ranges of integers. This class is used to define filters for various variable types. For example by defining cat_var = VarRange(0,0), calling matches(0) will return true, but any other integer will not match. You can also have unbounded ranges for example VarRange(2,None) will match to 2,3,4... up to infinity. for By default the lower and upper bounds are set to -1 so so that no matter what value is passed to matches() will return false. Matches only takes 0 and positive integers. .. py:attribute:: lower_bound :value: 0 .. py:attribute:: upper_bound :value: -1 .. py:method:: matches(val: int) -> bool Checks that a value is within the variable range. lower_bound and upper_bound are inclusive (lower_bound<=val<=upper_bound ) :param val: A positive integer representing a number of items :type val: int :returns: True if the items is within the range, False otherwise. :rtype: bool :raises ValueError: If val < 0 .. py:method:: matches_info(val: int, name: str) -> tuple[bool, str] Get matching info for a value with a descriptive name. :param val: A positive integer to check against the range :type val: int :param name: A descriptive name for the value being checked, used in the output string :type name: str :returns: A tuple containing: - bool: True if the value matches the range, False otherwise - str: A formatted string describing the match result :rtype: tuple[bool, str] .. py:method:: __str__() -> str .. py:class:: BenchData Frozen value type handed to plot plugins. The stable public contract surface for plugin authors — internal bencher refactors must preserve this shape. .. py:attribute:: dataset :type: xarray.Dataset .. py:attribute:: input_vars :type: tuple :value: () .. py:attribute:: result_vars :type: tuple :value: () .. py:attribute:: plt_cnt_cfg :type: Optional[bencher.plotting.plt_cnt_cfg.PltCntCfg] :value: None .. py:attribute:: run_meta :type: RunMeta .. py:attribute:: optimizer_study :type: Optional[Any] :value: None .. py:attribute:: baseline_runs :type: tuple[BenchData, Ellipsis] :value: () .. py:attribute:: cache :type: Optional[CacheHandle] :value: None .. py:attribute:: legacy_result :type: Optional[Any] :value: None .. py:attribute:: render_kwargs :type: dict .. py:method:: has(capability: str) -> bool True when an optional context field is populated. Used by PlotFilter.requires to gate plugins that need fields beyond dataset+vars. .. py:method:: with_changes(**kwargs) -> BenchData .. py:method:: fake(*, dataset: Optional[xarray.Dataset] = None, input_vars: tuple = (), result_vars: tuple = (), plt_cnt_cfg: Optional[bencher.plotting.plt_cnt_cfg.PltCntCfg] = None, **overrides) -> BenchData :classmethod: Construct a minimal BenchData for plugin unit tests. Defaults dataset to an empty xr.Dataset and plt_cnt_cfg to a zero-counted config so plugin authors can construct a usable handle in one line. .. py:class:: CacheHandle Bases: :py:obj:`Protocol` Plugin-accessible memoization surface. Bencher core supplies a concrete handle; plugins treat it as opaque key/value storage. .. py:method:: get(key: str) -> Optional[Any] .. py:method:: set(key: str, value: Any) -> None .. py:class:: RunMeta .. py:attribute:: name :type: str :value: '' .. py:attribute:: timestamp :type: datetime.datetime .. py:attribute:: sweep_hash :type: str :value: '' .. py:class:: PlotPlugin Bases: :py:obj:`Protocol` Stable public contract for plot plugins. A plugin renders a BenchData handle into a Panel-embeddable view. The plugin owns internal composition (linked hv.Layout, plotly.subplots, full Rerun blueprints, ...); bencher only does outer Panel-level composition over plugin outputs. .. py:attribute:: name :type: str .. py:attribute:: backend :type: str .. py:attribute:: match :type: bencher.plotting.plot_filter.PlotFilter .. py:attribute:: priority :type: int .. py:attribute:: requires :type: frozenset[str] .. py:method:: render(data: bencher.plugins.bench_data.BenchData) -> panel.viewable.Viewable .. py:function:: plot_plugin(*, name: str, backend: str = 'user', match: Optional[bencher.plotting.plot_filter.PlotFilter] = None, priority: int = 0, requires: Optional[frozenset[str] | set[str] | tuple[str, Ellipsis]] = None, register: bool = True) -> Callable[[Callable[[bencher.plugins.bench_data.BenchData], panel.viewable.Viewable]], _FunctionPlugin] Wrap a function as a plot plugin and (by default) register it with the global registry. Returns the plugin object so callers can also register manually with register=False. .. py:data:: ENTRY_POINT_GROUP :value: 'bencher.plot_plugins' .. py:class:: PluginRegistry In-process registry of plot plugins, keyed by (name, backend). `name` is the chart type ("line", "heatmap", ...); `backend` is the rendering library namespace ("holoviews", "rerun", ...). Several backends may implement the same chart type; selection resolves each chart type to one implementation — the preferred backend when given, otherwise the highest-priority one. Registering an existing (name, backend) pair replaces it, which is the documented override mechanism (a user plugin replaces a built-in by sharing its name and backend, or outranks it from a different backend via priority/preference). .. py:attribute:: _plugins :type: dict[tuple[str, str], bencher.plugins.plugin.PlotPlugin] .. py:attribute:: _entry_points_loaded :value: False .. py:method:: register(plugin: bencher.plugins.plugin.PlotPlugin) -> None .. py:method:: unregister(name: str, backend: Optional[str] = None) -> None Remove a plugin. With no backend, removes every backend's implementation of that chart type. .. py:method:: clear() -> None .. py:method:: mark_entry_points_loaded() -> None Skip the entry-point scan on next lookup. Test-only helper. .. py:method:: get(name: str, backend: Optional[str] = None) -> Optional[bencher.plugins.plugin.PlotPlugin] Resolve a chart type to one implementation. With a backend, exact lookup. Without, the preferred implementation: highest priority among all backends providing `name` (ties broken by backend string for determinism). .. py:method:: implementations(name: str) -> tuple[bencher.plugins.plugin.PlotPlugin, Ellipsis] Every backend's implementation of a chart type, highest priority first. .. py:method:: all() -> tuple[bencher.plugins.plugin.PlotPlugin, Ellipsis] .. py:method:: _ensure_entry_points_loaded() -> None .. py:method:: _register_loaded(ep_name: str, obj) -> None .. py:method:: select(data: bencher.plugins.bench_data.BenchData, *, include: Optional[Iterable[str]] = None, exclude: Optional[Iterable[str]] = None, backend: Optional[str] = None, only: Optional[str] = None) -> tuple[bencher.plugins.plugin.PlotPlugin, Ellipsis] Return one matching implementation per chart type, by descending priority. - `only` short-circuits to a single named chart type (no match-filter check; explicit opt-in by name implies the user knows what they want). - `include` / `exclude` filter the candidate set by chart-type name. - `backend` states the *preferred* backend: where a chart type is implemented by several backends, the preferred one is chosen when it matches; chart types the preferred backend does not provide still render through their best other implementation. This is what lets a config flag swap the rendering library under the same set of plotters. .. py:method:: render(data: bencher.plugins.bench_data.BenchData, *, include: Optional[Iterable[str]] = None, exclude: Optional[Iterable[str]] = None, backend: Optional[str] = None, only: Optional[str] = None, strict: bool = False) -> tuple[tuple[str, panel.viewable.Viewable], Ellipsis] Run every selected plugin, returning (name, pane) pairs in priority order. With strict=False (default) a render exception is caught and replaced with a visible error pane so one broken plugin doesn't kill the report. strict=True re-raises the first failure — intended for development. .. py:function:: get_registry() -> PluginRegistry .. py:function:: register_plugin(plugin: bencher.plugins.plugin.PlotPlugin) -> bencher.plugins.plugin.PlotPlugin .. py:function:: unregister_plugin(name: str, backend: Optional[str] = None) -> None