import uuid from collections import OrderedDict from functools import lru_cache, partial from html import escape from importlib.resources import read_binary from .formatting import inline_variable_array_repr, short_data_repr from .options import _get_boolean_with_default STATIC_FILES = ( ("xarray.static.html", "icons-svg-inline.html"), ("xarray.static.css", "style.css"), ) @lru_cache(None) def _load_static_files(): """Lazily load the resource files into memory the first time they are needed""" return [ read_binary(package, resource).decode("utf-8") for package, resource in STATIC_FILES ] def short_data_repr_html(array): """Format "data" for DataArray and Variable.""" internal_data = getattr(array, "variable", array)._data if hasattr(internal_data, "_repr_html_"): return internal_data._repr_html_() text = escape(short_data_repr(array)) return f"
{text}
" def format_dims(dims, coord_names): if not dims: return "" dim_css_map = { k: " class='xr-has-index'" if k in coord_names else "" for k, v in dims.items() } dims_li = "".join( f"
  • " f"{escape(str(dim))}: {size}
  • " for dim, size in dims.items() ) return f"" def summarize_attrs(attrs): attrs_dl = "".join( f"
    {escape(str(k))} :
    " f"
    {escape(str(v))}
    " for k, v in attrs.items() ) return f"
    {attrs_dl}
    " def _icon(icon_name): # icon_name should be defined in xarray/static/html/icon-svg-inline.html return ( "" "" "" "".format(icon_name) ) def _summarize_coord_multiindex(name, coord): preview = f"({', '.join(escape(l) for l in coord.level_names)})" return summarize_variable( name, coord, is_index=True, dtype="MultiIndex", preview=preview ) def summarize_coord(name, var): is_index = name in var.dims if is_index: coord = var.variable.to_index_variable() if coord.level_names is not None: coords = {name: _summarize_coord_multiindex(name, coord)} for lname in coord.level_names: var = coord.get_level_variable(lname) coords[lname] = summarize_variable(lname, var) return coords return {name: summarize_variable(name, var, is_index)} def summarize_coords(variables): coords = {} for k, v in variables.items(): coords.update(**summarize_coord(k, v)) vars_li = "".join(f"
  • {v}
  • " for v in coords.values()) return f"" def summarize_variable(name, var, is_index=False, dtype=None, preview=None): variable = var.variable if hasattr(var, "variable") else var cssclass_idx = " class='xr-has-index'" if is_index else "" dims_str = f"({', '.join(escape(dim) for dim in var.dims)})" name = escape(str(name)) dtype = dtype or escape(str(var.dtype)) # "unique" ids required to expand/collapse subsections attrs_id = "attrs-" + str(uuid.uuid4()) data_id = "data-" + str(uuid.uuid4()) disabled = "" if len(var.attrs) else "disabled" preview = preview or escape(inline_variable_array_repr(variable, 35)) attrs_ul = summarize_attrs(var.attrs) data_repr = short_data_repr_html(variable) attrs_icon = _icon("icon-file-text2") data_icon = _icon("icon-database") return ( f"
    {name}
    " f"
    {dims_str}
    " f"
    {dtype}
    " f"
    {preview}
    " f"" f"" f"" f"" f"
    {attrs_ul}
    " f"
    {data_repr}
    " ) def summarize_vars(variables): vars_li = "".join( f"
  • {summarize_variable(k, v)}
  • " for k, v in variables.items() ) return f"" def collapsible_section( name, inline_details="", details="", n_items=None, enabled=True, collapsed=False ): # "unique" id to expand/collapse the section data_id = "section-" + str(uuid.uuid4()) has_items = n_items is not None and n_items n_items_span = "" if n_items is None else f" ({n_items})" enabled = "" if enabled and has_items else "disabled" collapsed = "" if collapsed or not has_items else "checked" tip = " title='Expand/collapse section'" if enabled else "" return ( f"" f"" f"
    {inline_details}
    " f"
    {details}
    " ) def _mapping_section( mapping, name, details_func, max_items_collapse, expand_option_name, enabled=True ): n_items = len(mapping) expanded = _get_boolean_with_default( expand_option_name, n_items < max_items_collapse ) collapsed = not expanded return collapsible_section( name, details=details_func(mapping), n_items=n_items, enabled=enabled, collapsed=collapsed, ) def dim_section(obj): dim_list = format_dims(obj.dims, list(obj.coords)) return collapsible_section( "Dimensions", inline_details=dim_list, enabled=False, collapsed=True ) def array_section(obj): # "unique" id to expand/collapse the section data_id = "section-" + str(uuid.uuid4()) collapsed = ( "checked" if _get_boolean_with_default("display_expand_data", default=True) else "" ) variable = getattr(obj, "variable", obj) preview = escape(inline_variable_array_repr(variable, max_width=70)) data_repr = short_data_repr_html(obj) data_icon = _icon("icon-database") return ( "
    " f"" f"" f"
    {preview}
    " f"
    {data_repr}
    " "
    " ) coord_section = partial( _mapping_section, name="Coordinates", details_func=summarize_coords, max_items_collapse=25, expand_option_name="display_expand_coords", ) datavar_section = partial( _mapping_section, name="Data variables", details_func=summarize_vars, max_items_collapse=15, expand_option_name="display_expand_data_vars", ) attr_section = partial( _mapping_section, name="Attributes", details_func=summarize_attrs, max_items_collapse=10, expand_option_name="display_expand_attrs", ) def _obj_repr(obj, header_components, sections): """Return HTML repr of an xarray object. If CSS is not injected (untrusted notebook), fallback to the plain text repr. """ header = f"
    {''.join(h for h in header_components)}
    " sections = "".join(f"
  • {s}
  • " for s in sections) icons_svg, css_style = _load_static_files() return ( "
    " f"{icons_svg}" f"
    {escape(repr(obj))}
    " "" "
    " ) def array_repr(arr): dims = OrderedDict((k, v) for k, v in zip(arr.dims, arr.shape)) obj_type = "xarray.{}".format(type(arr).__name__) arr_name = f"'{arr.name}'" if getattr(arr, "name", None) else "" coord_names = list(arr.coords) if hasattr(arr, "coords") else [] header_components = [ f"
    {obj_type}
    ", f"
    {arr_name}
    ", format_dims(dims, coord_names), ] sections = [array_section(arr)] if hasattr(arr, "coords"): sections.append(coord_section(arr.coords)) sections.append(attr_section(arr.attrs)) return _obj_repr(arr, header_components, sections) def dataset_repr(ds): obj_type = "xarray.{}".format(type(ds).__name__) header_components = [f"
    {escape(obj_type)}
    "] sections = [ dim_section(ds), coord_section(ds.coords), datavar_section(ds.data_vars), attr_section(ds.attrs), ] return _obj_repr(ds, header_components, sections)