Source code for est.core.utils.pretty_print

import sys
from collections.abc import Mapping
from collections.abc import Sequence
from typing import Optional

import numpy
from pydantic import BaseModel


[docs] def tree(obj, title=None) -> str: """ Pretty-print a BaseModel or dictionary as a tree. - Nested dicts and BaseModels are recursed. - NumPy arrays are summarized (shape, min, max). - ASCII fallback if stdout does not support UTF-8. """ use_unicode = _supports_unicode() return _tree_recursive(obj, prefix="", use_unicode=use_unicode, title=title)
def _tree_recursive(obj, prefix="", use_unicode=True, title=None) -> str: pipe = "│ " if use_unicode else "| " tee = "├── " if use_unicode else "|-- " corner = "└── " if use_unicode else "`-- " lines = [] # Root node if prefix == "": if isinstance(obj, BaseModel): lines.append(title or type(obj).__name__) elif isinstance(obj, Mapping): lines.append(title or "root") else: lines.append(title or repr(obj)) return "\n".join(lines) # Determine keys/items if isinstance(obj, BaseModel): items = [(name, getattr(obj, name)) for name in type(obj).model_fields.keys()] elif isinstance(obj, Mapping): items = list(obj.items()) else: # Not a container: just print summary lines.append(prefix + corner + repr(obj)) return "\n".join(lines) for i, (key, value) in enumerate(items): last = i == len(items) - 1 connector = corner if last else tee line_prefix = prefix + connector summary = _summarize(value) if summary is not None: lines.append(f"{line_prefix}{key}: {summary}") elif isinstance(value, (Mapping, BaseModel)): lines.append(f"{line_prefix}{key}") extension = " " if last else pipe lines.append( _tree_recursive(value, prefix + extension, use_unicode=use_unicode) ) else: lines.append(f"{line_prefix}{key}: {repr(value)}") return "\n".join(lines) def _summarize(value) -> Optional[str]: """Return a string summary for non-container types.""" if isinstance(value, numpy.ndarray): if value.size > 0: return f"shape={value.shape} min={value.min():.3g} max={value.max():.3g}" else: return f"shape={value.shape} EMPTY" elif value is None: return "<MISSING>" elif isinstance(value, Sequence) and not isinstance(value, str): return f"len={len(value)}" return None def _supports_unicode() -> bool: encoding = sys.stdout.encoding or "" return encoding.lower().startswith("utf")