Show More
@@ -115,6 +115,7 b' import sys' | |||||
115 | import traceback |
|
115 | import traceback | |
116 | import types |
|
116 | import types | |
117 | import weakref |
|
117 | import weakref | |
|
118 | import inspect | |||
118 | from importlib import import_module |
|
119 | from importlib import import_module | |
119 | from importlib.util import source_from_cache |
|
120 | from importlib.util import source_from_cache | |
120 | from imp import reload |
|
121 | from imp import reload | |
@@ -267,6 +268,54 b' def update_function(old, new):' | |||||
267 | pass |
|
268 | pass | |
268 |
|
269 | |||
269 |
|
270 | |||
|
271 | def update_instances(old, new, objects=None): | |||
|
272 | """Iterate through objects recursively, searching for instances of old and | |||
|
273 | replace their __class__ reference with new. If no objects are given, start | |||
|
274 | with the current ipython workspace. | |||
|
275 | """ | |||
|
276 | if not objects: | |||
|
277 | # find ipython workspace stack frame | |||
|
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 | # use dict values if objects is a dict but don't touch private variables | |||
|
291 | if hasattr(objects, 'items'): | |||
|
292 | objects = (value for key, value in objects.items() | |||
|
293 | if not str(key).startswith('_') | |||
|
294 | and not inspect.ismodule(value) ) | |||
|
295 | ||||
|
296 | # try if objects is iterable | |||
|
297 | try: | |||
|
298 | for obj in objects: | |||
|
299 | ||||
|
300 | # update, if object is instance of old_class (but no subclasses) | |||
|
301 | if type(obj) is old: | |||
|
302 | obj.__class__ = new | |||
|
303 | ||||
|
304 | ||||
|
305 | # if object is instance of other class, look for nested instances | |||
|
306 | if hasattr(obj, '__dict__') and not (inspect.isfunction(obj) | |||
|
307 | or inspect.ismethod(obj)): | |||
|
308 | update_instances(old, new, obj.__dict__) | |||
|
309 | ||||
|
310 | # if object is a container, search it | |||
|
311 | if hasattr(obj, 'items') or (hasattr(obj, '__contains__') | |||
|
312 | and not isinstance(obj, str)): | |||
|
313 | update_instances(old, new, obj) | |||
|
314 | ||||
|
315 | except TypeError: | |||
|
316 | pass | |||
|
317 | ||||
|
318 | ||||
270 | def update_class(old, new): |
|
319 | def update_class(old, new): | |
271 | """Replace stuff in the __dict__ of a class, and upgrade |
|
320 | """Replace stuff in the __dict__ of a class, and upgrade | |
272 | method code objects, and add new methods, if any""" |
|
321 | method code objects, and add new methods, if any""" | |
@@ -300,6 +349,9 b' def update_class(old, new):' | |||||
300 | except (AttributeError, TypeError): |
|
349 | except (AttributeError, TypeError): | |
301 | pass # skip non-writable attributes |
|
350 | pass # skip non-writable attributes | |
302 |
|
351 | |||
|
352 | # update all instances of class | |||
|
353 | update_instances(old, new) | |||
|
354 | ||||
303 |
|
355 | |||
304 | def update_property(old, new): |
|
356 | def update_property(old, new): | |
305 | """Replace get/set/del functions of a property""" |
|
357 | """Replace get/set/del functions of a property""" |
General Comments 0
You need to be logged in to leave comments.
Login now