|
2 | 2 |
|
3 | 3 | import uuid
|
4 | 4 | from collections import OrderedDict
|
| 5 | +from collections.abc import Mapping |
5 | 6 | from functools import lru_cache, partial
|
6 | 7 | from html import escape
|
7 | 8 | from importlib.resources import files
|
| 9 | +from typing import TYPE_CHECKING |
8 | 10 |
|
9 | 11 | from xarray.core.formatting import (
|
10 | 12 | inline_index_repr,
|
|
18 | 20 | ("xarray.static.css", "style.css"),
|
19 | 21 | )
|
20 | 22 |
|
| 23 | +if TYPE_CHECKING: |
| 24 | + from xarray.core.datatree import DataTree |
| 25 | + |
21 | 26 |
|
22 | 27 | @lru_cache(None)
|
23 | 28 | def _load_static_files():
|
@@ -341,3 +346,129 @@ def dataset_repr(ds) -> str:
|
341 | 346 | ]
|
342 | 347 |
|
343 | 348 | return _obj_repr(ds, header_components, sections)
|
| 349 | + |
| 350 | + |
| 351 | +def summarize_datatree_children(children: Mapping[str, DataTree]) -> str: |
| 352 | + N_CHILDREN = len(children) - 1 |
| 353 | + |
| 354 | + # Get result from datatree_node_repr and wrap it |
| 355 | + lines_callback = lambda n, c, end: _wrap_datatree_repr( |
| 356 | + datatree_node_repr(n, c), end=end |
| 357 | + ) |
| 358 | + |
| 359 | + children_html = "".join( |
| 360 | + ( |
| 361 | + lines_callback(n, c, end=False) # Long lines |
| 362 | + if i < N_CHILDREN |
| 363 | + else lines_callback(n, c, end=True) |
| 364 | + ) # Short lines |
| 365 | + for i, (n, c) in enumerate(children.items()) |
| 366 | + ) |
| 367 | + |
| 368 | + return "".join( |
| 369 | + [ |
| 370 | + "<div style='display: inline-grid; grid-template-columns: 100%; grid-column: 1 / -1'>", |
| 371 | + children_html, |
| 372 | + "</div>", |
| 373 | + ] |
| 374 | + ) |
| 375 | + |
| 376 | + |
| 377 | +children_section = partial( |
| 378 | + _mapping_section, |
| 379 | + name="Groups", |
| 380 | + details_func=summarize_datatree_children, |
| 381 | + max_items_collapse=1, |
| 382 | + expand_option_name="display_expand_groups", |
| 383 | +) |
| 384 | + |
| 385 | + |
| 386 | +def datatree_node_repr(group_title: str, dt: DataTree) -> str: |
| 387 | + header_components = [f"<div class='xr-obj-type'>{escape(group_title)}</div>"] |
| 388 | + |
| 389 | + ds = dt.ds |
| 390 | + |
| 391 | + sections = [ |
| 392 | + children_section(dt.children), |
| 393 | + dim_section(ds), |
| 394 | + coord_section(ds.coords), |
| 395 | + datavar_section(ds.data_vars), |
| 396 | + attr_section(ds.attrs), |
| 397 | + ] |
| 398 | + |
| 399 | + return _obj_repr(ds, header_components, sections) |
| 400 | + |
| 401 | + |
| 402 | +def _wrap_datatree_repr(r: str, end: bool = False) -> str: |
| 403 | + """ |
| 404 | + Wrap HTML representation with a tee to the left of it. |
| 405 | +
|
| 406 | + Enclosing HTML tag is a <div> with :code:`display: inline-grid` style. |
| 407 | +
|
| 408 | + Turns: |
| 409 | + [ title ] |
| 410 | + | details | |
| 411 | + |_____________| |
| 412 | +
|
| 413 | + into (A): |
| 414 | + |─ [ title ] |
| 415 | + | | details | |
| 416 | + | |_____________| |
| 417 | +
|
| 418 | + or (B): |
| 419 | + └─ [ title ] |
| 420 | + | details | |
| 421 | + |_____________| |
| 422 | +
|
| 423 | + Parameters |
| 424 | + ---------- |
| 425 | + r: str |
| 426 | + HTML representation to wrap. |
| 427 | + end: bool |
| 428 | + Specify if the line on the left should continue or end. |
| 429 | +
|
| 430 | + Default is True. |
| 431 | +
|
| 432 | + Returns |
| 433 | + ------- |
| 434 | + str |
| 435 | + Wrapped HTML representation. |
| 436 | +
|
| 437 | + Tee color is set to the variable :code:`--xr-border-color`. |
| 438 | + """ |
| 439 | + # height of line |
| 440 | + end = bool(end) |
| 441 | + height = "100%" if end is False else "1.2em" |
| 442 | + return "".join( |
| 443 | + [ |
| 444 | + "<div style='display: inline-grid; grid-template-columns: 0px 20px auto; width: 100%;'>", |
| 445 | + "<div style='", |
| 446 | + "grid-column-start: 1;", |
| 447 | + "border-right: 0.2em solid;", |
| 448 | + "border-color: var(--xr-border-color);", |
| 449 | + f"height: {height};", |
| 450 | + "width: 0px;", |
| 451 | + "'>", |
| 452 | + "</div>", |
| 453 | + "<div style='", |
| 454 | + "grid-column-start: 2;", |
| 455 | + "grid-row-start: 1;", |
| 456 | + "height: 1em;", |
| 457 | + "width: 20px;", |
| 458 | + "border-bottom: 0.2em solid;", |
| 459 | + "border-color: var(--xr-border-color);", |
| 460 | + "'>", |
| 461 | + "</div>", |
| 462 | + "<div style='", |
| 463 | + "grid-column-start: 3;", |
| 464 | + "'>", |
| 465 | + r, |
| 466 | + "</div>", |
| 467 | + "</div>", |
| 468 | + ] |
| 469 | + ) |
| 470 | + |
| 471 | + |
| 472 | +def datatree_repr(dt: DataTree) -> str: |
| 473 | + obj_type = f"datatree.{type(dt).__name__}" |
| 474 | + return datatree_node_repr(obj_type, dt) |
0 commit comments