from . import get_console from ._loop import loop_last from ._pick import pick_bool from .abc import RichRenderable from .cells import cell_len from .highlighter import ReprHighlighter from .jupyter import JupyterMixin, JupyterRenderable from .measure import Measurement from .text import Text
if TYPE_CHECKING: from .console import (
Console,
ConsoleOptions,
HighlighterType,
JustifyMethod,
OverflowMethod,
RenderResult,
)
def _is_attr_object(obj: Any) -> bool: """Check if an object was created with attrs module.""" return _has_attrs and _attr_module.has(type(obj))
def _get_attr_fields(obj: Any) -> Sequence["_attr_module.Attribute[Any]"]: """Get fields for an attrs object.""" return _attr_module.fields(type(obj)) if _has_attrs else []
def _is_dataclass_repr(obj: object) -> bool: """Check if an instance of a dataclass contains the default repr.
Args:
obj (object): A dataclass instance.
Returns:
bool: Trueif the default repr is used, Falseif there is a custom repr. """ # Digging in to a lot of internals here # Catching all exceptions in case something is missing on a non CPython implementation try: return obj.__repr__.__code__.co_filename in (
dataclasses.__file__,
reprlib.__file__,
) except Exception: # pragma: no coverage returnFalse
def _has_default_namedtuple_repr(obj: object) -> bool: """Check if an instance of namedtuple contains the default repr
Args:
obj (object): A namedtuple
Returns:
bool: Trueif the default repr is used, Falseif there's a custom repr. """
obj_file = None try:
obj_file = inspect.getfile(obj.__repr__) except (OSError, TypeError): # OSError handles case where object is defined in __main__ scope, e.g. REPL - no filename available. # TypeError trapped defensively, in case of object without filename slips through. pass
default_repr_file = inspect.getfile(_dummy_namedtuple.__repr__) return obj_file == default_repr_file
# always skip rich generated jupyter renderables or None values if _safe_isinstance(value, JupyterRenderable) or value isNone: returnNone
console = console or get_console()
with console.capture() as capture: # certain renderables should start on a new line if _safe_isinstance(value, ConsoleRenderable):
console.line()
console.print(
(
value if _safe_isinstance(value, RichRenderable) else Pretty(
value,
overflow=overflow,
indent_guides=indent_guides,
max_length=max_length,
max_string=max_string,
max_depth=max_depth,
expand_all=expand_all,
margin=12,
)
),
crop=crop,
new_line_start=True,
end="",
) # strip trailing newline, not usually part of a text repr # I'm not sure if this should be prevented at a lower level return capture.get().rstrip("\n")
def _safe_isinstance(
obj: object, class_or_tuple: Union[type, Tuple[type, ...]]
) -> bool: """isinstance can fail in rare cases, for example types with no __class__""" try: return isinstance(obj, class_or_tuple) except Exception: returnFalse
Args:
console (Console, optional): Console instance or ``None`` to use global console. Defaults to None.
overflow (Optional[OverflowMethod], optional): Overflow method. Defaults to "ignore".
crop (Optional[bool], optional): Enable cropping of long lines. Defaults to False.
indent_guides (bool, optional): Enable indentation guides. Defaults to False.
max_length (int, optional): Maximum length of containers before abbreviating, orNonefor no abbreviation.
Defaults to None.
max_string (int, optional): Maximum length of string before truncating, orNone to disable. Defaults to None.
max_depth (int, optional): Maximum depth of nested data structures, orNonefor no maximum. Defaults to None.
expand_all (bool, optional): Expand all containers. Defaults to False.
max_frames (int): Maximum number of frames to show in a traceback, 0 for no maximum. Defaults to 100. """ from rich import get_console
console = console or get_console() assert console isnotNone
def display_hook(value: Any) -> None: """Replacement sys.displayhook which prettifies objects with Rich.""" if value isnotNone: assert console isnotNone
builtins._ = None# type: ignore[attr-defined]
console.print(
(
value if _safe_isinstance(value, RichRenderable) else Pretty(
value,
overflow=overflow,
indent_guides=indent_guides,
max_length=max_length,
max_string=max_string,
max_depth=max_depth,
expand_all=expand_all,
)
),
crop=crop,
)
builtins._ = value # type: ignore[attr-defined]
try:
ip = get_ipython() # type: ignore[name-defined] except NameError:
sys.displayhook = display_hook else: from IPython.core.formatters import BaseFormatter
class RichFormatter(BaseFormatter): # type: ignore[misc]
pprint: bool = True
# replace plain text formatter with rich formatter
rich_formatter = RichFormatter()
ip.display_formatter.formatters["text/plain"] = rich_formatter
class Pretty(JupyterMixin): """A rich renderable that pretty prints an object.
Args:
_object (Any): An object to pretty print.
highlighter (HighlighterType, optional): Highlighter object to apply to result, orNoneforReprHighlighter. Defaults to None.
indent_size (int, optional): Number of spaces in indent. Defaults to 4.
justify (JustifyMethod, optional): Justify method, orNonefor default. Defaults to None.
overflow (OverflowMethod, optional): Overflow method, orNonefor default. Defaults to None.
no_wrap (Optional[bool], optional): Disable word wrapping. Defaults to False.
indent_guides (bool, optional): Enable indentation guides. Defaults to False.
max_length (int, optional): Maximum length of containers before abbreviating, orNonefor no abbreviation.
Defaults to None.
max_string (int, optional): Maximum length of string before truncating, orNone to disable. Defaults to None.
max_depth (int, optional): Maximum depth of nested data structures, orNonefor no maximum. Defaults to None.
expand_all (bool, optional): Expand all containers. Defaults to False.
margin (int, optional): Subtrace a margin from width to force containers to expand earlier. Defaults to 0.
insert_line (bool, optional): Insert a new line if the output has multiple new lines. Defaults to False. """
def is_expandable(obj: Any) -> bool: """Check if an object may be expanded by pretty print.""" return (
_safe_isinstance(obj, _CONTAINERS) or (is_dataclass(obj)) or (hasattr(obj, "__rich_repr__")) or _is_attr_object(obj)
) andnot isclass(obj)
@dataclass class Node: """A node in a repr tree. May be atomic or a container."""
def iter_tokens(self) -> Iterable[str]: """Generate tokens for this node.""" if self.key_repr: yield self.key_repr yield self.key_separator if self.value_repr: yield self.value_repr elif self.children isnotNone: if self.children: yield self.open_brace if self.is_tuple andnot self.is_namedtuple and len(self.children) == 1: yieldfrom self.children[0].iter_tokens() yield"," else: for child in self.children: yieldfrom child.iter_tokens() ifnot child.last: yield self.separator yield self.close_brace else: yield self.empty
def check_length(self, start_length: int, max_length: int) -> bool: """Check the length fits within a limit.
Args:
start_length (int): Starting length of the line (indent, prefix, suffix).
max_length (int): Maximum length.
Returns:
bool: Trueif the node can be rendered within max length, otherwise False. """
total_length = start_length for token in self.iter_tokens():
total_length += cell_len(token) if total_length > max_length: returnFalse returnTrue
def render(
self, max_width: int = 80, indent_size: int = 4, expand_all: bool = False
) -> str: """Render the node to a pretty repr.
Args:
max_width (int, optional): Maximum width of the repr. Defaults to 80.
indent_size (int, optional): Size of indents. Defaults to 4.
expand_all (bool, optional): Expand all levels. Defaults to False.
Returns:
str: A repr string of the original object. """
lines = [_Line(node=self, is_root=True)]
line_no = 0 while line_no < len(lines):
line = lines[line_no] if line.expandable andnot line.expanded: if expand_all ornot line.check_length(max_width):
lines[line_no : line_no + 1] = line.expand(indent_size)
line_no += 1
repr_str = "\n".join(str(line) for line in lines) return repr_str
@dataclass class _Line: """A line in repr output."""
@property def expandable(self) -> bool: """Check if the line may be expanded.""" return bool(self.node isnotNoneand self.node.children)
def check_length(self, max_length: int) -> bool: """Check this line fits within a given number of cells."""
start_length = (
len(self.whitespace) + cell_len(self.text) + cell_len(self.suffix)
) assert self.node isnotNone return self.node.check_length(start_length, max_length)
def expand(self, indent_size: int) -> Iterable["_Line"]: """Expand this line by adding children on their own line."""
node = self.node assert node isnotNone
whitespace = self.whitespace assert node.children if node.key_repr:
new_line = yield _Line(
text=f"{node.key_repr}{node.key_separator}{node.open_brace}",
whitespace=whitespace,
) else:
new_line = yield _Line(text=node.open_brace, whitespace=whitespace)
child_whitespace = self.whitespace + " " * indent_size
tuple_of_one = node.is_tuple and len(node.children) == 1 for last, child in loop_last(node.children):
separator = ","if tuple_of_one else node.separator
line = _Line(
parent=new_line,
node=child,
whitespace=child_whitespace,
suffix=separator,
last=last andnot tuple_of_one,
) yield line
def __str__(self) -> str: if self.last: return f"{self.whitespace}{self.text}{self.node or ''}" else: return (
f"{self.whitespace}{self.text}{self.node or ''}{self.suffix.rstrip()}"
)
def _is_namedtuple(obj: Any) -> bool: """Checks if an object is most likely a namedtuple. It is possible
to craft an object that passes this check and isn't a namedtuple, but
there is only a minuscule chance of this happening unintentionally.
Args:
obj (Any): The object to test
Returns:
bool: Trueif the object is a namedtuple. False otherwise. """ try:
fields = getattr(obj, "_fields", None) except Exception: # Being very defensive - if we cannot get the attr then its not a namedtuple returnFalse return isinstance(obj, tuple) and isinstance(fields, tuple)
Args:
_object (Any): Object to be traversed.
max_length (int, optional): Maximum length of containers before abbreviating, orNonefor no abbreviation.
Defaults to None.
max_string (int, optional): Maximum length of string before truncating, orNone to disable truncating.
Defaults to None.
max_depth (int, optional): Maximum depth of data structures, orNonefor no maximum.
Defaults to None.
Returns:
Node: The root of a tree structure which can be used to render a pretty repr. """
def to_repr(obj: Any) -> str: """Get repr string for an object, but catch errors.""" if (
max_string isnotNone and _safe_isinstance(obj, (bytes, str)) and len(obj) > max_string
):
truncated = len(obj) - max_string
obj_repr = f"{obj[:max_string]!r}+{truncated}" else: try:
obj_repr = repr(obj) except Exception as error:
obj_repr = f"" return obj_repr
if reached_max_depth: if angular:
node = Node(value_repr=f"<{class_name}...>") else:
node = Node(value_repr=f"{class_name}(...)") else: if angular:
node = Node(
open_brace=f"<{class_name} ",
close_brace=">",
children=children,
last=root,
separator=" ",
) else:
node = Node(
open_brace=f"{class_name}(",
close_brace=")",
children=children,
last=root,
) for last, arg in loop_last(args): if _safe_isinstance(arg, tuple):
key, child = arg
child_node = _traverse(child, depth=depth + 1)
child_node.last = last
child_node.key_repr = key
child_node.key_separator = "="
append(child_node) else:
child_node = _traverse(arg, depth=depth + 1)
child_node.last = last
append(child_node) else:
node = Node(
value_repr=f"<{class_name}>"if angular else f"{class_name}()",
children=[],
last=root,
)
pop_visited(obj_id) elif _is_attr_object(obj) andnot fake_attributes:
push_visited(obj_id)
children = []
append = children.append
attr_fields = _get_attr_fields(obj) if attr_fields: if reached_max_depth:
node = Node(value_repr=f"{obj.__class__.__name__}(...)") else:
node = Node(
open_brace=f"{obj.__class__.__name__}(",
close_brace=")",
children=children,
last=root,
)
def iter_attrs() -> (
Iterable[Tuple[str, Any, Optional[Callable[[Any], str]]]]
): """Iterate over attr fields and values.""" for attr in attr_fields: if attr.repr: try:
value = getattr(obj, attr.name) except Exception as error: # Can happen, albeit rarely yield (attr.name, error, None) else: yield (
attr.name,
value,
attr.repr if callable(attr.repr) elseNone,
)
for last, (name, value, repr_callable) in loop_last(iter_attrs()): if repr_callable:
child_node = Node(value_repr=str(repr_callable(value))) else:
child_node = _traverse(value, depth=depth + 1)
child_node.last = last
child_node.key_repr = name
child_node.key_separator = "="
append(child_node) else:
node = Node(
value_repr=f"{obj.__class__.__name__}()", children=[], last=root
)
pop_visited(obj_id) elif (
is_dataclass(obj) andnot _safe_isinstance(obj, type) andnot fake_attributes and _is_dataclass_repr(obj)
):
push_visited(obj_id)
children = []
append = children.append if reached_max_depth:
node = Node(value_repr=f"{obj.__class__.__name__}(...)") else:
node = Node(
open_brace=f"{obj.__class__.__name__}(",
close_brace=")",
children=children,
last=root,
empty=f"{obj.__class__.__name__}()",
)
for last, field in loop_last(
field for field in fields(obj) if field.repr and hasattr(obj, field.name)
):
child_node = _traverse(getattr(obj, field.name), depth=depth + 1)
child_node.key_repr = field.name
child_node.last = last
child_node.key_separator = "="
append(child_node)
pop_visited(obj_id) elif _is_namedtuple(obj) and _has_default_namedtuple_repr(obj):
push_visited(obj_id)
class_name = obj.__class__.__name__ if reached_max_depth: # If we've reached the max depth, we still show the class name, but not its contents
node = Node(
value_repr=f"{class_name}(...)",
) else:
children = []
append = children.append
node = Node(
open_brace=f"{class_name}(",
close_brace=")",
children=children,
empty=f"{class_name}()",
) for last, (key, value) in loop_last(obj._asdict().items()):
child_node = _traverse(value, depth=depth + 1)
child_node.key_repr = key
child_node.last = last
child_node.key_separator = "="
append(child_node)
pop_visited(obj_id) elif _safe_isinstance(obj, _CONTAINERS): for container_type in _CONTAINERS: if _safe_isinstance(obj, container_type):
obj_type = container_type break
def pretty_repr(
_object: Any,
*,
max_width: int = 80,
indent_size: int = 4,
max_length: Optional[int] = None,
max_string: Optional[int] = None,
max_depth: Optional[int] = None,
expand_all: bool = False,
) -> str: """Prettify repr string by expanding on to new lines to fit within a given width.
Args:
_object (Any): Object to repr.
max_width (int, optional): Desired maximum width of repr string. Defaults to 80.
indent_size (int, optional): Number of spaces to indent. Defaults to 4.
max_length (int, optional): Maximum length of containers before abbreviating, orNonefor no abbreviation.
Defaults to None.
max_string (int, optional): Maximum length of string before truncating, orNone to disable truncating.
Defaults to None.
max_depth (int, optional): Maximum depth of nested data structure, orNonefor no depth.
Defaults to None.
expand_all (bool, optional): Expand all containers regardless of available width. Defaults to False.
Returns:
str: A possibly multi-line representation of the object. """
Args:
_object (Any): Object to pretty print.
console (Console, optional): Console instance, orNone to use default. Defaults to None.
max_length (int, optional): Maximum length of containers before abbreviating, orNonefor no abbreviation.
Defaults to None.
max_string (int, optional): Maximum length of strings before truncating, orNone to disable. Defaults to None.
max_depth (int, optional): Maximum depth for nested data structures, orNonefor unlimited depth. Defaults to None.
indent_guides (bool, optional): Enable indentation guides. Defaults to True.
expand_all (bool, optional): Expand all containers. Defaults to False. """
_console = get_console() if console isNoneelse console
_console.print(
Pretty(
_object,
max_length=max_length,
max_string=max_string,
max_depth=max_depth,
indent_guides=indent_guides,
expand_all=expand_all,
overflow="ignore",
),
soft_wrap=True,
)
if __name__ == "__main__": # pragma: no cover
class BrokenRepr: def __repr__(self) -> str:
1 / 0 return"this will fail"
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.