Method: rich.pretty.traverse
Calls: 162, Exceptions: 0, Paths: 1Back
Path 1: 162 calls (1.0)
1 (45) Layout (18) dict (16) 0 (14) Console (7) test_broken_call_attr.
None (155) 10 (5) 2 (2)
None (156) 60 (4) 8 (1) 80 (1)
None (151) 2 (4) 1 (3) 0 (1) 3 (1) 4 (1) 5 (1)
Node (162)
1def traverse(
2 _object: Any,
3 max_length: Optional[int] = None,
4 max_string: Optional[int] = None,
5 max_depth: Optional[int] = None,
6) -> Node:
7 """Traverse object and generate a tree.
8
9 Args:
10 _object (Any): Object to be traversed.
11 max_length (int, optional): Maximum length of containers before abbreviating, or None for no abbreviation.
12 Defaults to None.
13 max_string (int, optional): Maximum length of string before truncating, or None to disable truncating.
14 Defaults to None.
15 max_depth (int, optional): Maximum depth of data structures, or None for no maximum.
16 Defaults to None.
17
18 Returns:
19 Node: The root of a tree structure which can be used to render a pretty repr.
20 """
21
22 def to_repr(obj: Any) -> str:
23 """Get repr string for an object, but catch errors."""
24 if (
25 max_string is not None
26 and _safe_isinstance(obj, (bytes, str))
27 and len(obj) > max_string
28 ):
29 truncated = len(obj) - max_string
30 obj_repr = f"{obj[:max_string]!r}+{truncated}"
31 else:
32 try:
33 obj_repr = repr(obj)
34 except Exception as error:
35 obj_repr = f"<repr-error {str(error)!r}>"
36 return obj_repr
37
38 visited_ids: Set[int] = set()
39 push_visited = visited_ids.add
40 pop_visited = visited_ids.remove
41
42 def _traverse(obj: Any, root: bool = False, depth: int = 0) -> Node:
43 """Walk the object depth first."""
44
45 obj_id = id(obj)
46 if obj_id in visited_ids:
47 # Recursion detected
48 return Node(value_repr="...")
49
50 obj_type = type(obj)
51 py_version = (sys.version_info.major, sys.version_info.minor)
52 children: List[Node]
53 reached_max_depth = max_depth is not None and depth >= max_depth
54
55 def iter_rich_args(rich_args: Any) -> Iterable[Union[Any, Tuple[str, Any]]]:
56 for arg in rich_args:
57 if _safe_isinstance(arg, tuple):
58 if len(arg) == 3:
59 key, child, default = arg
60 if default == child:
61 continue
62 yield key, child
63 elif len(arg) == 2:
64 key, child = arg
65 yield key, child
66 elif len(arg) == 1:
67 yield arg[0]
68 else:
69 yield arg
70
71 try:
72 fake_attributes = hasattr(
73 obj, "awehoi234_wdfjwljet234_234wdfoijsdfmmnxpi492"
74 )
75 except Exception:
76 fake_attributes = False
77
78 rich_repr_result: Optional[RichReprResult] = None
79 if not fake_attributes:
80 try:
81 if hasattr(obj, "__rich_repr__") and not isclass(obj):
82 rich_repr_result = obj.__rich_repr__()
83 except Exception:
84 pass
85
86 if rich_repr_result is not None:
87 push_visited(obj_id)
88 angular = getattr(obj.__rich_repr__, "angular", False)
89 args = list(iter_rich_args(rich_repr_result))
90 class_name = obj.__class__.__name__
91
92 if args:
93 children = []
94 append = children.append
95
96 if reached_max_depth:
97 if angular:
98 node = Node(value_repr=f"<{class_name}...>")
99 else:
100 node = Node(value_repr=f"{class_name}(...)")
101 else:
102 if angular:
103 node = Node(
104 open_brace=f"<{class_name} ",
105 close_brace=">",
106 children=children,
107 last=root,
108 separator=" ",
109 )
110 else:
111 node = Node(
112 open_brace=f"{class_name}(",
113 close_brace=")",
114 children=children,
115 last=root,
116 )
117 for last, arg in loop_last(args):
118 if _safe_isinstance(arg, tuple):
119 key, child = arg
120 child_node = _traverse(child, depth=depth + 1)
121 child_node.last = last
122 child_node.key_repr = key
123 child_node.key_separator = "="
124 append(child_node)
125 else:
126 child_node = _traverse(arg, depth=depth + 1)
127 child_node.last = last
128 append(child_node)
129 else:
130 node = Node(
131 value_repr=f"<{class_name}>" if angular else f"{class_name}()",
132 children=[],
133 last=root,
134 )
135 pop_visited(obj_id)
136 elif _is_attr_object(obj) and not fake_attributes:
137 push_visited(obj_id)
138 children = []
139 append = children.append
140
141 attr_fields = _get_attr_fields(obj)
142 if attr_fields:
143 if reached_max_depth:
144 node = Node(value_repr=f"{obj.__class__.__name__}(...)")
145 else:
146 node = Node(
147 open_brace=f"{obj.__class__.__name__}(",
148 close_brace=")",
149 children=children,
150 last=root,
151 )
152
153 def iter_attrs() -> Iterable[
154 Tuple[str, Any, Optional[Callable[[Any], str]]]
155 ]:
156 """Iterate over attr fields and values."""
157 for attr in attr_fields:
158 if attr.repr:
159 try:
160 value = getattr(obj, attr.name)
161 except Exception as error:
162 # Can happen, albeit rarely
163 yield (attr.name, error, None)
164 else:
165 yield (
166 attr.name,
167 value,
168 attr.repr if callable(attr.repr) else None,
169 )
170
171 for last, (name, value, repr_callable) in loop_last(iter_attrs()):
172 if repr_callable:
173 child_node = Node(value_repr=str(repr_callable(value)))
174 else:
175 child_node = _traverse(value, depth=depth + 1)
176 child_node.last = last
177 child_node.key_repr = name
178 child_node.key_separator = "="
179 append(child_node)
180 else:
181 node = Node(
182 value_repr=f"{obj.__class__.__name__}()", children=[], last=root
183 )
184 pop_visited(obj_id)
185 elif (
186 is_dataclass(obj)
187 and not _safe_isinstance(obj, type)
188 and not fake_attributes
189 and (_is_dataclass_repr(obj) or py_version == (3, 6))
190 ):
191 push_visited(obj_id)
192 children = []
193 append = children.append
194 if reached_max_depth:
195 node = Node(value_repr=f"{obj.__class__.__name__}(...)")
196 else:
197 node = Node(
198 open_brace=f"{obj.__class__.__name__}(",
199 close_brace=")",
200 children=children,
201 last=root,
202 )
203
204 for last, field in loop_last(
205 field for field in fields(obj) if field.repr
206 ):
207 child_node = _traverse(getattr(obj, field.name), depth=depth + 1)
208 child_node.key_repr = field.name
209 child_node.last = last
210 child_node.key_separator = "="
211 append(child_node)
212
213 pop_visited(obj_id)
214 elif _is_namedtuple(obj) and _has_default_namedtuple_repr(obj):
215 push_visited(obj_id)
216 class_name = obj.__class__.__name__
217 if reached_max_depth:
218 # If we've reached the max depth, we still show the class name, but not its contents
219 node = Node(
220 value_repr=f"{class_name}(...)",
221 )
222 else:
223 children = []
224 append = children.append
225 node = Node(
226 open_brace=f"{class_name}(",
227 close_brace=")",
228 children=children,
229 empty=f"{class_name}()",
230 )
231 for last, (key, value) in loop_last(obj._asdict().items()):
232 child_node = _traverse(value, depth=depth + 1)
233 child_node.key_repr = key
234 child_node.last = last
235 child_node.key_separator = "="
236 append(child_node)
237 pop_visited(obj_id)
238 elif _safe_isinstance(obj, _CONTAINERS):
239 for container_type in _CONTAINERS:
240 if _safe_isinstance(obj, container_type):
241 obj_type = container_type
242 break
243
244 push_visited(obj_id)
245
246 open_brace, close_brace, empty = _BRACES[obj_type](obj)
247
248 if reached_max_depth:
249 node = Node(value_repr=f"{open_brace}...{close_brace}")
250 elif obj_type.__repr__ != type(obj).__repr__:
251 node = Node(value_repr=to_repr(obj), last=root)
252 elif obj:
253 children = []
254 node = Node(
255 open_brace=open_brace,
256 close_brace=close_brace,
257 children=children,
258 last=root,
259 )
260 append = children.append
261 num_items = len(obj)
262 last_item_index = num_items - 1
263
264 if _safe_isinstance(obj, _MAPPING_CONTAINERS):
265 iter_items = iter(obj.items())
266 if max_length is not None:
267 iter_items = islice(iter_items, max_length)
268 for index, (key, child) in enumerate(iter_items):
269 child_node = _traverse(child, depth=depth + 1)
270 child_node.key_repr = to_repr(key)
271 child_node.last = index == last_item_index
272 append(child_node)
273 else:
274 iter_values = iter(obj)
275 if max_length is not None:
276 iter_values = islice(iter_values, max_length)
277 for index, child in enumerate(iter_values):
278 child_node = _traverse(child, depth=depth + 1)
279 child_node.last = index == last_item_index
280 append(child_node)
281 if max_length is not None and num_items > max_length:
282 append(Node(value_repr=f"... +{num_items - max_length}", last=True))
283 else:
284 node = Node(empty=empty, children=[], last=root)
285
286 pop_visited(obj_id)
287 else:
288 node = Node(value_repr=to_repr(obj), last=root)
289 node.is_tuple = _safe_isinstance(obj, tuple)
290 node.is_namedtuple = _is_namedtuple(obj)
291 return node
292
293 node = _traverse(_object, root=True)
294 return node