|
31 | 31 | # The logger is created here but initialized in the debug support module
|
32 | 32 | logger = logging.getLogger("sentry_sdk.errors")
|
33 | 33 |
|
| 34 | +CYCLE_MARKER = object() |
| 35 | + |
34 | 36 |
|
35 | 37 | def _get_debug_hub():
|
36 | 38 | # This function is replaced by debug.py
|
@@ -312,9 +314,13 @@ def _walk(obj, depth):
|
312 | 314 | return [_walk(x, depth + 1) for x in obj]
|
313 | 315 | if isinstance(obj, Mapping):
|
314 | 316 | return {safe_str(k): _walk(v, depth + 1) for k, v in obj.items()}
|
| 317 | + |
| 318 | + if obj is CYCLE_MARKER: |
| 319 | + return obj |
| 320 | + |
315 | 321 | return safe_repr(obj)
|
316 | 322 |
|
317 |
| - return _walk(obj, 0) |
| 323 | + return _walk(break_cycles(obj), 0) |
318 | 324 |
|
319 | 325 |
|
320 | 326 | def extract_locals(frame):
|
@@ -615,7 +621,26 @@ def strip_frame_mut(frame):
|
615 | 621 | frame["vars"] = strip_databag(frame["vars"])
|
616 | 622 |
|
617 | 623 |
|
| 624 | +def break_cycles(obj, memo=None): |
| 625 | + if memo is None: |
| 626 | + memo = {} |
| 627 | + if id(obj) in memo: |
| 628 | + return CYCLE_MARKER |
| 629 | + memo[id(obj)] = obj |
| 630 | + |
| 631 | + try: |
| 632 | + if isinstance(obj, Mapping): |
| 633 | + return {k: break_cycles(v, memo) for k, v in obj.items()} |
| 634 | + if isinstance(obj, Sequence) and not isinstance(obj, (text_type, bytes)): |
| 635 | + return [break_cycles(v, memo) for v in obj] |
| 636 | + return obj |
| 637 | + finally: |
| 638 | + del memo[id(obj)] |
| 639 | + |
| 640 | + |
618 | 641 | def convert_types(obj):
|
| 642 | + if obj is CYCLE_MARKER: |
| 643 | + return u"<cyclic>" |
619 | 644 | if isinstance(obj, datetime):
|
620 | 645 | return obj.strftime("%Y-%m-%dT%H:%M:%SZ")
|
621 | 646 | if isinstance(obj, Mapping):
|
|
0 commit comments