|
import json |
|
import pkgutil |
|
import textwrap |
|
from typing import Callable, Dict, Optional, Tuple, Any, Union |
|
import uuid |
|
|
|
from ._vegafusion_data import compile_with_vegafusion, using_vegafusion |
|
from .plugin_registry import PluginRegistry, PluginEnabler |
|
from .mimebundle import spec_to_mimebundle |
|
from .schemapi import validate_jsonschema |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MimeBundleDataType = Dict[str, Any] |
|
MimeBundleMetaDataType = Dict[str, Any] |
|
MimeBundleType = Union[ |
|
MimeBundleDataType, Tuple[MimeBundleDataType, MimeBundleMetaDataType] |
|
] |
|
RendererType = Callable[..., MimeBundleType] |
|
|
|
DefaultRendererReturnType = Tuple[ |
|
Dict[str, Union[str, dict]], Dict[str, Dict[str, Any]] |
|
] |
|
|
|
|
|
class RendererRegistry(PluginRegistry[RendererType]): |
|
entrypoint_err_messages = { |
|
"notebook": textwrap.dedent( |
|
""" |
|
To use the 'notebook' renderer, you must install the vega package |
|
and the associated Jupyter extension. |
|
See https://altair-viz.github.io/getting_started/installation.html |
|
for more information. |
|
""" |
|
), |
|
"altair_viewer": textwrap.dedent( |
|
""" |
|
To use the 'altair_viewer' renderer, you must install the altair_viewer |
|
package; see http://github.com/altair-viz/altair_viewer/ |
|
for more information. |
|
""" |
|
), |
|
} |
|
|
|
def set_embed_options( |
|
self, |
|
defaultStyle: Optional[Union[bool, str]] = None, |
|
renderer: Optional[str] = None, |
|
width: Optional[int] = None, |
|
height: Optional[int] = None, |
|
padding: Optional[int] = None, |
|
scaleFactor: Optional[float] = None, |
|
actions: Optional[Union[bool, Dict[str, bool]]] = None, |
|
format_locale: Optional[Union[str, dict]] = None, |
|
time_format_locale: Optional[Union[str, dict]] = None, |
|
**kwargs, |
|
) -> PluginEnabler: |
|
"""Set options for embeddings of Vega & Vega-Lite charts. |
|
|
|
Options are fully documented at https://github.com/vega/vega-embed. |
|
Similar to the `enable()` method, this can be used as either |
|
a persistent global switch, or as a temporary local setting using |
|
a context manager (i.e. a `with` statement). |
|
|
|
Parameters |
|
---------- |
|
defaultStyle : bool or string |
|
Specify a default stylesheet for embed actions. |
|
renderer : string |
|
The renderer to use for the view. One of "canvas" (default) or "svg" |
|
width : integer |
|
The view width in pixels |
|
height : integer |
|
The view height in pixels |
|
padding : integer |
|
The view padding in pixels |
|
scaleFactor : number |
|
The number by which to multiply the width and height (default 1) |
|
of an exported PNG or SVG image. |
|
actions : bool or dict |
|
Determines if action links ("Export as PNG/SVG", "View Source", |
|
"View Vega" (only for Vega-Lite), "Open in Vega Editor") are |
|
included with the embedded view. If the value is true, all action |
|
links will be shown and none if the value is false. This property |
|
can take a key-value mapping object that maps keys (export, source, |
|
compiled, editor) to boolean values for determining if |
|
each action link should be shown. |
|
format_locale : str or dict |
|
d3-format locale name or dictionary. Defaults to "en-US" for United States English. |
|
See https://github.com/d3/d3-format/tree/main/locale for available names and example |
|
definitions. |
|
time_format_locale : str or dict |
|
d3-time-format locale name or dictionary. Defaults to "en-US" for United States English. |
|
See https://github.com/d3/d3-time-format/tree/main/locale for available names and example |
|
definitions. |
|
**kwargs : |
|
Additional options are passed directly to embed options. |
|
""" |
|
options: Dict[str, Optional[Union[bool, str, float, Dict[str, bool]]]] = { |
|
"defaultStyle": defaultStyle, |
|
"renderer": renderer, |
|
"width": width, |
|
"height": height, |
|
"padding": padding, |
|
"scaleFactor": scaleFactor, |
|
"actions": actions, |
|
"formatLocale": format_locale, |
|
"timeFormatLocale": time_format_locale, |
|
} |
|
kwargs.update({key: val for key, val in options.items() if val is not None}) |
|
return self.enable(None, embed_options=kwargs) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Displayable: |
|
"""A base display class for VegaLite v1/v2. |
|
|
|
This class takes a VegaLite v1/v2 spec and does the following: |
|
|
|
1. Optionally validates the spec against a schema. |
|
2. Uses the RendererPlugin to grab a renderer and call it when the |
|
IPython/Jupyter display method (_repr_mimebundle_) is called. |
|
|
|
The spec passed to this class must be fully schema compliant and already |
|
have the data portion of the spec fully processed and ready to serialize. |
|
In practice, this means, the data portion of the spec should have been passed |
|
through appropriate data model transformers. |
|
""" |
|
|
|
renderers: Optional[RendererRegistry] = None |
|
schema_path = ("altair", "") |
|
|
|
def __init__(self, spec: dict, validate: bool = False) -> None: |
|
self.spec = spec |
|
self.validate = validate |
|
self._validate() |
|
|
|
def _validate(self) -> None: |
|
"""Validate the spec against the schema.""" |
|
data = pkgutil.get_data(*self.schema_path) |
|
assert data is not None |
|
schema_dict: dict = json.loads(data.decode("utf-8")) |
|
validate_jsonschema( |
|
self.spec, |
|
schema_dict, |
|
) |
|
|
|
def _repr_mimebundle_( |
|
self, include: Any = None, exclude: Any = None |
|
) -> MimeBundleType: |
|
"""Return a MIME bundle for display in Jupyter frontends.""" |
|
if self.renderers is not None: |
|
renderer_func = self.renderers.get() |
|
assert renderer_func is not None |
|
return renderer_func(self.spec) |
|
else: |
|
return {} |
|
|
|
|
|
def default_renderer_base( |
|
spec: dict, mime_type: str, str_repr: str, **options |
|
) -> DefaultRendererReturnType: |
|
"""A default renderer for Vega or VegaLite that works for modern frontends. |
|
|
|
This renderer works with modern frontends (JupyterLab, nteract) that know |
|
how to render the custom VegaLite MIME type listed above. |
|
""" |
|
|
|
from altair.vegalite.v5.display import VEGA_MIME_TYPE, VEGALITE_MIME_TYPE |
|
|
|
assert isinstance(spec, dict) |
|
bundle: Dict[str, Union[str, dict]] = {} |
|
metadata: Dict[str, Dict[str, Any]] = {} |
|
|
|
if using_vegafusion(): |
|
spec = compile_with_vegafusion(spec) |
|
|
|
|
|
|
|
if mime_type == VEGALITE_MIME_TYPE: |
|
mime_type = VEGA_MIME_TYPE |
|
|
|
bundle[mime_type] = spec |
|
bundle["text/plain"] = str_repr |
|
if options: |
|
metadata[mime_type] = options |
|
return bundle, metadata |
|
|
|
|
|
def json_renderer_base( |
|
spec: dict, str_repr: str, **options |
|
) -> DefaultRendererReturnType: |
|
"""A renderer that returns a MIME type of application/json. |
|
|
|
In JupyterLab/nteract this is rendered as a nice JSON tree. |
|
""" |
|
return default_renderer_base( |
|
spec, mime_type="application/json", str_repr=str_repr, **options |
|
) |
|
|
|
|
|
class HTMLRenderer: |
|
"""Object to render charts as HTML, with a unique output div each time""" |
|
|
|
def __init__(self, output_div: str = "altair-viz-{}", **kwargs) -> None: |
|
self._output_div = output_div |
|
self.kwargs = kwargs |
|
|
|
@property |
|
def output_div(self) -> str: |
|
return self._output_div.format(uuid.uuid4().hex) |
|
|
|
def __call__(self, spec: dict, **metadata) -> Dict[str, str]: |
|
kwargs = self.kwargs.copy() |
|
kwargs.update(metadata) |
|
|
|
|
|
return spec_to_mimebundle( |
|
spec, format="html", output_div=self.output_div, **kwargs |
|
) |
|
|