11import sys
22
33from 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