Spaces:
Running
Running
File size: 8,852 Bytes
b72ab63 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 |
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
# ==============================================================================
# Renderer registry
# ==============================================================================
# MimeBundleType needs to be the same as what are acceptable return values
# for _repr_mimebundle_,
# see https://ipython.readthedocs.io/en/stable/config/integrating.html#MyObject._repr_mimebundle_
MimeBundleDataType = Dict[str, Any]
MimeBundleMetaDataType = Dict[str, Any]
MimeBundleType = Union[
MimeBundleDataType, Tuple[MimeBundleDataType, MimeBundleMetaDataType]
]
RendererType = Callable[..., MimeBundleType]
# Subtype of MimeBundleType as more specific in the values of the dictionaries
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)
# ==============================================================================
# VegaLite v1/v2 renderer logic
# ==============================================================================
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.
"""
# Local import to avoid circular ImportError
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)
# Swap mimetype from Vega-Lite to Vega.
# If mimetype was JSON, leave it alone
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)
# To get proper return value type, would need to write complex
# overload signatures for spec_to_mimebundle based on `format`
return spec_to_mimebundle( # type: ignore[return-value]
spec, format="html", output_div=self.output_div, **kwargs
)
|