xopr.xarray_accessor.formatting_html
1# This is a very lightly modified version of: 2# https://github.com/pydata/xarray/blob/8796d55a8506f2d3bd50a265ecbc72e3f2f76f18/xarray/core/formatting_html.py 3# It is designed to implement a very simple nested dictionary _repr_html_ 4# Eventually, we should either figure out how to more cleanly integreate a custom _repr_html_ 5# or make it generic enough to get a PR into xarray. 6 7import uuid 8from collections import OrderedDict 9from collections.abc import Mapping 10from functools import lru_cache, partial 11from html import escape 12from importlib.resources import files 13from math import ceil 14from typing import Literal 15 16from xarray.core.datatree import DataTree 17from xarray.core.formatting import ( 18 inherited_vars, 19 inline_index_repr, 20 inline_variable_array_repr, 21 short_data_repr, 22) 23from xarray.core.options import OPTIONS, _get_boolean_with_default 24 25STATIC_FILES = ( 26 ("xarray.static.html", "icons-svg-inline.html"), 27 ("xopr.xarray_accessor", "style.css"), 28) 29 30 31@lru_cache(None) 32def _load_static_files(): 33 """Lazily load the resource files into memory the first time they are needed""" 34 return [ 35 files(package).joinpath(resource).read_text(encoding="utf-8") 36 for package, resource in STATIC_FILES 37 ] 38 39 40def short_data_repr_html(array) -> str: 41 """Format "data" for DataArray and Variable.""" 42 internal_data = getattr(array, "variable", array)._data 43 if hasattr(internal_data, "_repr_html_"): 44 return internal_data._repr_html_() 45 text = escape(short_data_repr(array)) 46 return f"<pre>{text}</pre>" 47 48 49def format_dims(dim_sizes, dims_with_index) -> str: 50 if not dim_sizes: 51 return "" 52 53 dim_css_map = { 54 dim: " class='xr-has-index'" if dim in dims_with_index else "" 55 for dim in dim_sizes 56 } 57 58 dims_li = "".join( 59 f"<li><span{dim_css_map[dim]}>{escape(str(dim))}</span>: {size}</li>" 60 for dim, size in dim_sizes.items() 61 ) 62 63 return f"<ul class='xr-dim-list'>{dims_li}</ul>" 64 65 66def summarize_attrs_inner(attrs) -> str: 67 attrs_dl = "" 68 for k, v in attrs.items(): 69 if isinstance(v, dict): 70 attrs_dl += f"<dt><span>{escape(str(k))} :</span></dt><dd>" 71 attrs_dl += summarize_attrs_inner(v) 72 attrs_dl += "</dd>" 73 else: 74 attrs_dl += f"<dt><span>{escape(str(k))} :</span></dt><dd>{escape(str(v))}</dd>" 75 76 return attrs_dl 77 78 79def summarize_attrs(attrs) -> str: 80 # === Orig: 81 # attrs_dl = "".join( 82 # f"<dt><span>{escape(str(k))} :</span></dt><dd>{escape(str(v))}</dd>" 83 # for k, v in attrs.items() 84 # ) 85 # === V2: 86 # attrs_dl = "" 87 # for k, v in attrs.items(): 88 # if isinstance(v, dict): 89 # attrs_dl += "<span class='xr-section-item'>" 90 # attrs_dl += collapsible_section( 91 # name=escape(str(k)), 92 # inline_details="", 93 # details=summarize_attrs(v), 94 # n_items=len(v), 95 # enabled=True, 96 # collapsed=True, 97 # ) 98 # attrs_dl += "</span>" 99 # else: 100 # attrs_dl += f"<dt><span>{escape(str(k))} :</span></dt><dd>{escape(str(v))}</dd>" 101 102 attrs_dl = "" 103 for k, v in attrs.items(): 104 if isinstance(v, dict): 105 attr_id = "attrs-" + str(uuid.uuid4()) 106 107 attrs_dl += f"<div class='xr-attr-item'>" 108 attrs_dl += f"<input id='{attr_id}' class='xr-attr-in' type='checkbox'>" 109 attrs_dl += f"<label class='xr-attr-nested' for='{attr_id}'>{escape(str(k))} : " 110 attrs_dl += f"<span>({len(v)})</span></label>" 111 attrs_dl += "<span class='xr-attr-nested-inner'>" 112 attrs_dl += summarize_attrs(v) 113 attrs_dl += "</span></div>" 114 else: 115 attrs_dl += f"<dt><span>{escape(str(k))} :</span></dt><dd>{escape(str(v))}</dd>" 116 117 118 return f"<dl class='xr-attrs'>{attrs_dl}</dl>" 119 120 121def _icon(icon_name) -> str: 122 # icon_name should be defined in xarray/static/html/icon-svg-inline.html 123 return ( 124 f"<svg class='icon xr-{icon_name}'><use xlink:href='#{icon_name}'></use></svg>" 125 ) 126 127 128def summarize_variable(name, var, is_index=False, dtype=None) -> str: 129 variable = var.variable if hasattr(var, "variable") else var 130 131 cssclass_idx = " class='xr-has-index'" if is_index else "" 132 dims_str = f"({', '.join(escape(dim) for dim in var.dims)})" 133 name = escape(str(name)) 134 dtype = dtype or escape(str(var.dtype)) 135 136 # "unique" ids required to expand/collapse subsections 137 attrs_id = "attrs-" + str(uuid.uuid4()) 138 data_id = "data-" + str(uuid.uuid4()) 139 disabled = "" if len(var.attrs) else "disabled" 140 141 preview = escape(inline_variable_array_repr(variable, 35)) 142 attrs_ul = summarize_attrs(var.attrs) 143 data_repr = short_data_repr_html(variable) 144 145 attrs_icon = _icon("icon-file-text2") 146 data_icon = _icon("icon-database") 147 148 return ( 149 f"<div class='xr-var-name'><span{cssclass_idx}>{name}</span></div>" 150 f"<div class='xr-var-dims'>{dims_str}</div>" 151 f"<div class='xr-var-dtype'>{dtype}</div>" 152 f"<div class='xr-var-preview xr-preview'>{preview}</div>" 153 f"<input id='{attrs_id}' class='xr-var-attrs-in' " 154 f"type='checkbox' {disabled}>" 155 f"<label for='{attrs_id}' title='Show/Hide attributes'>" 156 f"{attrs_icon}</label>" 157 f"<input id='{data_id}' class='xr-var-data-in' type='checkbox'>" 158 f"<label for='{data_id}' title='Show/Hide data repr'>" 159 f"{data_icon}</label>" 160 f"<div class='xr-var-attrs'>{attrs_ul}</div>" 161 f"<div class='xr-var-data'>{data_repr}</div>" 162 ) 163 164 165def summarize_coords(variables) -> str: 166 li_items = [] 167 for k, v in variables.items(): 168 li_content = summarize_variable(k, v, is_index=k in variables.xindexes) 169 li_items.append(f"<li class='xr-var-item'>{li_content}</li>") 170 171 vars_li = "".join(li_items) 172 173 return f"<ul class='xr-var-list'>{vars_li}</ul>" 174 175 176def summarize_vars(variables) -> str: 177 vars_li = "".join( 178 f"<li class='xr-var-item'>{summarize_variable(k, v)}</li>" 179 for k, v in variables.items() 180 ) 181 182 return f"<ul class='xr-var-list'>{vars_li}</ul>" 183 184 185def short_index_repr_html(index) -> str: 186 if hasattr(index, "_repr_html_"): 187 return index._repr_html_() 188 189 return f"<pre>{escape(repr(index))}</pre>" 190 191 192def summarize_index(coord_names, index) -> str: 193 name = "<br>".join([escape(str(n)) for n in coord_names]) 194 195 index_id = f"index-{uuid.uuid4()}" 196 preview = escape(inline_index_repr(index, max_width=70)) 197 details = short_index_repr_html(index) 198 199 data_icon = _icon("icon-database") 200 201 return ( 202 f"<div class='xr-index-name'><div>{name}</div></div>" 203 f"<div class='xr-index-preview'>{preview}</div>" 204 # need empty input + label here to conform to the fixed CSS grid layout 205 f"<input type='checkbox' disabled/>" 206 f"<label></label>" 207 f"<input id='{index_id}' class='xr-index-data-in' type='checkbox'/>" 208 f"<label for='{index_id}' title='Show/Hide index repr'>{data_icon}</label>" 209 f"<div class='xr-index-data'>{details}</div>" 210 ) 211 212 213def summarize_indexes(indexes) -> str: 214 indexes_li = "".join( 215 f"<li class='xr-var-item'>{summarize_index(v, i)}</li>" 216 for v, i in indexes.items() 217 ) 218 return f"<ul class='xr-var-list'>{indexes_li}</ul>" 219 220 221def collapsible_section( 222 name, inline_details="", details="", n_items=None, enabled=True, collapsed=False 223) -> str: 224 # "unique" id to expand/collapse the section 225 data_id = "section-" + str(uuid.uuid4()) 226 227 has_items = n_items is not None and n_items 228 n_items_span = "" if n_items is None else f" <span>({n_items})</span>" 229 enabled = "" if enabled and has_items else "disabled" 230 collapsed = "" if collapsed or not has_items else "checked" 231 tip = " title='Expand/collapse section'" if enabled else "" 232 233 return ( 234 f"<input id='{data_id}' class='xr-section-summary-in' " 235 f"type='checkbox' {enabled} {collapsed}>" 236 f"<label for='{data_id}' class='xr-section-summary' {tip}>" 237 f"{name}:{n_items_span}</label>" 238 f"<div class='xr-section-inline-details'>{inline_details}</div>" 239 f"<div class='xr-section-details'>{details}</div>" 240 ) 241 242 243def _mapping_section( 244 mapping, 245 name, 246 details_func, 247 max_items_collapse, 248 expand_option_name, 249 enabled=True, 250 max_option_name: Literal["display_max_children"] | None = None, 251) -> str: 252 n_items = len(mapping) 253 expanded = _get_boolean_with_default( 254 expand_option_name, n_items < max_items_collapse 255 ) 256 collapsed = not expanded 257 258 inline_details = "" 259 if max_option_name and max_option_name in OPTIONS: 260 max_items = int(OPTIONS[max_option_name]) 261 if n_items > max_items: 262 inline_details = f"({max_items}/{n_items})" 263 264 return collapsible_section( 265 name, 266 inline_details=inline_details, 267 details=details_func(mapping), 268 n_items=n_items, 269 enabled=enabled, 270 collapsed=collapsed, 271 ) 272 273 274def dim_section(obj) -> str: 275 dim_list = format_dims(obj.sizes, obj.xindexes.dims) 276 277 return collapsible_section( 278 "Dimensions", inline_details=dim_list, enabled=False, collapsed=True 279 ) 280 281 282def array_section(obj) -> str: 283 # "unique" id to expand/collapse the section 284 data_id = "section-" + str(uuid.uuid4()) 285 collapsed = ( 286 "checked" 287 if _get_boolean_with_default("display_expand_data", default=True) 288 else "" 289 ) 290 variable = getattr(obj, "variable", obj) 291 preview = escape(inline_variable_array_repr(variable, max_width=70)) 292 data_repr = short_data_repr_html(obj) 293 data_icon = _icon("icon-database") 294 295 return ( 296 "<div class='xr-array-wrap'>" 297 f"<input id='{data_id}' class='xr-array-in' type='checkbox' {collapsed}>" 298 f"<label for='{data_id}' title='Show/hide data repr'>{data_icon}</label>" 299 f"<div class='xr-array-preview xr-preview'><span>{preview}</span></div>" 300 f"<div class='xr-array-data'>{data_repr}</div>" 301 "</div>" 302 ) 303 304 305coord_section = partial( 306 _mapping_section, 307 name="Coordinates", 308 details_func=summarize_coords, 309 max_items_collapse=25, 310 expand_option_name="display_expand_coords", 311) 312 313datavar_section = partial( 314 _mapping_section, 315 name="Data variables", 316 details_func=summarize_vars, 317 max_items_collapse=15, 318 expand_option_name="display_expand_data_vars", 319) 320 321index_section = partial( 322 _mapping_section, 323 name="Indexes", 324 details_func=summarize_indexes, 325 max_items_collapse=0, 326 expand_option_name="display_expand_indexes", 327) 328 329attr_section = partial( 330 _mapping_section, 331 name="Attributes", 332 details_func=summarize_attrs, 333 max_items_collapse=10, 334 expand_option_name="display_expand_attrs", 335) 336 337 338def _get_indexes_dict(indexes): 339 return { 340 tuple(index_vars.keys()): idx for idx, index_vars in indexes.group_by_index() 341 } 342 343 344def _obj_repr(obj, header_components, sections): 345 """Return HTML repr of an xarray object. 346 347 If CSS is not injected (untrusted notebook), fallback to the plain text repr. 348 349 """ 350 header = f"<div class='xr-header'>{''.join(h for h in header_components)}</div>" 351 sections = "".join(f"<li class='xr-section-item'>{s}</li>" for s in sections) 352 353 icons_svg, css_style = _load_static_files() 354 return ( 355 "<div>" 356 f"{icons_svg}<style>{css_style}</style>" 357 f"<pre class='xr-text-repr-fallback'>{escape(repr(obj))}</pre>" 358 "<div class='xr-wrap' style='display:none'>" 359 f"{header}" 360 f"<ul class='xr-sections'>{sections}</ul>" 361 "</div>" 362 "</div>" 363 ) 364 365 366def array_repr(arr) -> str: 367 dims = OrderedDict((k, v) for k, v in zip(arr.dims, arr.shape, strict=True)) 368 if hasattr(arr, "xindexes"): 369 indexed_dims = arr.xindexes.dims 370 else: 371 indexed_dims = {} 372 373 obj_type = f"xarray.{type(arr).__name__}" 374 arr_name = f"'{arr.name}'" if getattr(arr, "name", None) else "" 375 376 header_components = [ 377 f"<div class='xr-obj-type'>{obj_type}</div>", 378 f"<div class='xr-array-name'>{arr_name}</div>", 379 format_dims(dims, indexed_dims), 380 ] 381 382 sections = [array_section(arr)] 383 384 if hasattr(arr, "coords"): 385 sections.append(coord_section(arr.coords)) 386 387 if hasattr(arr, "xindexes"): 388 indexes = _get_indexes_dict(arr.xindexes) 389 sections.append(index_section(indexes)) 390 391 sections.append(attr_section(arr.attrs)) 392 393 return _obj_repr(arr, header_components, sections) 394 395 396def dataset_repr(ds) -> str: 397 obj_type = f"xarray.{type(ds).__name__}" 398 399 header_components = [f"<div class='xr-obj-type'>{escape(obj_type)}</div>"] 400 401 sections = [ 402 dim_section(ds), 403 coord_section(ds.coords), 404 datavar_section(ds.data_vars), 405 index_section(_get_indexes_dict(ds.xindexes)), 406 attr_section(ds.attrs), 407 ] 408 409 return _obj_repr(ds, header_components, sections) 410 411 412def summarize_datatree_children(children: Mapping[str, DataTree]) -> str: 413 MAX_CHILDREN = OPTIONS["display_max_children"] 414 n_children = len(children) 415 416 children_html = [] 417 for i, (n, c) in enumerate(children.items()): 418 if i < ceil(MAX_CHILDREN / 2) or i >= ceil(n_children - MAX_CHILDREN / 2): 419 is_last = i == (n_children - 1) 420 children_html.append( 421 _wrap_datatree_repr(datatree_node_repr(n, c), end=is_last) 422 ) 423 elif n_children > MAX_CHILDREN and i == ceil(MAX_CHILDREN / 2): 424 children_html.append("<div>...</div>") 425 426 return "".join( 427 [ 428 "<div style='display: inline-grid; grid-template-columns: 100%; grid-column: 1 / -1'>", 429 "".join(children_html), 430 "</div>", 431 ] 432 ) 433 434 435children_section = partial( 436 _mapping_section, 437 name="Groups", 438 details_func=summarize_datatree_children, 439 max_items_collapse=1, 440 max_option_name="display_max_children", 441 expand_option_name="display_expand_groups", 442) 443 444inherited_coord_section = partial( 445 _mapping_section, 446 name="Inherited coordinates", 447 details_func=summarize_coords, 448 max_items_collapse=25, 449 expand_option_name="display_expand_coords", 450) 451 452 453def datatree_node_repr(group_title: str, node: DataTree, show_inherited=False) -> str: 454 from xarray.core.coordinates import Coordinates 455 456 header_components = [f"<div class='xr-obj-type'>{escape(group_title)}</div>"] 457 458 ds = node._to_dataset_view(rebuild_dims=False, inherit=True) 459 node_coords = node.to_dataset(inherit=False).coords 460 461 # use this class to get access to .xindexes property 462 inherited_coords = Coordinates( 463 coords=inherited_vars(node._coord_variables), 464 indexes=inherited_vars(node._indexes), 465 ) 466 467 sections = [ 468 children_section(node.children), 469 dim_section(ds), 470 coord_section(node_coords), 471 ] 472 473 # only show inherited coordinates on the root 474 if show_inherited: 475 sections.append(inherited_coord_section(inherited_coords)) 476 477 sections += [ 478 datavar_section(ds.data_vars), 479 attr_section(ds.attrs), 480 ] 481 482 return _obj_repr(ds, header_components, sections) 483 484 485def _wrap_datatree_repr(r: str, end: bool = False) -> str: 486 """ 487 Wrap HTML representation with a tee to the left of it. 488 489 Enclosing HTML tag is a <div> with :code:`display: inline-grid` style. 490 491 Turns: 492 [ title ] 493 | details | 494 |_____________| 495 496 into (A): 497 |─ [ title ] 498 | | details | 499 | |_____________| 500 501 or (B): 502 └─ [ title ] 503 | details | 504 |_____________| 505 506 Parameters 507 ---------- 508 r: str 509 HTML representation to wrap. 510 end: bool 511 Specify if the line on the left should continue or end. 512 513 Default is True. 514 515 Returns 516 ------- 517 str 518 Wrapped HTML representation. 519 520 Tee color is set to the variable :code:`--xr-border-color`. 521 """ 522 # height of line 523 end = bool(end) 524 height = "100%" if end is False else "1.2em" 525 return "".join( 526 [ 527 "<div style='display: inline-grid; grid-template-columns: 0px 20px auto; width: 100%;'>", 528 "<div style='", 529 "grid-column-start: 1;", 530 "border-right: 0.2em solid;", 531 "border-color: var(--xr-border-color);", 532 f"height: {height};", 533 "width: 0px;", 534 "'>", 535 "</div>", 536 "<div style='", 537 "grid-column-start: 2;", 538 "grid-row-start: 1;", 539 "height: 1em;", 540 "width: 20px;", 541 "border-bottom: 0.2em solid;", 542 "border-color: var(--xr-border-color);", 543 "'>", 544 "</div>", 545 "<div style='", 546 "grid-column-start: 3;", 547 "'>", 548 r, 549 "</div>", 550 "</div>", 551 ] 552 ) 553 554 555def datatree_repr(dt: DataTree) -> str: 556 obj_type = f"xarray.{type(dt).__name__}" 557 return datatree_node_repr(obj_type, dt, show_inherited=True)
STATIC_FILES =
(('xarray.static.html', 'icons-svg-inline.html'), ('xopr.xarray_accessor', 'style.css'))
def
short_data_repr_html(array) -> str:
41def short_data_repr_html(array) -> str: 42 """Format "data" for DataArray and Variable.""" 43 internal_data = getattr(array, "variable", array)._data 44 if hasattr(internal_data, "_repr_html_"): 45 return internal_data._repr_html_() 46 text = escape(short_data_repr(array)) 47 return f"<pre>{text}</pre>"
Format "data" for DataArray and Variable.
def
format_dims(dim_sizes, dims_with_index) -> str:
50def format_dims(dim_sizes, dims_with_index) -> str: 51 if not dim_sizes: 52 return "" 53 54 dim_css_map = { 55 dim: " class='xr-has-index'" if dim in dims_with_index else "" 56 for dim in dim_sizes 57 } 58 59 dims_li = "".join( 60 f"<li><span{dim_css_map[dim]}>{escape(str(dim))}</span>: {size}</li>" 61 for dim, size in dim_sizes.items() 62 ) 63 64 return f"<ul class='xr-dim-list'>{dims_li}</ul>"
def
summarize_attrs_inner(attrs) -> str:
67def summarize_attrs_inner(attrs) -> str: 68 attrs_dl = "" 69 for k, v in attrs.items(): 70 if isinstance(v, dict): 71 attrs_dl += f"<dt><span>{escape(str(k))} :</span></dt><dd>" 72 attrs_dl += summarize_attrs_inner(v) 73 attrs_dl += "</dd>" 74 else: 75 attrs_dl += f"<dt><span>{escape(str(k))} :</span></dt><dd>{escape(str(v))}</dd>" 76 77 return attrs_dl
def
summarize_attrs(attrs) -> str:
80def summarize_attrs(attrs) -> str: 81 # === Orig: 82 # attrs_dl = "".join( 83 # f"<dt><span>{escape(str(k))} :</span></dt><dd>{escape(str(v))}</dd>" 84 # for k, v in attrs.items() 85 # ) 86 # === V2: 87 # attrs_dl = "" 88 # for k, v in attrs.items(): 89 # if isinstance(v, dict): 90 # attrs_dl += "<span class='xr-section-item'>" 91 # attrs_dl += collapsible_section( 92 # name=escape(str(k)), 93 # inline_details="", 94 # details=summarize_attrs(v), 95 # n_items=len(v), 96 # enabled=True, 97 # collapsed=True, 98 # ) 99 # attrs_dl += "</span>" 100 # else: 101 # attrs_dl += f"<dt><span>{escape(str(k))} :</span></dt><dd>{escape(str(v))}</dd>" 102 103 attrs_dl = "" 104 for k, v in attrs.items(): 105 if isinstance(v, dict): 106 attr_id = "attrs-" + str(uuid.uuid4()) 107 108 attrs_dl += f"<div class='xr-attr-item'>" 109 attrs_dl += f"<input id='{attr_id}' class='xr-attr-in' type='checkbox'>" 110 attrs_dl += f"<label class='xr-attr-nested' for='{attr_id}'>{escape(str(k))} : " 111 attrs_dl += f"<span>({len(v)})</span></label>" 112 attrs_dl += "<span class='xr-attr-nested-inner'>" 113 attrs_dl += summarize_attrs(v) 114 attrs_dl += "</span></div>" 115 else: 116 attrs_dl += f"<dt><span>{escape(str(k))} :</span></dt><dd>{escape(str(v))}</dd>" 117 118 119 return f"<dl class='xr-attrs'>{attrs_dl}</dl>"
def
summarize_variable(name, var, is_index=False, dtype=None) -> str:
129def summarize_variable(name, var, is_index=False, dtype=None) -> str: 130 variable = var.variable if hasattr(var, "variable") else var 131 132 cssclass_idx = " class='xr-has-index'" if is_index else "" 133 dims_str = f"({', '.join(escape(dim) for dim in var.dims)})" 134 name = escape(str(name)) 135 dtype = dtype or escape(str(var.dtype)) 136 137 # "unique" ids required to expand/collapse subsections 138 attrs_id = "attrs-" + str(uuid.uuid4()) 139 data_id = "data-" + str(uuid.uuid4()) 140 disabled = "" if len(var.attrs) else "disabled" 141 142 preview = escape(inline_variable_array_repr(variable, 35)) 143 attrs_ul = summarize_attrs(var.attrs) 144 data_repr = short_data_repr_html(variable) 145 146 attrs_icon = _icon("icon-file-text2") 147 data_icon = _icon("icon-database") 148 149 return ( 150 f"<div class='xr-var-name'><span{cssclass_idx}>{name}</span></div>" 151 f"<div class='xr-var-dims'>{dims_str}</div>" 152 f"<div class='xr-var-dtype'>{dtype}</div>" 153 f"<div class='xr-var-preview xr-preview'>{preview}</div>" 154 f"<input id='{attrs_id}' class='xr-var-attrs-in' " 155 f"type='checkbox' {disabled}>" 156 f"<label for='{attrs_id}' title='Show/Hide attributes'>" 157 f"{attrs_icon}</label>" 158 f"<input id='{data_id}' class='xr-var-data-in' type='checkbox'>" 159 f"<label for='{data_id}' title='Show/Hide data repr'>" 160 f"{data_icon}</label>" 161 f"<div class='xr-var-attrs'>{attrs_ul}</div>" 162 f"<div class='xr-var-data'>{data_repr}</div>" 163 )
def
summarize_coords(variables) -> str:
166def summarize_coords(variables) -> str: 167 li_items = [] 168 for k, v in variables.items(): 169 li_content = summarize_variable(k, v, is_index=k in variables.xindexes) 170 li_items.append(f"<li class='xr-var-item'>{li_content}</li>") 171 172 vars_li = "".join(li_items) 173 174 return f"<ul class='xr-var-list'>{vars_li}</ul>"
def
summarize_vars(variables) -> str:
def
short_index_repr_html(index) -> str:
def
summarize_index(coord_names, index) -> str:
193def summarize_index(coord_names, index) -> str: 194 name = "<br>".join([escape(str(n)) for n in coord_names]) 195 196 index_id = f"index-{uuid.uuid4()}" 197 preview = escape(inline_index_repr(index, max_width=70)) 198 details = short_index_repr_html(index) 199 200 data_icon = _icon("icon-database") 201 202 return ( 203 f"<div class='xr-index-name'><div>{name}</div></div>" 204 f"<div class='xr-index-preview'>{preview}</div>" 205 # need empty input + label here to conform to the fixed CSS grid layout 206 f"<input type='checkbox' disabled/>" 207 f"<label></label>" 208 f"<input id='{index_id}' class='xr-index-data-in' type='checkbox'/>" 209 f"<label for='{index_id}' title='Show/Hide index repr'>{data_icon}</label>" 210 f"<div class='xr-index-data'>{details}</div>" 211 )
def
summarize_indexes(indexes) -> str:
def
collapsible_section( name, inline_details='', details='', n_items=None, enabled=True, collapsed=False) -> str:
222def collapsible_section( 223 name, inline_details="", details="", n_items=None, enabled=True, collapsed=False 224) -> str: 225 # "unique" id to expand/collapse the section 226 data_id = "section-" + str(uuid.uuid4()) 227 228 has_items = n_items is not None and n_items 229 n_items_span = "" if n_items is None else f" <span>({n_items})</span>" 230 enabled = "" if enabled and has_items else "disabled" 231 collapsed = "" if collapsed or not has_items else "checked" 232 tip = " title='Expand/collapse section'" if enabled else "" 233 234 return ( 235 f"<input id='{data_id}' class='xr-section-summary-in' " 236 f"type='checkbox' {enabled} {collapsed}>" 237 f"<label for='{data_id}' class='xr-section-summary' {tip}>" 238 f"{name}:{n_items_span}</label>" 239 f"<div class='xr-section-inline-details'>{inline_details}</div>" 240 f"<div class='xr-section-details'>{details}</div>" 241 )
def
dim_section(obj) -> str:
def
array_section(obj) -> str:
283def array_section(obj) -> str: 284 # "unique" id to expand/collapse the section 285 data_id = "section-" + str(uuid.uuid4()) 286 collapsed = ( 287 "checked" 288 if _get_boolean_with_default("display_expand_data", default=True) 289 else "" 290 ) 291 variable = getattr(obj, "variable", obj) 292 preview = escape(inline_variable_array_repr(variable, max_width=70)) 293 data_repr = short_data_repr_html(obj) 294 data_icon = _icon("icon-database") 295 296 return ( 297 "<div class='xr-array-wrap'>" 298 f"<input id='{data_id}' class='xr-array-in' type='checkbox' {collapsed}>" 299 f"<label for='{data_id}' title='Show/hide data repr'>{data_icon}</label>" 300 f"<div class='xr-array-preview xr-preview'><span>{preview}</span></div>" 301 f"<div class='xr-array-data'>{data_repr}</div>" 302 "</div>" 303 )
coord_section =
functools.partial(<function _mapping_section>, name='Coordinates', details_func=<function summarize_coords>, max_items_collapse=25, expand_option_name='display_expand_coords')
datavar_section =
functools.partial(<function _mapping_section>, name='Data variables', details_func=<function summarize_vars>, max_items_collapse=15, expand_option_name='display_expand_data_vars')
index_section =
functools.partial(<function _mapping_section>, name='Indexes', details_func=<function summarize_indexes>, max_items_collapse=0, expand_option_name='display_expand_indexes')
attr_section =
functools.partial(<function _mapping_section>, name='Attributes', details_func=<function summarize_attrs>, max_items_collapse=10, expand_option_name='display_expand_attrs')
def
array_repr(arr) -> str:
367def array_repr(arr) -> str: 368 dims = OrderedDict((k, v) for k, v in zip(arr.dims, arr.shape, strict=True)) 369 if hasattr(arr, "xindexes"): 370 indexed_dims = arr.xindexes.dims 371 else: 372 indexed_dims = {} 373 374 obj_type = f"xarray.{type(arr).__name__}" 375 arr_name = f"'{arr.name}'" if getattr(arr, "name", None) else "" 376 377 header_components = [ 378 f"<div class='xr-obj-type'>{obj_type}</div>", 379 f"<div class='xr-array-name'>{arr_name}</div>", 380 format_dims(dims, indexed_dims), 381 ] 382 383 sections = [array_section(arr)] 384 385 if hasattr(arr, "coords"): 386 sections.append(coord_section(arr.coords)) 387 388 if hasattr(arr, "xindexes"): 389 indexes = _get_indexes_dict(arr.xindexes) 390 sections.append(index_section(indexes)) 391 392 sections.append(attr_section(arr.attrs)) 393 394 return _obj_repr(arr, header_components, sections)
def
dataset_repr(ds) -> str:
397def dataset_repr(ds) -> str: 398 obj_type = f"xarray.{type(ds).__name__}" 399 400 header_components = [f"<div class='xr-obj-type'>{escape(obj_type)}</div>"] 401 402 sections = [ 403 dim_section(ds), 404 coord_section(ds.coords), 405 datavar_section(ds.data_vars), 406 index_section(_get_indexes_dict(ds.xindexes)), 407 attr_section(ds.attrs), 408 ] 409 410 return _obj_repr(ds, header_components, sections)
def
summarize_datatree_children(children: Mapping[str, xarray.core.datatree.DataTree]) -> str:
413def summarize_datatree_children(children: Mapping[str, DataTree]) -> str: 414 MAX_CHILDREN = OPTIONS["display_max_children"] 415 n_children = len(children) 416 417 children_html = [] 418 for i, (n, c) in enumerate(children.items()): 419 if i < ceil(MAX_CHILDREN / 2) or i >= ceil(n_children - MAX_CHILDREN / 2): 420 is_last = i == (n_children - 1) 421 children_html.append( 422 _wrap_datatree_repr(datatree_node_repr(n, c), end=is_last) 423 ) 424 elif n_children > MAX_CHILDREN and i == ceil(MAX_CHILDREN / 2): 425 children_html.append("<div>...</div>") 426 427 return "".join( 428 [ 429 "<div style='display: inline-grid; grid-template-columns: 100%; grid-column: 1 / -1'>", 430 "".join(children_html), 431 "</div>", 432 ] 433 )
children_section =
functools.partial(<function _mapping_section>, name='Groups', details_func=<function summarize_datatree_children>, max_items_collapse=1, max_option_name='display_max_children', expand_option_name='display_expand_groups')
inherited_coord_section =
functools.partial(<function _mapping_section>, name='Inherited coordinates', details_func=<function summarize_coords>, max_items_collapse=25, expand_option_name='display_expand_coords')
def
datatree_node_repr( group_title: str, node: xarray.core.datatree.DataTree, show_inherited=False) -> str:
454def datatree_node_repr(group_title: str, node: DataTree, show_inherited=False) -> str: 455 from xarray.core.coordinates import Coordinates 456 457 header_components = [f"<div class='xr-obj-type'>{escape(group_title)}</div>"] 458 459 ds = node._to_dataset_view(rebuild_dims=False, inherit=True) 460 node_coords = node.to_dataset(inherit=False).coords 461 462 # use this class to get access to .xindexes property 463 inherited_coords = Coordinates( 464 coords=inherited_vars(node._coord_variables), 465 indexes=inherited_vars(node._indexes), 466 ) 467 468 sections = [ 469 children_section(node.children), 470 dim_section(ds), 471 coord_section(node_coords), 472 ] 473 474 # only show inherited coordinates on the root 475 if show_inherited: 476 sections.append(inherited_coord_section(inherited_coords)) 477 478 sections += [ 479 datavar_section(ds.data_vars), 480 attr_section(ds.attrs), 481 ] 482 483 return _obj_repr(ds, header_components, sections)
def
datatree_repr(dt: xarray.core.datatree.DataTree) -> str: