Skip to content

pygx.views.html

HTML views for PyGX objects.

html

HTML views for PyGX objects.

Html

Html(
    *content: WritableTypes,
    style_files: Iterable[str] | None = None,
    styles: Iterable[str] | None = None,
    script_files: Iterable[str] | None = None,
    scripts: Iterable[str] | None = None
)

Bases: Content

HTML with consolidated CSS and Scripts.

Example:

def foo() -> pg.Html:
  s = pg.Html()
  s.add_style('div.foo { color: red; }')
  s.add_script('function myFoo() { console.log("foo");}')
  s.write('<div class="foo">Foo</div>')
  return s

def bar() -> pg.Html:
  s = pg.Html()
  s.add_style('div.bar { color: green; }')
  s.add_script('function myBar() { console.log("bar");}')
  s.write('<div class="bar">')
  s.write(foo())
  s.write('</div>')
  return s

html = bar().to_str()

This will output:

<html>
<head>
<style>
div.bar { color: green; }
div.foo { color: red; }
</style>
<script>
function myBar() { console.log("bar");}
function myFoo() { console.log("foo");}
</script>
</head>
<body>
<div class="bar"><div class="foo">Foo</div></div>
</body>
</html>

Constructor.

Parameters:

Name Type Description Default
*content WritableTypes

One or multiple body parts (str, Html, callable, or None) of the HTML.

()
style_files Iterable[str] | None

URLs for external styles to include.

None
styles Iterable[str] | None

CSS styles to include.

None
script_files Iterable[str] | None

URLs for external scripts to include.

None
scripts Iterable[str] | None

JavaScript scripts to include.

None
Source code in pygx/views/html/_base.py
def __init__(  # pylint: disable=useless-super-delegation
    self,
    *content: WritableTypes,
    style_files: Iterable[str] | None = None,
    styles: Iterable[str] | None = None,
    script_files: Iterable[str] | None = None,
    scripts: Iterable[str] | None = None,
) -> None:
    """Constructor.

    Args:
      *content: One or multiple body parts (str, Html, callable, or None)
        of the HTML.
      style_files: URLs for external styles to include.
      styles: CSS styles to include.
      script_files: URLs for external scripts to include.
      scripts: JavaScript scripts to include.
    """
    # Avoid materializing an empty list per kwarg — most `Html()` calls
    # pass none of the optional kwargs, so we go straight to the
    # zero-arg constructors (which short-circuit `add()`).
    super().__init__(
        *content,  # pyright: ignore[reportArgumentType]
        style_files=(
            Html.StyleFiles(*style_files)
            if style_files
            else Html.StyleFiles()
        ),
        styles=Html.Styles(*styles) if styles else Html.Styles(),
        script_files=(
            Html.ScriptFiles(*script_files)
            if script_files
            else Html.ScriptFiles()
        ),
        scripts=Html.Scripts(*scripts) if scripts else Html.Scripts(),
    )

styles property

styles: Styles

Returns the styles to include in the HTML.

style_files property

style_files: StyleFiles

Returns the style files to link to.

scripts property

scripts: Scripts

Returns the scripts to include in the HTML.

script_files property

script_files: ScriptFiles

Returns the script files to link to.

head_section property

head_section: str

Returns the head section.

style_section property

style_section: str

Returns the style section.

script_section property

script_section: str

Returns the script section.

body_section property

body_section: str

Returns the body section.

Scripts

Scripts(*parts: Union[str, SharedParts, None])

Bases: SharedParts

Shared script definitions in the HEAD section.

Source code in pygx/views/_base.py
def __init__(
    self, *parts: Union[str, 'Content.SharedParts', None]
) -> None:
    # Plain dict (not defaultdict) — `add()` uses explicit `.get(p, 0)`
    # so the defaultdict factory is never needed, and a plain dict
    # constructs faster.
    self._parts: dict[str, int] = {}
    # Skip the no-op `add()` call when there's nothing to add — most
    # `Html` constructions pre-allocate empty SharedParts.
    if parts:
        self.add(*parts)

ScriptFiles

ScriptFiles(*parts: Union[str, SharedParts, None])

Bases: SharedParts

Shared script files to link to in the HEAD section.

Source code in pygx/views/_base.py
def __init__(
    self, *parts: Union[str, 'Content.SharedParts', None]
) -> None:
    # Plain dict (not defaultdict) — `add()` uses explicit `.get(p, 0)`
    # so the defaultdict factory is never needed, and a plain dict
    # constructs faster.
    self._parts: dict[str, int] = {}
    # Skip the no-op `add()` call when there's nothing to add — most
    # `Html` constructions pre-allocate empty SharedParts.
    if parts:
        self.add(*parts)

Styles

Styles(*parts: Union[str, SharedParts, None])

Bases: SharedParts

Shared style definitions in the HEAD section.

Source code in pygx/views/_base.py
def __init__(
    self, *parts: Union[str, 'Content.SharedParts', None]
) -> None:
    # Plain dict (not defaultdict) — `add()` uses explicit `.get(p, 0)`
    # so the defaultdict factory is never needed, and a plain dict
    # constructs faster.
    self._parts: dict[str, int] = {}
    # Skip the no-op `add()` call when there's nothing to add — most
    # `Html` constructions pre-allocate empty SharedParts.
    if parts:
        self.add(*parts)

StyleFiles

StyleFiles(*parts: Union[str, SharedParts, None])

Bases: SharedParts

Shared style files to link to in the HEAD section.

Source code in pygx/views/_base.py
def __init__(
    self, *parts: Union[str, 'Content.SharedParts', None]
) -> None:
    # Plain dict (not defaultdict) — `add()` uses explicit `.get(p, 0)`
    # so the defaultdict factory is never needed, and a plain dict
    # constructs faster.
    self._parts: dict[str, int] = {}
    # Skip the no-op `add()` call when there's nothing to add — most
    # `Html` constructions pre-allocate empty SharedParts.
    if parts:
        self.add(*parts)

add_style

add_style(*css: str) -> Html

Adds CSS styles to the HTML.

Source code in pygx/views/html/_base.py
def add_style(self, *css: str) -> 'Html':
    """Adds CSS styles to the HTML."""
    self.styles.add(*css)
    return self

add_script

add_script(*js: str) -> Html

Adds JavaScript scripts to the HTML.

Source code in pygx/views/html/_base.py
def add_script(self, *js: str) -> 'Html':
    """Adds JavaScript scripts to the HTML."""
    self.scripts.add(*js)
    return self

add_style_file

add_style_file(*url: str) -> Html

Adds one or more style file URLs to the HTML.

Source code in pygx/views/html/_base.py
def add_style_file(self, *url: str) -> 'Html':
    """Adds one or more style file URLs to the HTML."""
    self.style_files.add(*url)
    return self

add_script_file

add_script_file(*url: str) -> Html

Adds one or more script file URLs to the HTML.

Source code in pygx/views/html/_base.py
def add_script_file(self, *url: str) -> 'Html':
    """Adds one or more script file URLs to the HTML."""
    self.script_files.add(*url)
    return self

to_str

to_str(*, content_only: bool = False, **kwargs: Any) -> str

Returns the HTML str.

Parameters:

Name Type Description Default
content_only bool

If True, only the content will be returned.

False
**kwargs Any

Additional keyword arguments passed from the user that will be ignored.

{}

Returns:

Type Description
str

The generated HTML str.

Source code in pygx/views/html/_base.py
def to_str(self, *, content_only: bool = False, **kwargs: Any) -> str:
    """Returns the HTML str.

    Args:
      content_only: If True, only the content will be returned.
      **kwargs: Additional keyword arguments passed from the user that
        will be ignored.

    Returns:
      The generated HTML str.
    """
    if content_only:
        return self.content
    return '\n'.join(
        [
            v
            for v in [
                '<html>',
                self.head_section,
                self.body_section,
                '</html>',
            ]
            if v
        ]
    )

element classmethod

element(
    tag: str,
    inner_html: Nestable[WritableTypes] | None = None,
    *,
    options: str | Iterable[str] | None = None,
    css_classes: NestableStr = None,
    styles: str | dict[str, Any] | None = None,
    **properties: Any
) -> Html

Creates an HTML element.

Parameters:

Name Type Description Default
tag str

The HTML tag name.

required
inner_html Nestable[WritableTypes] | None

The inner HTML of the element.

None
options str | Iterable[str] | None

Positional options that will be added to the element. E.g. 'open' for <details open>.

None
css_classes NestableStr

The CSS class name or a list of CSS class names.

None
styles str | dict[str, Any] | None

A single CSS style string or a dictionary of CSS properties.

None
**properties Any

Keyword arguments for HTML properties. For properties with underscore in the name, the underscore will be replaced by dash in the generated HTML. E.g. background_color will be converted to background-color.

{}

Returns:

Type Description
Html

An Html object for the complete element (opening tag, inner HTML

Html

and closing tag).

Source code in pygx/views/html/_base.py
@classmethod
def element(
    cls,
    tag: str,
    inner_html: wire.Nestable[WritableTypes] | None = None,
    *,
    options: str | Iterable[str] | None = None,
    css_classes: NestableStr = None,
    styles: str | dict[str, Any] | None = None,
    **properties: Any,
) -> 'Html':
    """Creates an HTML element.

    Args:
      tag: The HTML tag name.
      inner_html: The inner HTML of the element.
      options: Positional options that will be added to the element. E.g. 'open'
        for `<details open>`.
      css_classes: The CSS class name or a list of CSS class names.
      styles: A single CSS style string or a dictionary of CSS properties.
      **properties: Keyword arguments for HTML properties. For properties with
        underscore in the name, the underscore will be replaced by dash in the
        generated HTML. E.g. `background_color` will be converted to
        `background-color`.

    Returns:
      An `Html` object for the complete element (opening tag, inner HTML
      and closing tag).
    """
    s = cls()

    # Hot path: only `css_classes` (often the only non-default arg in
    # tree-view rendering). A single f-string is ~2x faster than the
    # generic list+join path below.
    if options is None and styles is None and not properties:
        css_classes = cls.concate(css_classes)

        # Even hotter path: when `inner_html` is `None`, a `str`, or a
        # flat list of `str` only, we can emit the entire element with
        # a single `write()` call (no shared-parts merging needed).
        inner_str: str | None
        if inner_html is None:
            inner_str = ''
        elif isinstance(inner_html, str):
            inner_str = inner_html
        elif isinstance(inner_html, list):
            inner_str = ''
            for c in inner_html:
                if isinstance(c, str):
                    inner_str += c
                else:
                    inner_str = None
                    break
        else:
            inner_str = None

        if inner_str is not None:
            if css_classes:
                s.write(f'<{tag} class="{css_classes}">{inner_str}</{tag}>')
            else:
                s.write(f'<{tag}>{inner_str}</{tag}>')
            return s

        if css_classes:
            s.write(f'<{tag} class="{css_classes}">')
        else:
            s.write(f'<{tag}>')
    else:
        # General path: assemble the opening tag as a single string to
        # minimize `write()` invocations (each call goes through
        # `_to_content` and dict-pop).
        css_classes = cls.concate(css_classes)
        options = cls.concate(options)  # pyright: ignore[reportArgumentType]
        styles = cls.style_str(styles)
        open_parts = ['<', tag]
        if options:
            open_parts.append(' ')
            open_parts.append(options)
        if css_classes:
            open_parts.append(' class="')
            open_parts.append(css_classes)
            open_parts.append('"')
        if styles:
            open_parts.append(' style="')
            open_parts.append(styles)
            open_parts.append('"')
        for k, v in properties.items():
            if v is not None:
                open_parts.append(' ')
                open_parts.append(k.replace('_', '-'))
                open_parts.append('="')
                open_parts.append(str(v))
                open_parts.append('"')
        open_parts.append('>')
        s.write(''.join(open_parts))

    # Write the inner HTML.
    if inner_html:
        if isinstance(inner_html, list):
            # Fast path for a flat list (the overwhelmingly common shape).
            # Only fall back to `topology.flatten` when nested containers
            # are detected.
            nested = False
            for child in inner_html:
                if isinstance(child, (list, tuple)):
                    nested = True
                    break
            if nested:
                for child in topology.flatten(inner_html).values():
                    s.write(child)
            else:
                s.write(*inner_html)
        else:
            s.write(inner_html)  # pyright: ignore[reportArgumentType]

    # Write the closing tag.
    s.write(f'</{tag}>')
    return s

escape classmethod

escape(s: WritableTypes, javascript_str: bool = False) -> Any

Escapes an HTML-writable value.

When javascript_str is True, the value is escaped for embedding in a JavaScript string literal instead of HTML-escaped.

Source code in pygx/views/html/_base.py
@classmethod
def escape(cls, s: WritableTypes, javascript_str: bool = False) -> Any:
    """Escapes an HTML-writable value.

    When `javascript_str` is True, the value is escaped for embedding in a
    JavaScript string literal instead of HTML-escaped.
    """
    if s is None:
        return None

    if callable(s):
        s = s()
    if isinstance(s, HtmlConvertible):
        s = s.to_html()

    def _escape(s: str) -> str:
        if javascript_str:
            return (
                s.replace('\\', '\\\\')
                .replace('"', '\\"')
                .replace('\r', '\\r')
                .replace('\n', '\\n')
                .replace('\t', '\\t')
            )
        return html_lib.escape(s)

    if isinstance(s, str):
        return _escape(s)
    else:
        assert isinstance(s, Html), s
        return Html(_escape(s.content)).write(s, shared_parts_only=True)

concate classmethod

concate(
    nestable_str: NestableStr, separator: str = " ", dedup: bool = True
) -> str | None

Concatenates the string nodes in a nestable object.

Returns a separator-joined string of all string leaves (deduplicated when dedup is True), or None if there is no string leaf.

Source code in pygx/views/html/_base.py
@classmethod
def concate(
    cls, nestable_str: NestableStr, separator: str = ' ', dedup: bool = True
) -> str | None:
    """Concatenates the string nodes in a nestable object.

    Returns a `separator`-joined string of all string leaves (deduplicated
    when `dedup` is True), or None if there is no string leaf.
    """
    # Fast path for the common shapes: None, str, and (recursively)
    # nested list/tuple of str|None. This avoids a full `topology.flatten`
    # traversal — which is by far the dominant cost when rendering HTML
    # trees, since `concate` is called for `css_classes` and `options`
    # on every element. The top-level loop is inlined (rather than
    # delegated to `_collect_str_leaves`) to elide a function call per
    # invocation — flat lists are the dominant case and don't need
    # recursion at all.
    if nestable_str is None:
        return None
    if isinstance(nestable_str, str):
        return nestable_str
    if isinstance(nestable_str, (list, tuple)):
        items: list[str] = []
        ok = True
        for v in nestable_str:
            if v is None:
                continue
            if isinstance(v, str):
                items.append(v)
            elif isinstance(v, (list, tuple)):
                if not _collect_str_leaves(v, items):
                    ok = False
                    break
            else:
                ok = False
                break
        if ok:
            if not items:
                return None
            if dedup and len(items) > 1:
                items = list(dict.fromkeys(items))
            return separator.join(items)
    # Fallback for any other shape (e.g., dict, custom Nestable).
    # `nestable_str` can no longer be str at this point (handled above),
    # so `topology.flatten` will return either a dict (containers) or some
    # other value that we treat as containing no string leaves.
    flattened = topology.flatten(nestable_str)
    if isinstance(flattened, dict):
        str_items = [v for v in flattened.values() if isinstance(v, str)]
        if dedup:
            str_items = list(dict.fromkeys(str_items).keys())
        if str_items:
            return separator.join(str_items)
    return None

style_str classmethod

style_str(style: str | dict[str, Any] | None) -> str | None

Gets a string representing an inline CSS style.

Parameters:

Name Type Description Default
style str | dict[str, Any] | None

A single CSS style string, or a dictionary for CSS properties. When dictionary form is used, underscore in the key name will be replaced by dash in the generated CSS style string. For example, background_color will be converted to background-color.

required

Returns:

Type Description
str | None

A CSS style string or None if no CSS property is provided.

Source code in pygx/views/html/_base.py
@classmethod
def style_str(
    cls,
    style: str | dict[str, Any] | None,
) -> str | None:
    """Gets a string representing an inline CSS style.

    Args:
      style: A single CSS style string, or a dictionary for CSS properties.
        When dictionary form is used, underscore in the key name will be
        replaced by dash in the generated CSS style string.
        For example, `background_color` will be converted to `background-color`.

    Returns:
      A CSS style string or None if no CSS property is provided.
    """
    if not style:
        return None
    if isinstance(style, str):
        return style
    else:
        assert isinstance(style, dict), style
        return (
            ''.join(
                [
                    f'{k.replace("_", "-")}:{v};'
                    for k, v in style.items()
                    if v is not None
                ]
            )
            or None
        )

HtmlConvertible

Base class for HTML convertible objects.

to_html

to_html(**kwargs) -> Html

Returns the HTML representation of the object.

Source code in pygx/views/html/_base.py
def to_html(self, **kwargs) -> Html:
    """Returns the HTML representation of the object."""
    return to_html(self, **kwargs)

to_html_str

to_html_str(*, content_only: bool = False, **kwargs) -> str

Returns the HTML str of the object.

Source code in pygx/views/html/_base.py
def to_html_str(self, *, content_only: bool = False, **kwargs) -> str:
    """Returns the HTML str of the object."""
    return self.to_html(**kwargs).to_str(content_only=content_only)

HtmlView

HtmlView(**kwargs)

Bases: View

Base class for HTML views.

Source code in pygx/views/_base.py
def __init__(self, **kwargs):
    del kwargs
    super().__init__()

Extension

Bases: Extension, HtmlConvertible

Base class for HtmlView extensions.

render

render(
    value: Any,
    *,
    name: str | None = None,
    root_path: KeyPath | None = None,
    **kwargs
) -> Html

Renders the input value into an HTML object.

Source code in pygx/views/html/_base.py
def render(
    self,
    value: Any,
    *,
    name: str | None = None,
    root_path: topology.KeyPath | None = None,
    **kwargs,
) -> Html:
    """Renders the input value into an HTML object."""
    # For customized HtmlConvertible objects, call their `to_html()` method.
    if (
        isinstance(value, HtmlConvertible)
        and not isinstance(value, self.__class__.Extension)
        and value.__class__.to_html is not HtmlConvertible.to_html
    ):
        return value.to_html(name=name, root_path=root_path, **kwargs)
    return self._render(value, name=name, root_path=root_path, **kwargs)

HtmlTreeView

Bases: HtmlView

HTML tree view: the default view for PyGX objects.

Renders a value as a collapsible HTML tree (view ID 'html-tree-view', the default for pg.to_html and pg.view). Symbolic and plain containers are rendered recursively; each node has a summary line with an optional tooltip and a collapsible content section. User classes customize the rendering by inheriting HtmlTreeView.Extension and overriding hook methods such as _html_tree_view_summary and _html_tree_view_content (pg.Object does not inherit the extension class automatically — list it as an explicit base).

Extension

Bases: Extension

Base class for custom tree-view rendering of user classes.

Inherit this class (alongside pg.Object or any other base) and override its hook methods to customize how instances appear in the tree view. The overridable hooks (private by convention, called by the view) are:

  • _html_tree_view_render: renders the whole subtree.
  • _html_tree_view_summary: renders the collapsible summary line.
  • _html_tree_view_content: renders the content below the summary.
  • _html_tree_view_css_styles: extra CSS styles for the subtree.
  • _html_tree_view_config: default rendering arguments for the subtree.

Example:

class Timestamp(pg.Object, pg.views.HtmlTreeView.Extension):
  seconds: int

  def _html_tree_view_summary(self, *, view, **kwargs):
    kwargs.pop('title', None)
    return view.summary(
        self, title=f'Timestamp({self.seconds})', **kwargs
    )

assert 'Timestamp(100)' in Timestamp(seconds=100).to_html_str()

should_collapse

should_collapse(
    value: Any,
    name: str | None,
    root_path: KeyPath,
    parent: Any,
    collapse_level: int | None = 1,
    uncollapse: KeyPathSet | NodeFilter | None = None,
) -> bool

Returns True if the object should be collapsed.

Parameters:

Name Type Description Default
value Any

The value to render.

required
name str | None

The referred field name of the value.

required
root_path KeyPath

The root path of the value.

required
parent Any

The parent of the value.

required
collapse_level int | None

The level of collapsing. If 0, the object will be collapsed (without showing its sub-nodes). If 1, the immediate sub-nodes will be shown in collapsed form. If None, all sub-tree will be shown.

1
uncollapse KeyPathSet | NodeFilter | None

Individual nodes to uncollapse, as a KeyPathSet or a function that takes (root_path, value, parent) and returns a KeyPathSet.

None

Returns:

Type Description
bool

True if the object should be collapsed.

Source code in pygx/views/html/_tree_view.py
def should_collapse(
    self,
    value: Any,
    name: str | None,
    root_path: KeyPath,
    parent: Any,
    collapse_level: int | None = 1,
    uncollapse: KeyPathSet | base.NodeFilter | None = None,
) -> bool:
    """Returns True if the object should be collapsed.

    Args:
      value: The value to render.
      name: The referred field name of the value.
      root_path: The root path of the value.
      parent: The parent of the value.
      collapse_level: The level of collapsing. If 0, the object will be
        collapsed (without showing its sub-nodes). If 1, the immediate sub-nodes
        will be shown in collapsed form. If None, all sub-tree will be shown.
      uncollapse: Individual nodes to uncollapse, as a KeyPathSet or a
        function that takes (root_path, value, parent) and returns a
        KeyPathSet.

    Returns:
      True if the object should be collapsed.
    """
    if collapse_level is None or collapse_level > 0:
        return False
    if callable(uncollapse):
        return not uncollapse(root_path, value, parent)
    if uncollapse is not None and root_path in uncollapse:
        return False
    # Always uncollapse simple types.
    if name is not None and isinstance(
        value, (bool, int, float, str, type(None))
    ):
        return False
    return True

needs_summary

needs_summary(
    value: Any,
    *,
    name: str | None = None,
    parent: Any = None,
    title: str | Html | None = None,
    enable_summary: bool | None = None,
    enable_summary_for_str: bool = True,
    max_summary_len_for_str: int = 80
) -> bool

Returns True if the object needs a summary.

Parameters:

Name Type Description Default
value Any

The value to render.

required
name str | None

The referred field name of the value.

None
parent Any

The parent of the value.

None
title str | Html | None

The title of the summary.

None
enable_summary bool | None

Whether to enable the summary. If None, a summary is generated when the value has a referred name or an explicit title, is a complex type, or is a string longer than max_summary_len_for_str.

None
enable_summary_for_str bool

Whether to enable the summary for strings.

True
max_summary_len_for_str int

The maximum length of the string to display.

80

Returns:

Type Description
bool

True if the object needs a summary.

Source code in pygx/views/html/_tree_view.py
def needs_summary(
    self,
    value: Any,
    *,
    name: str | None = None,
    parent: Any = None,
    title: str | Html | None = None,
    enable_summary: bool | None = None,
    enable_summary_for_str: bool = True,
    max_summary_len_for_str: int = 80,
) -> bool:
    """Returns True if the object needs a summary.

    Args:
      value: The value to render.
      name: The referred field name of the value.
      parent: The parent of the value.
      title: The title of the summary.
      enable_summary: Whether to enable the summary. If None, a summary is
        generated when the value has a referred name or an explicit title,
        is a complex type, or is a string longer than
        `max_summary_len_for_str`.
      enable_summary_for_str: Whether to enable the summary for strings.
      max_summary_len_for_str: The maximum length of the string to display.

    Returns:
      True if the object needs a summary.
    """
    del parent
    if isinstance(enable_summary, bool):
        return enable_summary
    assert enable_summary is None
    if not enable_summary_for_str and isinstance(value, str):
        return False
    if (
        name is None
        and title is None
        and (
            isinstance(value, (int, float, bool, type(None)))
            or (
                isinstance(value, str)
                and len(value) <= max_summary_len_for_str
            )
        )
    ):
        return False
    return True

summary

summary(
    value: Any,
    *,
    name: str | None = None,
    parent: Any = None,
    root_path: KeyPath | None = None,
    css_classes: Sequence[str] | None = None,
    title: str | Html | None = None,
    enable_summary: bool | None = None,
    enable_summary_tooltip: bool = True,
    summary_color: (
        tuple[str | None, str | None]
        | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]]
        | None
    ) = None,
    max_summary_len_for_str: int = 80,
    enable_summary_for_str: bool = True,
    enable_key_tooltip: bool = True,
    summary_tooltip_fn: Callable[..., Html] | None = None,
    key_tooltip_fn: Callable[..., Html] | None = None,
    extra_flags: dict[str, Any] | None = None
) -> Html | None

Renders the summary for an input value.

Parameters:

Name Type Description Default
value Any

The value to render.

required
name str | None

The referred field name of the value.

None
parent Any

The parent of the value.

None
root_path KeyPath | None

The root path of the value.

None
css_classes Sequence[str] | None

The CSS classes to add to the HTML element.

None
title str | Html | None

The title of the summary.

None
enable_summary bool | None

Whether to enable the summary. If None, a summary is generated when the value has a referred name or an explicit title, is a complex type, or is a string longer than max_summary_len_for_str.

None
enable_summary_tooltip bool

Whether to enable the summary tooltip.

True
summary_color tuple[str | None, str | None] | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]] | None

The color of the summary. If None, the summary will be rendered without a color. If a tuple, the first element is the text color and the second element is the background color. If a function, the function takes (root_path, value, parent) and returns a tuple of (text_color, background_color).

None
max_summary_len_for_str int

The maximum length of the string to display.

80
enable_summary_for_str bool

Whether to enable the summary for strings.

True
enable_key_tooltip bool

Whether to enable the key tooltip.

True
summary_tooltip_fn Callable[..., Html] | None

The function to render the summary tooltip.

None
key_tooltip_fn Callable[..., Html] | None

The function to render the key tooltip.

None
extra_flags dict[str, Any] | None

The extra flags to pass to the summary.

None

Returns:

Type Description
Html | None

An optional HTML object representing the summary of the value. If None,

Html | None

the summary will not be rendered.

Source code in pygx/views/html/_tree_view.py
@HtmlView.extension_method('_html_tree_view_summary')
def summary(
    self,
    value: Any,
    *,
    name: str | None = None,
    parent: Any = None,
    root_path: KeyPath | None = None,
    css_classes: Sequence[str] | None = None,
    title: str | Html | None = None,
    enable_summary: bool | None = None,
    enable_summary_tooltip: bool = True,
    summary_color: (
        tuple[str | None, str | None]
        | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]]
        | None
    ) = None,
    max_summary_len_for_str: int = 80,
    enable_summary_for_str: bool = True,
    enable_key_tooltip: bool = True,
    summary_tooltip_fn: Callable[..., Html] | None = None,
    key_tooltip_fn: Callable[..., Html] | None = None,
    extra_flags: dict[str, Any] | None = None,
) -> Html | None:
    """Renders the summary for an input value.

    Args:
      value: The value to render.
      name: The referred field name of the value.
      parent: The parent of the value.
      root_path: The root path of the value.
      css_classes: The CSS classes to add to the HTML element.
      title: The title of the summary.
      enable_summary: Whether to enable the summary. If None, a summary is
        generated when the value has a referred name or an explicit title,
        is a complex type, or is a string longer than
        `max_summary_len_for_str`.
      enable_summary_tooltip: Whether to enable the summary tooltip.
      summary_color: The color of the summary. If None, the summary will be
        rendered without a color. If a tuple, the first element is the text
        color and the second element is the background color. If a function,
        the function takes (root_path, value, parent) and returns a tuple of
        (text_color, background_color).
      max_summary_len_for_str: The maximum length of the string to display.
      enable_summary_for_str: Whether to enable the summary for strings.
      enable_key_tooltip: Whether to enable the key tooltip.
      summary_tooltip_fn: The function to render the summary tooltip.
      key_tooltip_fn: The function to render the key tooltip.
      extra_flags: The extra flags to pass to the summary.

    Returns:
      An optional HTML object representing the summary of the value. If None,
      the summary will not be rendered.
    """
    del extra_flags
    root_path = root_path or KeyPath()
    if not self.needs_summary(
        value,
        name=name,
        parent=parent,
        title=title,
        max_summary_len_for_str=max_summary_len_for_str,
        enable_summary=enable_summary,
        enable_summary_for_str=enable_summary_for_str,
    ):
        return None

    key_tooltip_fn = key_tooltip_fn or self.tooltip
    summary_tooltip_fn = summary_tooltip_fn or self.tooltip

    def make_title(value: Any):
        if inspect.isclass(value):
            return 'type'
        elif isinstance(value, (int, float, bool, str)):
            return type(value).__name__
        return f'{type(value).__name__}(...)'

    if name is not None:
        summary_color = self.get_color(
            summary_color, root_path + name, value, parent
        )
    else:
        summary_color = (None, None)

    return Html.element(
        'summary',
        [
            # Summary name.
            lambda: (
                Html.element(  # pylint: disable=g-long-ternary
                    'div',
                    [
                        name,
                        key_tooltip_fn(  # pylint: disable=g-long-ternary
                            root_path,
                            name=name,
                            parent=parent,
                            root_path=root_path,
                            css_classes=css_classes,
                        )
                        if enable_key_tooltip
                        else None,
                    ],
                    css_classes=['summary-name', css_classes],
                    styles=dict(
                        color=summary_color[0],
                        background_color=summary_color[1],
                    ),
                )
                if name is not None
                else None
            ),
            # Summary title
            Html.element(
                'div',
                [
                    title or make_title(value),
                ],
                css_classes=['summary-title', css_classes],
            ),
            # Summary tooltip.
            lambda: (
                summary_tooltip_fn(  # pylint: disable=g-long-ternary
                    value,
                    parent=parent,
                    root_path=root_path,
                    css_classes=css_classes,
                )
                if enable_summary_tooltip
                else None
            ),
        ],
    ).add_style(
        """
    details.pygx summary {
      font-weight: bold;
      margin: -0.5em -0.5em 0;
      padding: 0.5em;
    }
    .summary-name {
      display: inline;
      padding: 3px 5px 3px 5px;
      margin: 0 5px;
      border-radius: 3px;
    }
    .summary-title {
      display: inline;
    }
    .summary-name + div.summary-title {
      display: inline;
      color: #aaa;
    }
    .summary-title:hover + span.tooltip {
      visibility: visible;
    }
    .summary-name:hover > span.tooltip {
      visibility: visible;
      background-color: darkblue;
    }
    """
    )

object_key

object_key(
    root_path: KeyPath,
    *,
    value: Any,
    parent: Any,
    css_classes: Sequence[str] | None = None,
    key_color: (
        tuple[str | None, str | None]
        | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]]
        | None
    ) = None,
    enable_key_tooltip: bool = True,
    key_tooltip_fn: Callable[..., Html] | None = None,
    **kwargs: Any
) -> Html

Renders a label-style key for the value.

Parameters:

Name Type Description Default
root_path KeyPath

The root path of the value.

required
value Any

The value to render.

required
parent Any

The parent of the value.

required
css_classes Sequence[str] | None

The CSS classes to add to the HTML element.

None
key_color tuple[str | None, str | None] | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]] | None

The color of the key. If None, the key will be rendered without a color. If a tuple, the first element is the text color and the second element is the background color. If a function, the function takes (root_path, value, parent) and returns a tuple of (text_color, background_color).

None
enable_key_tooltip bool

Whether to enable the tooltip.

True
key_tooltip_fn Callable[..., Html] | None

The function to render the key tooltip.

None
**kwargs Any

Additional arguments passed by the user that will be ignored.

{}

Returns:

Type Description
Html

The rendered HTML as the key of the value.

Source code in pygx/views/html/_tree_view.py
def object_key(
    self,
    root_path: KeyPath,
    *,
    value: Any,
    parent: Any,
    css_classes: Sequence[str] | None = None,
    key_color: (
        tuple[str | None, str | None]
        | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]]
        | None
    ) = None,
    enable_key_tooltip: bool = True,
    key_tooltip_fn: Callable[..., Html] | None = None,
    **kwargs: Any,
) -> Html:
    """Renders a label-style key for the value.

    Args:
      root_path: The root path of the value.
      value: The value to render.
      parent: The parent of the value.
      css_classes: The CSS classes to add to the HTML element.
      key_color: The color of the key. If None, the key will be rendered
        without a color. If a tuple, the first element is the text color and
        the second element is the background color. If a function, the function
        takes (root_path, value, parent) and returns a tuple of (text_color,
        background_color).
      enable_key_tooltip: Whether to enable the tooltip.
      key_tooltip_fn: The function to render the key tooltip.
      **kwargs: Additional arguments passed by the user that will be ignored.

    Returns:
      The rendered HTML as the key of the value.
    """
    del kwargs
    key_tooltip_fn = key_tooltip_fn or self.tooltip
    key_color = self.get_color(key_color, root_path, value, parent)
    # Build the result by writing directly into the key span — the
    # previous `+` form deep-copied the LHS once per label-style key.
    result = Html.element(
        'span',
        [
            str(root_path.key),
        ],
        css_classes=[
            'object-key',
            type(root_path.key).__name__,
            css_classes,
        ],
        styles=dict(
            color=key_color[0],
            background_color=key_color[1],
        ),
    )
    if enable_key_tooltip:
        result.write(
            key_tooltip_fn(
                value=root_path,
                root_path=root_path,
                parent=parent,
            )
        )
    return result.add_style(
        """
    .object-key {
      margin: 0.15em 0.3em 0.15em 0;
      display: block;
    }
    .object-key:hover + .tooltip {
      visibility: visible;
      background-color: darkblue;
    }
    .object-key.str {
      color: gray;
      border: 1px solid lightgray;
      background-color: ButtonFace;
      border-radius: 0.2em;
      padding: 0.3em;
    }
    .object-key.int::before{
      content: '[';
    }
    .object-key.int::after{
      content: ']';
    }
    .object-key.int{
      border: 0;
      color: lightgray;
      background-color: transparent;
      border-radius: 0;
      padding: 0;
    }
    """
    )

content

content(
    value: Any,
    *,
    name: str | None = None,
    parent: Any = None,
    root_path: KeyPath | None = None,
    css_classes: Sequence[str] | None = None,
    enable_summary: bool | None = None,
    enable_summary_for_str: bool = True,
    max_summary_len_for_str: int = 80,
    enable_summary_tooltip: bool = True,
    key_style: (
        Literal["label", "summary"]
        | Callable[[KeyPath, Any, Any], Literal["label", "summary"]]
    ) = "summary",
    key_color: (
        tuple[str | None, str | None]
        | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]]
        | None
    ) = None,
    include_keys: (
        Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None
    ) = None,
    exclude_keys: (
        Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None
    ) = None,
    enable_key_tooltip: bool = True,
    collapse_level: int | None = 1,
    uncollapse: KeyPathSet | NodeFilter | None = None,
    highlight: NodeFilter | None = None,
    lowlight: NodeFilter | None = None,
    child_config: dict[str, Any] | None = None,
    extra_flags: dict[str, Any] | None = None,
    debug: bool = False
) -> Html

Renders the main content for the value.

Parameters:

Name Type Description Default
value Any

The value to render.

required
name str | None

The name of the value.

None
parent Any

The parent of the value.

None
root_path KeyPath | None

The root path of the value.

None
css_classes Sequence[str] | None

CSS classes to add to the HTML element.

None
enable_summary bool | None

Whether to enable the summary.

None
enable_summary_for_str bool

Whether to enable the summary for string.

True
max_summary_len_for_str int

The maximum length of the string to display.

80
enable_summary_tooltip bool

Whether to enable the summary tooltip.

True
key_style Literal['label', 'summary'] | Callable[[KeyPath, Any, Any], Literal['label', 'summary']]

The style of the key. It can be either 'label' or 'summary'. If it is a function, the function takes (root_path, value, parent) and returns either 'label' or 'summary'.

'summary'
key_color tuple[str | None, str | None] | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]] | None

The color of the key. If it is a tuple, the first element is the text color and the second element is the background color. If it is a function, the function takes (root_path, value, parent) and returns a tuple of (text_color, background_color).

None
include_keys Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None

The keys to include (at the immediate child level). If a function, it is called with (child_path, child_value, parent) for each immediate child and returns whether the key should be included.

None
exclude_keys Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None

The keys to exclude (at the immediate child level). If a function, it is called with (child_path, child_value, parent) for each immediate child and returns whether the key should be excluded.

None
enable_key_tooltip bool

Whether to enable the key tooltip.

True
collapse_level int | None

The level to collapse the tree.

1
uncollapse KeyPathSet | NodeFilter | None

A key path set (relative to root_path) for the nodes to uncollapse. or a function with signature (path, value, parent) -> bool to filter nodes to uncollapse.

None
highlight NodeFilter | None

A function with signature (path, value, parent) -> bool to determine whether to highlight.

None
lowlight NodeFilter | None

A function with signature (path, value, parent) -> bool to determine whether to lowlight.

None
child_config dict[str, Any] | None

The configuration for rendering the child nodes.

None
extra_flags dict[str, Any] | None

Extra flags to pass to the child render.

None
debug bool

Whether to enable debug mode.

False

Returns:

Type Description
Html

The rendered HTML as the main content of the value.

Source code in pygx/views/html/_tree_view.py
@HtmlView.extension_method('_html_tree_view_content')
def content(
    self,
    value: Any,
    *,
    name: str | None = None,
    parent: Any = None,
    root_path: KeyPath | None = None,
    css_classes: Sequence[str] | None = None,
    # Summary settings (for child nodes).
    enable_summary: bool | None = None,
    enable_summary_for_str: bool = True,
    max_summary_len_for_str: int = 80,
    enable_summary_tooltip: bool = True,
    # Content settings.
    key_style: (
        Literal['label', 'summary']
        | Callable[[KeyPath, Any, Any], Literal['label', 'summary']]
    ) = 'summary',
    key_color: (
        tuple[str | None, str | None]
        | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]]
        | None
    ) = None,
    include_keys: (
        Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None
    ) = None,
    exclude_keys: (
        Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None
    ) = None,
    enable_key_tooltip: bool = True,
    # Collapse settings.
    collapse_level: int | None = 1,
    uncollapse: KeyPathSet | base.NodeFilter | None = None,
    # Other settings.
    highlight: base.NodeFilter | None = None,
    lowlight: base.NodeFilter | None = None,
    child_config: dict[str, Any] | None = None,
    extra_flags: dict[str, Any] | None = None,
    debug: bool = False,
) -> Html:
    """Renders the main content for the value.

    Args:
      value: The value to render.
      name: The name of the value.
      parent: The parent of the value.
      root_path: The root path of the value.
      css_classes: CSS classes to add to the HTML element.
      enable_summary: Whether to enable the summary.
      enable_summary_for_str: Whether to enable the summary for string.
      max_summary_len_for_str: The maximum length of the string to display.
      enable_summary_tooltip: Whether to enable the summary tooltip.
      key_style: The style of the key. It can be either 'label' or 'summary'.
        If it is a function, the function takes (root_path, value, parent) and
        returns either 'label' or 'summary'.
      key_color: The color of the key. If it is a tuple, the first element is
        the text color and the second element is the background color. If it is
        a function, the function takes (root_path, value, parent) and returns
        a tuple of (text_color, background_color).
      include_keys: The keys to include (at the immediate child level).
        If a function, it is called with (child_path, child_value,
        parent) for each immediate child and returns whether the key
        should be included.
      exclude_keys: The keys to exclude (at the immediate child level).
        If a function, it is called with (child_path, child_value,
        parent) for each immediate child and returns whether the key
        should be excluded.
      enable_key_tooltip: Whether to enable the key tooltip.
      collapse_level: The level to collapse the tree.
      uncollapse: A key path set (relative to root_path) for the nodes to
        uncollapse. or a function with signature (path, value, parent) -> bool
        to filter nodes to uncollapse.
      highlight: A function with signature (path, value, parent) -> bool
        to determine whether to highlight.
      lowlight: A function with signature (path, value, parent) -> bool
        to determine whether to lowlight.
      child_config: The configuration for rendering the child nodes.
      extra_flags: Extra flags to pass to the child render.
      debug: Whether to enable debug mode.

    Returns:
      The rendered HTML as the main content of the value.
    """
    root_path = root_path or KeyPath()
    if isinstance(value, pg_symbolic.Symbolic):
        extra_flags = extra_flags or {}
        exclude_frozen = extra_flags.get('exclude_frozen', True)
        exclude_defaults = extra_flags.get('exclude_defaults', False)
        use_inferred = extra_flags.get('use_inferred', False)
        items = {}
        for k, v in value.sym_items():
            # Apply frozen filter.
            field = value.sym_attr_field(k)
            if exclude_frozen and field and field.frozen:
                continue

            # Apply inferred value.
            if use_inferred and isinstance(v, pg_symbolic.Inferential):
                v = value.sym_inferred(k, default=v)

            # Apply default value filter.
            if field and exclude_defaults and v == field.default_value:
                continue
            items[k] = v
    elif isinstance(value, (tuple, list)):
        items = {i: v for i, v in enumerate(value)}
    elif isinstance(value, dict):
        items = value
    else:
        return self.simple_value(
            value,
            name=name,
            parent=parent,
            root_path=root_path,
            css_classes=css_classes,
            max_summary_len_for_str=max_summary_len_for_str,
        )
    return self.complex_value(
        items,  # pyright: ignore[reportArgumentType]
        name=name,
        parent=value,
        root_path=root_path,
        css_classes=css_classes,
        enable_summary=enable_summary,
        enable_summary_for_str=enable_summary_for_str,
        max_summary_len_for_str=max_summary_len_for_str,
        enable_summary_tooltip=enable_summary_tooltip,
        key_style=key_style,
        key_color=key_color,
        enable_key_tooltip=enable_key_tooltip,
        include_keys=include_keys,
        exclude_keys=exclude_keys,
        collapse_level=collapse_level,
        uncollapse=uncollapse,
        child_config=child_config,
        highlight=highlight,
        lowlight=lowlight,
        extra_flags=extra_flags,
        debug=debug,
    )

simple_value

simple_value(
    value: Any,
    *,
    name: str | None = None,
    parent: Any = None,
    root_path: KeyPath | None = None,
    css_classes: Sequence[str] | None = None,
    max_summary_len_for_str: int = 80
) -> Html

Renders a simple value.

Parameters:

Name Type Description Default
value Any

The value to render.

required
name str | None

The name of the value.

None
parent Any

The parent of the value.

None
root_path KeyPath | None

The root path of the value.

None
css_classes Sequence[str] | None

CSS classes to add to the HTML element.

None
max_summary_len_for_str int

The maximum length of the string to display.

80

Returns:

Type Description
Html

The rendered HTML as the simple value.

Source code in pygx/views/html/_tree_view.py
def simple_value(
    self,
    value: Any,
    *,
    name: str | None = None,
    parent: Any = None,
    root_path: KeyPath | None = None,
    css_classes: Sequence[str] | None = None,
    max_summary_len_for_str: int = 80,
) -> Html:
    """Renders a simple value.

    Args:
      value: The value to render.
      name: The name of the value.
      parent: The parent of the value.
      root_path: The root path of the value.
      css_classes: CSS classes to add to the HTML element.
      max_summary_len_for_str: The maximum length of the string to display.

    Returns:
      The rendered HTML as the simple value.
    """
    del name, parent, root_path

    def value_repr() -> str:
        if isinstance(value, str):
            if len(value) < max_summary_len_for_str:
                return repr(value)
            else:
                return value
        return formatting.format(
            value,
            compact=False,
            verbose=False,
            exclude_defaults=True,
            python_format=True,
            use_inferred=True,
            max_bytes_len=64,
        )

    return Html.element(
        'span',
        [
            Html.escape(value_repr),
        ],
        css_classes=[
            'simple-value',
            self.css_class_name(value),
            css_classes,
        ],
    ).add_style(
        """
    .simple-value {
      --value-color: blue;
      --str-color: darkred;
      --num-color: darkblue;
      color: var(--value-color);
      display: inline-block;
      white-space: pre-wrap;
      padding: 0.2em;
      margin-top: 0.15em;
    }
    .simple-value.str {
      color: var(--str-color);
      font-style: italic;
    }
    .simple-value.int, .simple-value.float {
      color: var(--num-color);
    }
    @media (prefers-color-scheme: dark) {
      .simple-value {
        --value-color: #79c0ff;
        --str-color: #ff7b72;
        --num-color: #79c0ff;
      }
    }
    html[theme=dark] .simple-value {
      --value-color: #79c0ff;
      --str-color: #ff7b72;
      --num-color: #79c0ff;
    }
    """
    )

complex_value

complex_value(
    kv: dict[int | str, Any],
    *,
    parent: Any,
    root_path: KeyPath,
    name: str | None = None,
    css_classes: Sequence[str] | None = None,
    enable_summary: bool | None = None,
    enable_summary_for_str: bool = True,
    max_summary_len_for_str: int = 80,
    enable_summary_tooltip: bool = True,
    key_style: (
        Literal["label", "summary"] | Callable[..., Literal["label", "summary"]]
    ) = "summary",
    key_color: (
        tuple[str | None, str | None]
        | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]]
        | None
    ) = None,
    include_keys: (
        Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None
    ) = None,
    exclude_keys: (
        Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None
    ) = None,
    enable_key_tooltip: bool = True,
    collapse_level: int | None = 1,
    uncollapse: KeyPathSet | NodeFilter | None = None,
    child_config: dict[str, Any] | None = None,
    highlight: NodeFilter | None = None,
    lowlight: NodeFilter | None = None,
    render_key_fn: Callable[..., Html] | None = None,
    render_value_fn: Callable[..., Html] | None = None,
    extra_flags: dict[str, Any] | None = None,
    debug: bool = False
) -> Html

Renders a list of key-value pairs.

Parameters:

Name Type Description Default
kv dict[int | str, Any]

The key-value pairs to render.

required
parent Any

The parent of the value.

required
root_path KeyPath

The root path of the value.

required
name str | None

The name of the value.

None
css_classes Sequence[str] | None

CSS classes to add to the HTML element.

None
enable_summary bool | None

Whether to enable the summary. If None, the default is to enable the summary for non-string and disable the summary for string.

None
enable_summary_for_str bool

Whether to enable the summary for string.

True
max_summary_len_for_str int

The maximum length of the string to display.

80
enable_summary_tooltip bool

Whether to enable the summary tooltip.

True
key_style Literal['label', 'summary'] | Callable[..., Literal['label', 'summary']]

The style of the key. It can be either 'label' or 'summary'. If it is a function, the function takes (root_path, value, parent) and returns either 'label' or 'summary'.

'summary'
key_color tuple[str | None, str | None] | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]] | None

The color of the key. If it is a tuple, the first element is the text color and the second element is the background color. If it is a function, the function takes (root_path, value, parent) and returns a tuple of (text_color, background_color).

None
include_keys Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None

The keys to include (at the immediate child level). If a function, it is called with (child_path, child_value, parent) for each immediate child and returns whether the key should be included.

None
exclude_keys Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None

The keys to exclude (at the immediate child level). If a function, it is called with (child_path, child_value, parent) for each immediate child and returns whether the key should be excluded.

None
enable_key_tooltip bool

Whether to enable the key tooltip.

True
collapse_level int | None

The level to collapse the tree.

1
uncollapse KeyPathSet | NodeFilter | None

A key path set (relative to root_path) for the nodes to uncollapse. or a function with signature (path, value, parent) -> bool to filter nodes to uncollapse.

None
child_config dict[str, Any] | None

The configuration for rendering the child nodes.

None
highlight NodeFilter | None

A function with signature (path, value, parent) -> bool to determine whether to highlight.

None
lowlight NodeFilter | None

A function with signature (path, value, parent) -> bool to determine whether to lowlight.

None
render_key_fn Callable[..., Html] | None

A custom function to render the label-style key.

None
render_value_fn Callable[..., Html] | None

A custom function to render the child value.

None
extra_flags dict[str, Any] | None

Extra flags to pass to the child render.

None
debug bool

Whether to enable debug mode.

False

Returns:

Type Description
Html

The rendered HTML as the key-value pairs.

Source code in pygx/views/html/_tree_view.py
def complex_value(
    self,
    kv: dict[int | str, Any],
    *,
    parent: Any,
    root_path: KeyPath,
    name: str | None = None,
    css_classes: Sequence[str] | None = None,
    # Summary settings (for child nodes).
    enable_summary: bool | None = None,
    enable_summary_for_str: bool = True,
    max_summary_len_for_str: int = 80,
    enable_summary_tooltip: bool = True,
    # Content settings.
    key_style: (
        Literal['label', 'summary']
        | Callable[..., Literal['label', 'summary']]
    ) = 'summary',
    key_color: (
        tuple[str | None, str | None]
        | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]]
        | None
    ) = None,
    include_keys: (
        Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None
    ) = None,
    exclude_keys: (
        Iterable[int | str] | Callable[[KeyPath, Any, Any], bool] | None
    ) = None,
    enable_key_tooltip: bool = True,
    # Collapse settings.
    collapse_level: int | None = 1,
    uncollapse: KeyPathSet | base.NodeFilter | None = None,
    # Other settings.
    child_config: dict[str, Any] | None = None,
    highlight: base.NodeFilter | None = None,
    lowlight: base.NodeFilter | None = None,
    # Custom render functions.
    render_key_fn: Callable[..., Html] | None = None,
    render_value_fn: Callable[..., Html] | None = None,
    extra_flags: dict[str, Any] | None = None,
    debug: bool = False,
) -> Html:
    """Renders a list of key-value pairs.

    Args:
      kv: The key-value pairs to render.
      parent: The parent of the value.
      root_path: The root path of the value.
      name: The name of the value.
      css_classes: CSS classes to add to the HTML element.
      enable_summary: Whether to enable the summary. If None, the default is
        to enable the summary for non-string and disable the summary for
        string.
      enable_summary_for_str: Whether to enable the summary for string.
      max_summary_len_for_str: The maximum length of the string to display.
      enable_summary_tooltip: Whether to enable the summary tooltip.
      key_style: The style of the key. It can be either 'label' or 'summary'.
        If it is a function, the function takes (root_path, value, parent) and
        returns either 'label' or 'summary'.
      key_color: The color of the key. If it is a tuple, the first element is
        the text color and the second element is the background color. If it is
        a function, the function takes (root_path, value, parent) and returns
        a tuple of (text_color, background_color).
      include_keys: The keys to include (at the immediate child level).
        If a function, it is called with (child_path, child_value,
        parent) for each immediate child and returns whether the key
        should be included.
      exclude_keys: The keys to exclude (at the immediate child level).
        If a function, it is called with (child_path, child_value,
        parent) for each immediate child and returns whether the key
        should be excluded.
      enable_key_tooltip: Whether to enable the key tooltip.
      collapse_level: The level to collapse the tree.
      uncollapse: A key path set (relative to root_path) for the nodes to
        uncollapse. or a function with signature (path, value, parent) -> bool
        to filter nodes to uncollapse.
      child_config: The configuration for rendering the child nodes.
      highlight: A function with signature (path, value, parent) -> bool
        to determine whether to highlight.
      lowlight: A function with signature (path, value, parent) -> bool
        to determine whether to lowlight.
      render_key_fn: A custom function to render the label-style key.
      render_value_fn: A custom function to render the child value.
      extra_flags: Extra flags to pass to the child render.
      debug: Whether to enable debug mode.

    Returns:
      The rendered HTML as the key-value pairs.
    """
    del name
    root_path = root_path or KeyPath()
    uncollapse = self.init_uncollapse(uncollapse)
    extra_flags = extra_flags or {}

    inherited_kwargs = dict(
        # For child summary.
        enable_summary=enable_summary,
        enable_summary_for_str=enable_summary_for_str,
        max_summary_len_for_str=max_summary_len_for_str,
        enable_summary_tooltip=enable_summary_tooltip,
        # For child content.
        enable_key_tooltip=enable_key_tooltip,
        key_style=key_style,
        key_color=key_color,
        include_keys=include_keys if callable(include_keys) else None,
        exclude_keys=exclude_keys if callable(exclude_keys) else None,
        collapse_level=None
        if collapse_level is None
        else (collapse_level - 1),
        uncollapse=uncollapse,
        highlight=highlight,
        lowlight=lowlight,
        extra_flags=extra_flags,
        debug=debug,
    )

    render_key_fn = render_key_fn or HtmlTreeView.object_key
    render_value_fn = render_value_fn or HtmlTreeView.render

    def render_child_key(child_path, value, parent, child_kwargs):
        render_child_key_fn = child_kwargs['extra_flags'].get(
            'render_key_fn', render_key_fn
        )
        return render_child_key_fn(
            self, child_path, value=value, parent=parent, **child_kwargs
        )

    def render_child_value(name, value, child_path, child_kwargs):
        render_child_value_fn = child_kwargs['extra_flags'].get(
            'render_value_fn', render_value_fn
        )
        child_html = render_child_value_fn(
            self,
            value=value,
            name=child_kwargs.pop('name', name),
            parent=parent,
            root_path=child_path,
            **child_kwargs,
        )
        should_highlight = highlight and highlight(
            child_path, value, parent
        )
        should_lowlight = lowlight and lowlight(child_path, value, parent)
        if should_highlight or should_lowlight:
            return Html.element(
                'div',
                [child_html],
                css_classes=[
                    'highlight' if should_highlight else None,
                    'lowlight' if should_lowlight else None,
                ],
            )
        else:
            return child_html

    has_child = False
    s = Html()
    if kv:
        # Compute included keys.
        if callable(include_keys):
            include_keys = [
                k
                for k, v in kv.items()
                if include_keys(root_path + k, v, parent)
            ]
        elif include_keys is not None:
            include_keys = list(k for k in include_keys if k in kv)
        else:
            include_keys = list(kv.keys())

        # Filter with excluded keys.
        if callable(exclude_keys):
            include_keys = [
                k
                for k in include_keys
                if not exclude_keys(root_path + k, kv[k], parent)
            ]
        elif exclude_keys is not None:
            exclude_keys = set(exclude_keys)
            include_keys = [
                k for k in include_keys if k not in exclude_keys
            ]

        # Figure out keys of different styles.
        label_keys = []
        summary_keys = []
        if isinstance(parent, (tuple, list)) or key_style == 'label':
            label_keys = include_keys
        elif key_style == 'summary':
            summary_keys = include_keys
        else:
            assert callable(key_style), key_style
            for k in include_keys:
                ks = key_style(root_path + k, kv[k], parent)
                if ks == 'summary':
                    summary_keys.append(k)
                elif ks == 'label':
                    label_keys.append(k)

        # Render child nodes with summary keys.
        if summary_keys:
            for k in summary_keys:
                child_path = root_path + k
                child_kwargs = self.get_child_kwargs(
                    inherited_kwargs, child_config, k, root_path
                )
                s.write(
                    render_child_value(k, kv[k], child_path, child_kwargs)
                )
                has_child = True

        # Render child nodes with label keys.
        if label_keys:
            s.write('<table>')
            for k in label_keys:
                v = kv[k]
                child_path = root_path + k
                child_kwargs = self.get_child_kwargs(
                    inherited_kwargs, child_config, k, root_path
                )
                key_cell = render_child_key(
                    child_path, v, parent, child_kwargs
                )
                value_cell = render_child_value(
                    None, v, child_path, child_kwargs
                )
                if value_cell is not None:
                    s.write(
                        Html.element(
                            'tr',
                            [
                                '<td>',
                                key_cell,
                                '</td>',
                                '<td>',
                                value_cell,
                                '</td>',
                            ],
                        )
                    )
                    has_child = True
            s.write('</table>')

    if not has_child:
        s.write(Html.element('span', css_classes=['empty-container']))

    return Html.element(
        'div',
        [s],
        css_classes=[
            'complex-value',
            self.css_class_name(parent),
            css_classes,
        ],
    ).add_style(
        """
    span.empty-container::before {
        content: '(empty)';
        font-style: italic;
        margin-left: 0.5em;
        color: #aaa;
    }
    """
    )

tooltip

tooltip(
    value: Any,
    *,
    parent: Any = None,
    root_path: KeyPath | None = None,
    css_classes: Sequence[str] | None = None,
    id: str | None = None,
    content: str | Html | None = None,
    **kwargs: Any
) -> Html

Renders a tooltip for the value.

Parameters:

Name Type Description Default
value Any

The value to render.

required
parent Any

The parent of the value.

None
root_path KeyPath | None

The root path of the value.

None
css_classes Sequence[str] | None

CSS classes to add to the HTML element.

None
id str | None

The ID of the tooltip span element. If None, no ID will be added.

None
content str | Html | None

The content to render. If None, the value will be rendered.

None
**kwargs Any

Additional keyword arguments passed from the user that will be ignored.

{}

Returns:

Type Description
Html

The rendered HTML as the tooltip of the value.

Source code in pygx/views/html/_tree_view.py
def tooltip(
    self,
    value: Any,
    *,
    parent: Any = None,
    root_path: KeyPath | None = None,
    css_classes: Sequence[str] | None = None,
    id: str | None = None,  # pylint: disable=redefined-builtin
    content: str | Html | None = None,
    **kwargs: Any,
) -> Html:
    """Renders a tooltip for the value.

    Args:
      value: The value to render.
      parent: The parent of the value.
      root_path: The root path of the value.
      css_classes: CSS classes to add to the HTML element.
      id: The ID of the tooltip span element. If None, no ID will be added.
      content: The content to render. If None, the value will be rendered.
      **kwargs: Additional keyword arguments passed from the user that
        will be ignored.

    Returns:
      The rendered HTML as the tooltip of the value.
    """
    del parent, kwargs
    if content is None:
        content = Html.escape(
            formatting.format(
                value,
                root_path=root_path,
                compact=False,
                verbose=False,
                python_format=True,
                max_bytes_len=64,
                max_str_len=256,
            )
        )
    return Html.element(
        'span',
        [content],
        id=id,
        css_classes=[
            'tooltip',
            css_classes,
        ],
    ).add_style(
        """
    span.tooltip {
      visibility: hidden;
      white-space: pre-wrap;
      font-weight: normal;
      background-color: #484848;
      color: #fff;
      padding: 10px;
      border-radius: 6px;
      position: absolute;
      z-index: 1;
    }
    span.tooltip:hover {
      visibility: visible;
    }
    """
    )

css_class_name staticmethod

css_class_name(value: Any) -> str | None

Returns the CSS class name for the value.

Source code in pygx/views/html/_tree_view.py
@staticmethod
def css_class_name(value: Any) -> str | None:
    """Returns the CSS class name for the value."""
    if inspect.isclass(value):
        return formatting.camel_to_snake(f'{value.__name__}-class', '-')
    cache = HtmlTreeView._CSS_CLASS_NAME_CACHE
    cls = type(value)
    cached = cache.get(cls)
    if cached is None:
        cached = formatting.camel_to_snake(cls.__name__, '-')
        cache[cls] = cached
    return cached

init_uncollapse staticmethod

init_uncollapse(
    uncollapse: Iterable[KeyPath | str] | NodeFilter | None,
) -> KeyPathSet | NodeFilter

Initializes the uncollapse argument.

Source code in pygx/views/html/_tree_view.py
@staticmethod
def init_uncollapse(
    uncollapse: Iterable[KeyPath | str] | base.NodeFilter | None,
) -> KeyPathSet | base.NodeFilter:
    """Initializes the uncollapse argument."""
    if uncollapse is None:
        return KeyPathSet()
    elif callable(uncollapse):
        return uncollapse
    else:
        return KeyPathSet.from_value(uncollapse, include_intermediate=True)

get_child_kwargs staticmethod

get_child_kwargs(
    call_kwargs: dict[str, Any],
    child_config: dict[str, Any] | None,
    child_key: int | str | None,
    root_path: KeyPath,
) -> dict[str, Any]

Returns the render kwargs for a child key with its config applied.

Source code in pygx/views/html/_tree_view.py
@staticmethod
def get_child_kwargs(
    call_kwargs: dict[str, Any],
    child_config: dict[str, Any] | None,
    child_key: int | str | None,
    root_path: KeyPath,
) -> dict[str, Any]:
    """Returns the render kwargs for a child key with its config applied."""
    if not child_config:
        return call_kwargs

    child_kwargs = child_config.get(  # pyright: ignore[reportCallIssue]
        child_key,  # pyright: ignore[reportArgumentType]
        child_config.get('__default__', None),
    )
    if not child_kwargs:
        return call_kwargs

    return HtmlTreeView.get_kwargs(
        call_kwargs,
        child_kwargs,
        root_path + child_key,
    )

get_passthrough_kwargs staticmethod

get_passthrough_kwargs(
    *,
    enable_summary: bool | None = MISSING_VALUE,
    enable_summary_for_str: bool = MISSING_VALUE,
    max_summary_len_for_str: int = MISSING_VALUE,
    enable_summary_tooltip: bool = MISSING_VALUE,
    key_style: (
        Literal["label", "summary"]
        | Callable[[KeyPath, Any, Any], Literal["label", "summary"]]
    ) = MISSING_VALUE,
    key_color: (
        tuple[str | None, str | None]
        | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]]
    ) = MISSING_VALUE,
    include_keys: (
        Iterable[int | str]
        | Callable[[KeyPath, Any, Any], Iterable[int | str]]
        | None
    ) = MISSING_VALUE,
    exclude_keys: (
        Iterable[int | str]
        | Callable[[KeyPath, Any, Any], Iterable[int | str]]
        | None
    ) = MISSING_VALUE,
    enable_key_tooltip: bool = MISSING_VALUE,
    uncollapse: KeyPathSet | NodeFilter | None = MISSING_VALUE,
    extra_flags: dict[str, Any] | None = MISSING_VALUE,
    highlight: NodeFilter | None = MISSING_VALUE,
    lowlight: NodeFilter | None = MISSING_VALUE,
    debug: bool = MISSING_VALUE,
    remove: Iterable[str] | None = None,
    **kwargs
)

Gets the rendering arguments to pass through to the child nodes.

Source code in pygx/views/html/_tree_view.py
@staticmethod
def get_passthrough_kwargs(
    *,
    enable_summary: bool | None = topology.MISSING_VALUE,
    enable_summary_for_str: bool = topology.MISSING_VALUE,
    max_summary_len_for_str: int = topology.MISSING_VALUE,
    enable_summary_tooltip: bool = topology.MISSING_VALUE,
    key_style: (
        Literal['label', 'summary']
        | Callable[[KeyPath, Any, Any], Literal['label', 'summary']]
    ) = topology.MISSING_VALUE,
    key_color: (
        tuple[str | None, str | None]
        | Callable[[KeyPath, Any, Any], tuple[str | None, str | None]]
    ) = topology.MISSING_VALUE,
    include_keys: (
        Iterable[int | str]
        | Callable[[KeyPath, Any, Any], Iterable[int | str]]
        | None
    ) = topology.MISSING_VALUE,
    exclude_keys: (
        Iterable[int | str]
        | Callable[[KeyPath, Any, Any], Iterable[int | str]]
        | None
    ) = topology.MISSING_VALUE,
    enable_key_tooltip: bool = topology.MISSING_VALUE,
    uncollapse: (
        KeyPathSet | base.NodeFilter | None
    ) = topology.MISSING_VALUE,
    extra_flags: dict[str, Any] | None = topology.MISSING_VALUE,
    highlight: base.NodeFilter | None = topology.MISSING_VALUE,
    lowlight: base.NodeFilter | None = topology.MISSING_VALUE,
    debug: bool = topology.MISSING_VALUE,
    remove: Iterable[str] | None = None,
    **kwargs,
):
    # pytype: enable=annotation-type-mismatch
    """Gets the rendering arguments to pass through to the child nodes."""
    del kwargs
    passthrough_kwargs = dict(
        enable_summary=enable_summary,
        enable_summary_for_str=enable_summary_for_str,
        max_summary_len_for_str=max_summary_len_for_str,
        enable_summary_tooltip=enable_summary_tooltip,
        enable_key_tooltip=enable_key_tooltip,
        key_style=key_style,
        key_color=key_color,
        include_keys=(
            include_keys
            if callable(include_keys)
            else topology.MISSING_VALUE
        ),
        exclude_keys=(
            exclude_keys
            if callable(exclude_keys)
            else topology.MISSING_VALUE
        ),
        uncollapse=uncollapse,
        highlight=highlight,
        lowlight=lowlight,
        extra_flags=extra_flags,
        debug=debug,
    )
    # Filter out missing values.
    passthrough_kwargs = {
        k: v
        for k, v in passthrough_kwargs.items()
        if v is not topology.MISSING_VALUE
    }
    if remove:
        return {
            k: v
            for k, v in passthrough_kwargs.items()
            if k not in remove  # pytype: disable=unsupported-operands
        }
    return passthrough_kwargs

get_collapse_level staticmethod

get_collapse_level(
    original_level: None | int | tuple[int | None, int],
    overriden_level: None | int | tuple[int | None, int],
) -> int | None

Gets the collapse level for a child node.

Source code in pygx/views/html/_tree_view.py
@staticmethod
def get_collapse_level(
    original_level: None | int | tuple[int | None, int],
    overriden_level: None | int | tuple[int | None, int],
) -> int | None:
    """Gets the collapse level for a child node."""
    original_offset, overriden_offset = 0, 0
    if isinstance(original_level, tuple):
        original_level, original_offset = original_level
    if isinstance(overriden_level, tuple):
        overriden_level, overriden_offset = overriden_level

    if original_level is None:
        return original_level
    elif overriden_level is None:
        return overriden_level
    else:
        return max(
            original_level + original_offset,
            overriden_level + overriden_offset,
        )

get_kwargs staticmethod

get_kwargs(
    call_kwargs: dict[str, Any],
    overriden_kwargs: dict[str, Any],
    root_path: KeyPath | None = None,
) -> dict[str, Any]

Returns render arguments with overrides applied.

Source code in pygx/views/html/_tree_view.py
@staticmethod
def get_kwargs(
    call_kwargs: dict[str, Any],
    overriden_kwargs: dict[str, Any],
    root_path: KeyPath | None = None,
) -> dict[str, Any]:
    """Returns render arguments with overrides applied."""
    # Select child config to override.
    if not overriden_kwargs:
        return call_kwargs

    call_kwargs = call_kwargs.copy()
    overriden_kwargs = overriden_kwargs.copy()

    # Override collapse_level.
    if (
        'collapse_level' in call_kwargs
        or 'collapse_level' in overriden_kwargs
    ):
        call_kwargs['collapse_level'] = HtmlTreeView.get_collapse_level(
            call_kwargs.pop('collapse_level', 1),
            overriden_kwargs.pop('collapse_level', 0),
        )

    # Override uncollapse.
    if 'uncollapse' in call_kwargs or 'uncollapse' in overriden_kwargs:
        uncollapse = KeyPathSet.from_value(
            call_kwargs.pop('uncollapse', None) or []
        )
        child_uncollapse = KeyPathSet.from_value(
            overriden_kwargs.pop('uncollapse', None) or []
        )
        call_kwargs['uncollapse'] = HtmlTreeView.merge_uncollapse(
            uncollapse, child_uncollapse, root_path
        )

    # Deep hierarchy merge.
    return topology.merge_tree(call_kwargs, overriden_kwargs)

merge_uncollapse staticmethod

merge_uncollapse(
    uncollapse: KeyPathSet | NodeFilter | None,
    child_uncollapse: KeyPathSet | None,
    child_path: KeyPath | None = None,
) -> KeyPathSet | NodeFilter

Merges uncollapse paths.

Source code in pygx/views/html/_tree_view.py
@staticmethod
def merge_uncollapse(
    uncollapse: KeyPathSet | base.NodeFilter | None,
    child_uncollapse: KeyPathSet | None,
    child_path: KeyPath | None = None,
) -> KeyPathSet | base.NodeFilter:
    """Merges uncollapse paths."""
    if not uncollapse and not child_uncollapse:
        return KeyPathSet()

    if callable(uncollapse) or not child_uncollapse:
        assert uncollapse is not None
        return uncollapse

    assert isinstance(uncollapse, KeyPathSet), uncollapse
    assert isinstance(child_uncollapse, KeyPathSet), child_uncollapse
    if child_path:
        child_uncollapse = child_uncollapse.copy()
        child_uncollapse.rebase(child_path)
    uncollapse.update(child_uncollapse)
    return uncollapse

to_html

to_html(
    value: Any,
    *,
    name: str | None = None,
    root_path: KeyPath | None = None,
    view_id: str = "html-tree-view",
    **kwargs: Any
) -> Html

Returns the HTML representation of a value.

Example:

class A(pg.Object):
  x: int = 1

html = pg.to_html(A(x=2), enable_summary_tooltip=False)
assert '<html>' in html.to_str()

Parameters:

Name Type Description Default
value Any

The value to render.

required
name str | None

(Optional) The referred name of the value from its container.

None
root_path KeyPath | None

(Optional) The path of value under its object tree.

None
view_id str

The ID of the view to render the value. See pg.views.HtmlView.dir() for all available HTML view IDs.

'html-tree-view'
**kwargs Any

Additional keyword arguments passed to the view, which will be passed to the HtmlView.render_xxx() (thus Extension._html_xxx()) methods.

{}

Returns:

Type Description
Html

The rendered Html object.

Source code in pygx/views/html/_base.py
def to_html(
    value: Any,
    *,
    name: str | None = None,
    root_path: topology.KeyPath | None = None,
    view_id: str = 'html-tree-view',
    **kwargs: Any,
) -> Html:
    """Returns the HTML representation of a value.

    **Example:**

    ```python
    class A(pg.Object):
      x: int = 1

    html = pg.to_html(A(x=2), enable_summary_tooltip=False)
    assert '<html>' in html.to_str()
    ```

    Args:
      value: The value to render.
      name: (Optional) The referred name of the value from its container.
      root_path: (Optional) The path of `value` under its object tree.
      view_id: The ID of the view to render the value.
        See `pg.views.HtmlView.dir()` for all available HTML view IDs.
      **kwargs: Additional keyword arguments passed to the view, which
          will be passed to the `HtmlView.render_xxx()` (thus
          `Extension._html_xxx()`) methods.

    Returns:
      The rendered `Html` object.
    """
    content = base.view(
        value, name=name, root_path=root_path, view_id=view_id, **kwargs
    )
    assert isinstance(content, Html), content
    return content

to_html_str

to_html_str(
    value: Any,
    *,
    name: str | None = None,
    root_path: KeyPath | None = None,
    view_id: str = "html-tree-view",
    content_only: bool = False,
    **kwargs: Any
) -> str

Returns an HTML str for a value.

Same as pg.to_html, but returns the final HTML string. When content_only is True, the <html>/<head>/<body> scaffolding is omitted and only the body content is returned.

Parameters:

Name Type Description Default
value Any

The value to render.

required
name str | None

(Optional) The referred name of the value from its container.

None
root_path KeyPath | None

(Optional) The path of value under its object tree.

None
view_id str

The ID of the view to render the value. See pg.views.HtmlView.dir() for all available HTML view IDs.

'html-tree-view'
content_only bool

If True, only the body content will be returned (without the <html>/<head>/<body> scaffolding and shared styles/scripts).

False
**kwargs Any

Additional keyword arguments passed to the view, which will be passed to the HtmlView.render_xxx() (thus Extension._html_xxx()) methods.

{}

Returns:

Type Description
str

The rendered HTML str.

Source code in pygx/views/html/_base.py
def to_html_str(
    value: Any,
    *,
    name: str | None = None,
    root_path: topology.KeyPath | None = None,
    view_id: str = 'html-tree-view',
    content_only: bool = False,
    **kwargs: Any,
) -> str:
    """Returns an HTML str for a value.

    Same as `pg.to_html`, but returns the final HTML string. When
    `content_only` is True, the `<html>`/`<head>`/`<body>` scaffolding is
    omitted and only the body content is returned.

    Args:
      value: The value to render.
      name: (Optional) The referred name of the value from its container.
      root_path: (Optional) The path of `value` under its object tree.
      view_id: The ID of the view to render the value.
        See `pg.views.HtmlView.dir()` for all available HTML view IDs.
      content_only: If True, only the body content will be returned (without
        the `<html>`/`<head>`/`<body>` scaffolding and shared styles/scripts).
      **kwargs: Additional keyword arguments passed to the view, which
          will be passed to the `HtmlView.render_xxx()` (thus
          `Extension._html_xxx()`) methods.

    Returns:
      The rendered HTML str.
    """
    return to_html(
        value, name=name, root_path=root_path, view_id=view_id, **kwargs
    ).to_str(content_only=content_only)

options: show_root_heading: false show_root_toc_entry: false members_order: source heading_level: 2