@@ -139,6 +139,76 @@ def is_excluded(o):
139139# }}}
140140
141141
142+ # {{{ Find circular references
143+
144+ # https://code.activestate.com/recipes/523004-find-cyclical-references/
145+
146+ import gc
147+ from types import FrameType
148+
149+
150+ def print_cycles (objects , outstream = sys .stdout , show_progress = True ):
151+ """
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.
158+ """
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+
186+ all_objs .add (id (obj ))
187+
188+ referents = gc .get_referents (obj )
189+ for referent in referents :
190+ # If we've found our way back to the start, this is
191+ # a cycle, so print it out
192+ if referent is start :
193+ print_path (current_path )
194+
195+ # Don't go back through the original list of objects, or
196+ # through temporary references to the object, since those
197+ # are just an artifact of the cycle detector itself.
198+ elif referent is objects or isinstance (referent , FrameType ):
199+ continue
200+
201+ # We haven't seen this object before, so recurse
202+ elif id (referent ) not in all_objs :
203+ recurse (referent , start , all_objs , current_path + [obj ])
204+
205+ for obj in objects :
206+ outstream .write (f"Examining: { obj } \n " )
207+ recurse (obj , obj , set (), [])
208+
209+ # }}}
210+
211+
142212# {{{ interactive shell
143213
144214def get_shell_hist_filename ():
0 commit comments