##// END OF EJS Templates
Combined recursive approach with check for already visited objects to avoid infinite recursion....
Niclas -
Show More
@@ -268,66 +268,14 b' def update_function(old, new):'
268 268 pass
269 269
270 270
271 def _find_instances(old_type):
272 """Try to find all instances of a class that need updating.
273
274 Classic graph exploration, we want to avoid re-visiting object multiple times.
275 """
276 # find ipython workspace stack frame, this is just to bootstrap where we
277 # find the object that need updating.
278 frame = next(frame_nfo.frame for frame_nfo in inspect.stack()
279 if 'trigger' in frame_nfo.function)
280 # build generator for non-private variable values from workspace
281 shell = frame.f_locals['self'].shell
282 user_ns = shell.user_ns
283 user_ns_hidden = shell.user_ns_hidden
284 nonmatching = object()
285 objects = ( value for key, value in user_ns.items()
286 if not key.startswith('_')
287 and (value is not user_ns_hidden.get(key, nonmatching))
288 and not inspect.ismodule(value))
289
290 # note: in the following we do use dict as object might not be hashable.
291 # list of objects we found that will need an update.
292 to_update = {}
293
294 # list of object we have not recursed into yet
295 open_set = {}
296
297 # list of object we have visited already
298 closed_set = {}
299
300 open_set.update({id(o):o for o in objects})
301
302 it = 0
303 while len(open_set) > 0:
304 it += 1
305 if it > 100_000:
306 raise ValueError('infinite')
307 (current_id,current) = next(iter(open_set.items()))
308 if type(current) is old_type:
309 to_update[current_id] = current
310 if hasattr(current, '__dict__') and not (inspect.isfunction(current)
311 or inspect.ismethod(current)):
312 potential_new = {id(o):o for o in current.__dict__.values() if id(o) not in closed_set.keys()}
313 open_set.update(potential_new)
314 # if object is a container, search it
315 if hasattr(current, 'items') or (hasattr(current, '__contains__')
316 and not isinstance(current, str)):
317 potential_new = (value for key, value in current.items()
318 if not str(key).startswith('_')
319 and not inspect.ismodule(value) and not id(value) in closed_set.keys())
320 open_set.update(potential_new)
321 del open_set[id(current)]
322 closed_set[id(current)] = current
323 return to_update.values()
324
325 def update_instances(old, new, objects=None):
271 def update_instances(old, new, objects=None, visited={}):
326 272 """Iterate through objects recursively, searching for instances of old and
327 273 replace their __class__ reference with new. If no objects are given, start
328 274 with the current ipython workspace.
329 275 """
330 if not objects:
276 if objects is None:
277 # make sure visited is cleaned when not called recursively
278 visited = {}
331 279 # find ipython workspace stack frame
332 280 frame = next(frame_nfo.frame for frame_nfo in inspect.stack()
333 281 if 'trigger' in frame_nfo.function)
@@ -349,7 +297,9 b' def update_instances(old, new, objects=None):'
349 297
350 298 # try if objects is iterable
351 299 try:
352 for obj in objects:
300 for obj in (obj for obj in objects if id(obj) not in visited):
301 # add current object to visited to avoid revisiting
302 visited.update({id(obj):obj})
353 303
354 304 # update, if object is instance of old_class (but no subclasses)
355 305 if type(obj) is old:
@@ -359,12 +309,12 b' def update_instances(old, new, objects=None):'
359 309 # if object is instance of other class, look for nested instances
360 310 if hasattr(obj, '__dict__') and not (inspect.isfunction(obj)
361 311 or inspect.ismethod(obj)):
362 update_instances(old, new, obj.__dict__)
312 update_instances(old, new, obj.__dict__, visited)
363 313
364 314 # if object is a container, search it
365 315 if hasattr(obj, 'items') or (hasattr(obj, '__contains__')
366 316 and not isinstance(obj, str)):
367 update_instances(old, new, obj)
317 update_instances(old, new, obj, visited)
368 318
369 319 except TypeError:
370 320 pass
@@ -373,7 +323,7 b' def update_instances(old, new, objects=None):'
373 323 def update_class(old, new):
374 324 """Replace stuff in the __dict__ of a class, and upgrade
375 325 method code objects, and add new methods, if any"""
376 print('old is', old)
326 print('old is', id(old))
377 327 for key in list(old.__dict__.keys()):
378 328 old_obj = getattr(old, key)
379 329 try:
@@ -405,8 +355,7 b' def update_class(old, new):'
405 355 pass # skip non-writable attributes
406 356
407 357 # update all instances of class
408 for instance in _find_instances(old):
409 instance.__class__ = new
358 update_instances(old, new)
410 359
411 360
412 361 def update_property(old, new):
General Comments 0
You need to be logged in to leave comments. Login now