| import pickle |
| |
| import six |
| |
| |
| class MetaNode(type): |
| def __new__(mcs, name, bases, dict): |
| attrs = list(dict['attrs']) |
| dict['attrs'] = list() |
| |
| for base in bases: |
| if hasattr(base, 'attrs'): |
| dict['attrs'].extend(base.attrs) |
| |
| dict['attrs'].extend(attrs) |
| |
| return type.__new__(mcs, name, bases, dict) |
| |
| |
| @six.add_metaclass(MetaNode) |
| class Node(object): |
| attrs = () |
| |
| def __init__(self, **kwargs): |
| values = kwargs.copy() |
| |
| for attr_name in self.attrs: |
| value = values.pop(attr_name, None) |
| setattr(self, attr_name, value) |
| |
| if values: |
| raise ValueError('Extraneous arguments') |
| |
| def __equals__(self, other): |
| if type(other) is not type(self): |
| return False |
| |
| for attr in self.attrs: |
| if getattr(other, attr) != getattr(self, attr): |
| return False |
| |
| return True |
| |
| def __repr__(self): |
| return type(self).__name__ |
| |
| def __iter__(self): |
| return walk_tree(self) |
| |
| def filter(self, pattern): |
| for path, node in self: |
| if ((isinstance(pattern, type) and isinstance(node, pattern)) or |
| (node == pattern)): |
| yield path, node |
| |
| @property |
| def children(self): |
| return [getattr(self, attr_name) for attr_name in self.attrs] |
| |
| @property |
| def position(self): |
| if hasattr(self, "_position"): |
| return self._position |
| |
| def walk_tree(root): |
| children = None |
| |
| if isinstance(root, Node): |
| yield (), root |
| children = root.children |
| else: |
| children = root |
| |
| for child in children: |
| if isinstance(child, (Node, list, tuple)): |
| for path, node in walk_tree(child): |
| yield (root,) + path, node |
| |
| def dump(ast, file): |
| pickle.dump(ast, file) |
| |
| def load(file): |
| return pickle.load(file) |