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 |
|
|
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,21 +309,21 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 |
|
371 | 321 | |
|
372 | ||
|
322 | ||
|
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