def state_not(inv, msg=None): """ Decorates an object method to throw StateError if its state is listed in the given invalid states The object is assumed to have a "state" attribute; it is up to the caller to ensure that this holds. @param inv: either the invalid state or an iterable of invalid states @param msg: either an error message, or a callable or map that returns one """ msg_cb = callable_wrap(msg) def decorate(method): if hasattr(inv, "__iter__"): def wrap(self, *args, **kwargs): if self.state in inv: raise StateError(msg_cb(self.state), self.state) return method(self, *args, **kwargs) else: def wrap(self, *args, **kwargs): if self.state == inv: raise StateError(msg_cb(self.state), self.state) return method(self, *args, **kwargs) return wrap return decorate
def build(self, keep_dangle=False, bipartite=False, node_attr=None, inverse=False, complete=True): """ Build a graph out of the sample. Definitions: an "explicit" node is one which has been explicited added to the sample with add_node(). A "dangling" node is one referenced by the out-dicts of other nodes, but not explicitly added to the sample (and implicitly has no out-dict of its own). The total number of explicit nodes will be stored in self.order, and if kept (see <keep_dangle>), the total number of dangling nodes will be stored in self.extra. @param keep_dangle: whether to keep dangling nodes; if so, these nodes will have vertex ids greater than explicit nodes. <bipartite> and <node_attr> will only have an effect if this is True. @param bipartite: whether to raise an error if an explicit node points to another explicit node @param node_attr: this is passed through callable_wrap() to give a mapping from dangling nodes to node attributes @param inverse: whether to invert edge directions @param complete: whether to store the built graph in self.graph and discard the cache. it will then be impossible to add new nodes, and future calls to this method will always return self.graph @return: The built graph """ if self.graph is not None: return self.graph if keep_dangle: attr_cb = callable_wrap(node_attr) # init nodes v_id = [node.id for node in self._list] v_attr = [node.attr for node in self._list] id_v = dict((node.id, i) for i, node in enumerate(self._list)) self.order = j = len(self._list) # init edges arc_s, arc_t, edges, e_attr = edge_array(0x10001 if keep_dangle else j, 'd', inverse) for (i, node) in enumerate(self._list): for (dst, attr) in node.out.iteritems(): if dst in self._keys: if keep_dangle and bipartite: raise ValueError("non-bipartite graph: %s - %s" % (node.id, dst)) arc_s.append(i) arc_t.append(id_v[dst]) e_attr.append(attr) elif keep_dangle: if dst in id_v: x = id_v[dst] else: x = id_v[dst] = j v_id.append(dst) v_attr.append(attr_cb(dst)) j += 1 arc_s.append(i) arc_t.append(x) e_attr.append(attr) else: pass assert j == len(id_v) == len(v_id) == len(v_attr) if keep_dangle: self.extra = j - self.order # igraph can't handle utf-8 output, see launchpad bug #545663 for (i, id) in enumerate(v_id): if type(id) == unicode: v_id[i] = id.encode("utf-8") # prepare attributes va = {NID: v_id} if any(a is not None for a in v_attr): va[NAT] = v_attr # build graph gg = Graph(n=j, directed=True, vertex_attrs=va) gg.add_edges(edges) gg.es[AAT] = e_attr if complete: self.graph = gg self._keys = None self._list = None return gg