##// END OF EJS Templates
Try to fix updating classes in Autoreload....
Matthias Bussonnier -
Show More
@@ -268,6 +268,60 b' def update_function(old, new):'
268 pass
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
271 def update_instances(old, new, objects=None):
325 def update_instances(old, new, objects=None):
272 """Iterate through objects recursively, searching for instances of old and
326 """Iterate through objects recursively, searching for instances of old and
273 replace their __class__ reference with new. If no objects are given, start
327 replace their __class__ reference with new. If no objects are given, start
@@ -319,6 +373,7 b' def update_instances(old, new, objects=None):'
319 def update_class(old, new):
373 def update_class(old, new):
320 """Replace stuff in the __dict__ of a class, and upgrade
374 """Replace stuff in the __dict__ of a class, and upgrade
321 method code objects, and add new methods, if any"""
375 method code objects, and add new methods, if any"""
376 print('old is', old)
322 for key in list(old.__dict__.keys()):
377 for key in list(old.__dict__.keys()):
323 old_obj = getattr(old, key)
378 old_obj = getattr(old, key)
324 try:
379 try:
@@ -350,7 +405,8 b' def update_class(old, new):'
350 pass # skip non-writable attributes
405 pass # skip non-writable attributes
351
406
352 # update all instances of class
407 # update all instances of class
353 update_instances(old, new)
408 for instance in _find_instances(old):
409 instance.__class__ = new
354
410
355
411
356 def update_property(old, new):
412 def update_property(old, new):
@@ -35,10 +35,12 b' from IPython.core.events import EventManager, pre_run_cell'
35
35
36 noop = lambda *a, **kw: None
36 noop = lambda *a, **kw: None
37
37
38 class FakeShell(object):
38 class FakeShell:
39
39
40 def __init__(self):
40 def __init__(self):
41 self.ns = {}
41 self.ns = {}
42 self.user_ns = {}
43 self.user_ns_hidden = {}
42 self.events = EventManager(self, {'pre_run_cell', pre_run_cell})
44 self.events = EventManager(self, {'pre_run_cell', pre_run_cell})
43 self.auto_magics = AutoreloadMagics(shell=self)
45 self.auto_magics = AutoreloadMagics(shell=self)
44 self.events.register('pre_run_cell', self.auto_magics.pre_run_cell)
46 self.events.register('pre_run_cell', self.auto_magics.pre_run_cell)
General Comments 0
You need to be logged in to leave comments. Login now