You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
3.2 KiB

#!/usr/bin/env python
import copy
class Ref(object):
def __init__(self, obj):
self._id = self.id(obj)
def get_id(self):
return self._id
@staticmethod
def id(obj):
return str(id(obj))
def flatten(obj):
"""Flatten an object hierarchy.
Walks an object hierarchy while keeping a dictionary of
non-Ref objects. Replaces all non-Ref objects with Ref
objects.
Parameters
----------
obj : object (new-style)
Root of the object hierarchy to flatten.
Returns
-------
(flat_obj, refs) : tuple
flat_obj is a deep-copy of obj with all non-Ref objects
replaced with Ref objects. refs is a dictionary of non-Ref
objects.
"""
refs = {}
cpy = copy.deepcopy(obj)
todo = [cpy]
while len(todo) > 0:
current = todo.pop(0)
if isinstance(current, object) and hasattr(current, "__dict__"):
todo.append(current.__dict__)
elif isinstance(current, dict):
for k, v in list(current.items()):
if isinstance(v, object) and hasattr(v, "__dict__") and not isinstance(v, Ref):
todo.append(v)
refs.setdefault(Ref.id(v), v)
current[k] = Ref(v)
elif isinstance(current, list):
for i, v in enumerate(current):
if isinstance(v, object) and hasattr(v, "__dict__") and not isinstance(v, Ref):
todo.append(v)
refs.setdefault(Ref.id(v), v)
current[i] = Ref(v)
elif (
isinstance(current, set)
or isinstance(current, frozenset)
or isinstance(current, tuple)
):
raise ValueError("unsupported: %s", current)
return (cpy, refs)
def expand(obj, refs):
"""Expand a flattened object hierarchy.
Walks a flattened object hierarchy and replaces Ref objects
with the actual objects referred to from refs.
Parameters
----------
obj : object (new-style)
Root of the object hierarchy flattened by flatten(). All
objects are modified in place.
refs : dict
Dictionary of object references used by Ref objects in obj.
Returns
-------
obj : object (new-style)
For convenience, the expanded obj is returned.
"""
todo = [obj]
while len(todo) > 0:
current = todo.pop(0)
if isinstance(current, object) and hasattr(current, "__dict__"):
assert not isinstance(current, Ref)
todo.append(current.__dict__)
elif isinstance(current, dict):
for k, v in list(current.items()):
if isinstance(v, Ref):
current[k] = refs[v.get_id()]
todo.append(current[k])
elif isinstance(current, list):
for i, v in enumerate(current):
if isinstance(v, Ref):
current[i] = refs[v.get_id()]
todo.append(current[i])
elif (
isinstance(current, set)
or isinstance(current, frozenset)
or isinstance(current, tuple)
):
raise ValueError("unsupported: %s", current)
return obj