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 |
|
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