def disambiguate(self): gd = GraphDisambiguator() gd.find_instances(self) self.nodes = TopographicalSorter( self.nodes, dependencies=lambda n: tuple( e.related for e in n.related(backward=False))).sorted()
def process(self, normalize=True, prune=True, disambiguate=True): # TODO move models' normalize methods to regulator steps (SHARE-1023) if normalize: self.normalize() gd = GraphDisambiguator() # TODO move pruning to regulator step (SHARE-1024) if prune: gd.prune(self) if disambiguate: gd.find_instances(self) self.nodes = TopographicalSorter(self.nodes, dependencies=lambda n: tuple(e.related for e in n.related(backward=False))).sorted()
def process(self, normalize=True, prune=True, disambiguate=True): if normalize: self.normalize() gd = GraphDisambiguator() if prune: gd.prune(self) if disambiguate: gd.find_instances(self) self.nodes = TopographicalSorter( self.nodes, dependencies=lambda n: tuple( e.related for e in n.related(backward=False))).sorted()
def __init__(self, data, namespace=None): self.nodes = [] self.relations = {} self._lookup = {} self.namespace = namespace hints, relations = {}, set() for blob in copy.deepcopy(data): id, type = blob.pop('@id'), blob.pop('@type').lower() self._lookup[id, type] = ChangeNode(self, id, type, blob, namespace=namespace) self.relations[self._lookup[id, type]] = set() self.nodes.append(self._lookup[id, type]) for k, v in tuple(blob.items()): if isinstance(v, dict) and k != 'extra' and not k.startswith('@'): related = (v.pop('@id'), v.pop('@type').lower()) hints[(id, type), related] = k relations.add(((id, type), related)) blob.pop(k) if isinstance(v, list): for rel in v: subject = (rel.pop('@id'), rel.pop('@type').lower()) relations.add((subject, (id, type))) blob.pop(k) for subject, related in relations: try: edge = GraphEdge(self._lookup[subject], self._lookup[related], hint=hints.get((subject, related))) except KeyError as e: raise UnresolvableReference(*e.args) self.relations[self._lookup[subject]].add(edge) self.relations[self._lookup[related]].add(edge) self.nodes = TopographicalSorter( self.nodes, dependencies=lambda n: tuple( e.related for e in n.related(backward=False))).sorted()
def process(self, normalize=True, prune=True, disambiguate=True): if normalize: self.normalize() gd = GraphDisambiguator() if prune: gd.prune(self) if disambiguate: gd.find_instances(self) self.nodes = TopographicalSorter(self.nodes, dependencies=lambda n: tuple(e.related for e in n.related(backward=False))).sorted()
def __init__(self, data, namespace=None): self.nodes = [] self.relations = {} self._lookup = {} self.namespace = namespace hints, relations = {}, set() for blob in copy.deepcopy(data): id, type = blob.pop('@id'), blob.pop('@type').lower() self._lookup[id, type] = ChangeNode(self, id, type, blob, namespace=namespace) self.relations[self._lookup[id, type]] = set() self.nodes.append(self._lookup[id, type]) for k, v in tuple(blob.items()): if isinstance(v, dict) and k != 'extra' and not k.startswith('@'): related = (v.pop('@id'), v.pop('@type').lower()) hints[(id, type), related] = k relations.add(((id, type), related)) blob.pop(k) if isinstance(v, list): for rel in v: subject = (rel.pop('@id'), rel.pop('@type').lower()) relations.add((subject, (id, type))) blob.pop(k) for subject, related in relations: try: edge = GraphEdge(self._lookup[subject], self._lookup[related], hint=hints.get((subject, related))) except KeyError as e: raise UnresolvableReference(*e.args) self.relations[self._lookup[subject]].add(edge) self.relations[self._lookup[related]].add(edge) self.nodes = TopographicalSorter(self.nodes, dependencies=lambda n: tuple(e.related for e in n.related(backward=False))).sorted()
class ChangeGraph: def __init__(self, data, namespace=None): self.nodes = [] self.relations = {} self._lookup = {} self.namespace = namespace hints, relations = {}, set() for blob in copy.deepcopy(data): id, type = blob.pop('@id'), blob.pop('@type').lower() self._lookup[id, type] = ChangeNode(self, id, type, blob, namespace=namespace) self.relations[self._lookup[id, type]] = set() self.nodes.append(self._lookup[id, type]) for k, v in tuple(blob.items()): if isinstance(v, dict) and k != 'extra' and not k.startswith('@'): related = (v.pop('@id'), v.pop('@type').lower()) hints[(id, type), related] = k relations.add(((id, type), related)) blob.pop(k) if isinstance(v, list): for rel in v: subject = (rel.pop('@id'), rel.pop('@type').lower()) relations.add((subject, (id, type))) blob.pop(k) for subject, related in relations: try: edge = GraphEdge(self._lookup[subject], self._lookup[related], hint=hints.get((subject, related))) except KeyError as e: raise UnresolvableReference(*e.args) self.relations[self._lookup[subject]].add(edge) self.relations[self._lookup[related]].add(edge) self.nodes = TopographicalSorter( self.nodes, dependencies=lambda n: tuple( e.related for e in n.related(backward=False))).sorted() def prune(self): gd = GraphDisambiguator() gd.prune(self) self.nodes = TopographicalSorter( self.nodes, dependencies=lambda n: tuple( e.related for e in n.related(backward=False))).sorted() def disambiguate(self): gd = GraphDisambiguator() gd.find_instances(self) self.nodes = TopographicalSorter( self.nodes, dependencies=lambda n: tuple( e.related for e in n.related(backward=False))).sorted() def normalize(self): # Freeze nodes to avoid oddities with inserting and removing nodes for node in tuple(self.nodes): # If a node has been removed dont normalize it # Fast check may be wrong if type has changed double check with a slower method if not (node.id, node.type) in self._lookup and node not in self.nodes: logger.debug('Skipping removed node %s', node) continue # This feels overly hacky if hasattr(node.model, 'normalize'): node.model.normalize(node, self) def process(self, normalize=True, prune=True, disambiguate=True): if normalize: self.normalize() gd = GraphDisambiguator() if prune: gd.prune(self) if disambiguate: gd.find_instances(self) self.nodes = TopographicalSorter( self.nodes, dependencies=lambda n: tuple( e.related for e in n.related(backward=False))).sorted() def get(self, id, type): return self._lookup[(id, type)] def create(self, id, type, attrs): return self.add( ChangeNode(self, id or '_:{}'.format(uuid.uuid4()), type, attrs, namespace=self.namespace)) def add(self, node): node.graph = self self.nodes.append(node) self.relations[node] = set() self._lookup[node.id, node.type] = node return node def relate(self, subject, related, hint=None): edge = GraphEdge(subject, related, hint) self.relations[subject].add(edge) self.relations[related].add(edge) return edge def replace(self, source, replacement): for edge in tuple(source.related()): # NOTE: Order of add & removes matters here due to # the hash function of an edge and how sets work self.relations[source].remove(edge) if edge.subject == source: self.relations[edge.related].remove(edge) edge.subject = replacement self.relations[edge.related].add(edge) else: self.relations[edge.subject].remove(edge) edge.related = replacement self.relations[edge.subject].add(edge) self.relations[replacement].add(edge) return self.remove(source) def remove(self, node, cascade=True): for edge in tuple(self.relations[node]): if edge.subject is node: self.relations[edge.related].remove(edge) elif cascade: self.remove(edge.subject, cascade=True) else: self.relations[edge.subject].remove(edge) self.nodes.remove(node) del self.relations[node] try: del self._lookup[node.id, node.type] except KeyError: key = next(k for k in self._lookup.keys() if k[0] == node.id) logger.warning( 'Could not find lookup entry for %s. Falling back to %s', node, key) del self._lookup[key] def serialize(self): return [ n.serialize() for n in sorted(self.nodes, key=lambda x: x.type + str(x.id)) ]
class ChangeGraph: def __init__(self, data, namespace=None): self.nodes = [] self.relations = {} self._lookup = {} self.namespace = namespace hints, relations = {}, set() for blob in copy.deepcopy(data): id, type = blob.pop('@id'), blob.pop('@type').lower() self._lookup[id, type] = ChangeNode(self, id, type, blob, namespace=namespace) self.relations[self._lookup[id, type]] = set() self.nodes.append(self._lookup[id, type]) for k, v in tuple(blob.items()): if isinstance(v, dict) and k != 'extra' and not k.startswith('@'): related = (v.pop('@id'), v.pop('@type').lower()) hints[(id, type), related] = k relations.add(((id, type), related)) blob.pop(k) if isinstance(v, list): for rel in v: subject = (rel.pop('@id'), rel.pop('@type').lower()) relations.add((subject, (id, type))) blob.pop(k) for subject, related in relations: try: edge = GraphEdge(self._lookup[subject], self._lookup[related], hint=hints.get((subject, related))) except KeyError as e: raise UnresolvableReference(*e.args) self.relations[self._lookup[subject]].add(edge) self.relations[self._lookup[related]].add(edge) self.nodes = TopographicalSorter(self.nodes, dependencies=lambda n: tuple(e.related for e in n.related(backward=False))).sorted() def prune(self): gd = GraphDisambiguator() gd.prune(self) self.nodes = TopographicalSorter(self.nodes, dependencies=lambda n: tuple(e.related for e in n.related(backward=False))).sorted() def disambiguate(self): gd = GraphDisambiguator() gd.find_instances(self) self.nodes = TopographicalSorter(self.nodes, dependencies=lambda n: tuple(e.related for e in n.related(backward=False))).sorted() def normalize(self): # Freeze nodes to avoid oddities with inserting and removing nodes for node in tuple(self.nodes): # If a node has been removed dont normalize it # Fast check may be wrong if type has changed double check with a slower method if not (node.id, node.type) in self._lookup and node not in self.nodes: logger.debug('Skipping removed node %s', node) continue # This feels overly hacky if hasattr(node.model, 'normalize'): node.model.normalize(node, self) def process(self, normalize=True, prune=True, disambiguate=True): if normalize: self.normalize() gd = GraphDisambiguator() if prune: gd.prune(self) if disambiguate: gd.find_instances(self) self.nodes = TopographicalSorter(self.nodes, dependencies=lambda n: tuple(e.related for e in n.related(backward=False))).sorted() def get(self, id, type): return self._lookup[(id, type)] def create(self, id, type, attrs): return self.add(ChangeNode(self, id or '_:{}'.format(uuid.uuid4()), type, attrs, namespace=self.namespace)) def add(self, node): node.graph = self self.nodes.append(node) self.relations[node] = set() self._lookup[node.id, node.type] = node return node def relate(self, subject, related, hint=None): edge = GraphEdge(subject, related, hint) self.relations[subject].add(edge) self.relations[related].add(edge) return edge def replace(self, source, replacement): for edge in tuple(source.related()): # NOTE: Order of add & removes matters here due to # the hash function of an edge and how sets work self.relations[source].remove(edge) if edge.subject == source: self.relations[edge.related].remove(edge) edge.subject = replacement self.relations[edge.related].add(edge) else: self.relations[edge.subject].remove(edge) edge.related = replacement self.relations[edge.subject].add(edge) self.relations[replacement].add(edge) return self.remove(source) def remove(self, node, cascade=True): for edge in tuple(self.relations[node]): if edge.subject is node: self.relations[edge.related].remove(edge) elif cascade: self.remove(edge.subject, cascade=True) else: self.relations[edge.subject].remove(edge) self.nodes.remove(node) del self.relations[node] try: del self._lookup[node.id, node.type] except KeyError: key = next(k for k in self._lookup.keys() if k[0] == node.id) logger.warning('Could not find lookup entry for %s. Falling back to %s', node, key) del self._lookup[key] def serialize(self): return [ n.serialize() for n in sorted(self.nodes, key=lambda x: x.type + str(x.id)) ]
def disambiguate(self): gd = GraphDisambiguator() gd.find_instances(self) self.nodes = TopographicalSorter(self.nodes, dependencies=lambda n: tuple(e.related for e in n.related(backward=False))).sorted()