Skip to content

Commit 03f935a

Browse files
make it a proper function
1 parent ca7ec17 commit 03f935a

File tree

1 file changed

+21
-42
lines changed

1 file changed

+21
-42
lines changed

pytools/debug.py

Lines changed: 21 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import sys
22

33
from pytools import memoize
4+
from typing import List, Set, Optional, Collection
45

56

67
# {{{ debug files -------------------------------------------------------------
@@ -143,54 +144,26 @@ def is_excluded(o):
143144

144145
# https://code.activestate.com/recipes/523004-find-cyclical-references/
145146

146-
import gc
147-
from types import FrameType
148-
149-
150-
def print_cycles(objects, outstream=sys.stdout, show_progress=True):
147+
def get_object_cycles(objects: Collection[object]) -> List[List[object]]:
151148
"""
152-
objects: A list of objects to find cycles in. It is often useful
153-
to pass in gc.garbage to find the cycles that are
154-
preventing some objects from being garbage collected.
155-
outstream: The stream for output.
156-
show_progress: If True, print the number of objects reached as they are
157-
found.
149+
:param objects: A collection of objects to find cycles in. It is often
150+
useful to pass in gc.garbage to find the cycles that are preventing some
151+
objects from being garbage collected.
158152
"""
159-
def print_path(path):
160-
for i, step in enumerate(path):
161-
# next_obj "wraps around"
162-
next_obj = path[(i + 1) % len(path)]
163-
164-
outstream.write(" %s -- " % str(type(step)))
165-
if isinstance(step, dict):
166-
for key, val in step.items():
167-
if val is next_obj:
168-
outstream.write("[%s]" % repr(key))
169-
break
170-
if key is next_obj:
171-
outstream.write("[key] = %s" % repr(val))
172-
break
173-
elif isinstance(step, list):
174-
outstream.write("[%d]" % step.index(next_obj))
175-
elif isinstance(step, tuple):
176-
outstream.write("[%d]" % step.index(next_obj))
177-
else:
178-
outstream.write(repr(step))
179-
outstream.write(" ->\n")
180-
outstream.write("\n")
181-
182-
def recurse(obj, start, all_objs, current_path):
183-
if show_progress:
184-
outstream.write("%d\r" % len(all_objs))
185-
153+
def recurse(obj: object, start: object, all_objs: Set[object],
154+
current_path: List[object]) -> Optional[List[object]]:
186155
all_objs.add(id(obj))
187156

157+
import gc
158+
from types import FrameType
159+
188160
referents = gc.get_referents(obj)
161+
189162
for referent in referents:
190163
# If we've found our way back to the start, this is
191164
# a cycle, so print it out
192165
if referent is start:
193-
print_path(current_path)
166+
return current_path
194167

195168
# Don't go back through the original list of objects, or
196169
# through temporary references to the object, since those
@@ -200,11 +173,17 @@ def recurse(obj, start, all_objs, current_path):
200173

201174
# We haven't seen this object before, so recurse
202175
elif id(referent) not in all_objs:
203-
recurse(referent, start, all_objs, current_path + [obj])
176+
return recurse(referent, start, all_objs, current_path + [obj])
204177

178+
return None
179+
180+
res = []
205181
for obj in objects:
206-
outstream.write(f"Examining: {obj}\n")
207-
recurse(obj, obj, set(), [])
182+
r = recurse(obj, obj, set(), [])
183+
if r:
184+
res.append(r)
185+
186+
return res
208187

209188
# }}}
210189

0 commit comments

Comments
 (0)