def test_data_types(self): data = [ True, False, 10**20, -2e33, "'", '"&&&""', [{ (b"\xfd", ): "\x7f", chr(0x4444): (1, 2) }, (2, "3")], ] data.append(chr(0x14444)) data.append(literal_eval("{2.3j, 1 - 2.3j, ()}")) G = eg.Graph() G.name = data G.graph["data"] = data G.add_node(0, int=-1, data=dict(data=data)) G.add_edge(0, 0, float=-2.5, data=data) gml = "\n".join(eg.generate_gml(G, stringizer=literal_stringizer)) G = eg.parse_gml(gml, destringizer=literal_destringizer) assert data == G.name assert {"name": data, "data": data} == G.graph assert G.nodes == {0: dict(int=-1, data=dict(data=data))} assert list(G.edges) == [(0, 0, dict(float=-2.5, data=data))] G = eg.Graph() G.graph["data"] = "frozenset([1, 2, 3])" G = eg.parse_gml(eg.generate_gml(G), destringizer=literal_eval) assert G.graph["data"] == "frozenset([1, 2, 3])"
def test_edge_id_construct(self): G = eg.Graph() G.add_edges_from([(0, 1, {"id": 0}), (1, 2, {"id": 2}), (2, 3)]) expected = f"""<gexf xmlns="http://www.gexf.net/1.2draft" xmlns:xsi\ ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.\ gexf.net/1.2draft http://www.gexf.net/1.2draft/gexf.xsd" version="1.2"> <meta lastmodifieddate="{time.strftime('%Y-%m-%d')}"> <creator>EasyGraph</creator> </meta> <graph defaultedgetype="undirected" mode="static" name=""> <nodes> <node id="0" label="0" /> <node id="1" label="1" /> <node id="2" label="2" /> <node id="3" label="3" /> </nodes> <edges> <edge source="0" target="1" id="0" /> <edge source="1" target="2" id="2" /> <edge source="2" target="3" id="1" /> </edges> </graph> </gexf>""" obtained = "\n".join(eg.generate_gexf(G)) assert expected == obtained
def get_graph_blogcatalog(): """Returns the undirected graph of blogcatalog. Returns ------- get_graph_blogcatalog : easygraph.Graph The undirected graph instance of blogcatalog from dataset: https://github.com/phanein/deepwalk/blob/master/example_graphs/blogcatalog.mat References ---------- .. [1] https://github.com/phanein/deepwalk/blob/master/example_graphs/blogcatalog.mat """ from scipy.io import loadmat def sparse2graph(x): from collections import defaultdict G = defaultdict(lambda: set()) cx = x.tocoo() for i, j, v in zip(cx.row, cx.col, cx.data): G[i].add(j) return {str(k): [str(x) for x in v] for k, v in G.items()} mat = loadmat("./samples/blogcatalog.mat") A = mat["network"] data = sparse2graph(A) G = eg.Graph() for u in data: for v in data[u]: G.add_edge(u, v) return G
def test_outofrange_integers(self): # GML restricts integers to 32 signed bits. # Check that we honor this restriction on export G = eg.Graph() # Test export for numbers that barely fit or don't fit into 32 bits, # and 3 numbers in the middle numbers = { "toosmall": (-(2**31)) - 1, "small": -(2**31), "med1": -4, "med2": 0, "med3": 17, "big": (2**31) - 1, "toobig": 2**31, } G.add_node("Node", **numbers) fd, fname = tempfile.mkstemp() try: eg.write_gml(G, fname) # Check that the export wrote the nonfitting numbers as strings G2 = eg.read_gml(fname) for attr, value in G2.nodes["Node"].items(): if attr == "toosmall" or attr == "toobig": assert type(value) == str else: assert type(value) == int finally: os.close(fd) os.unlink(fname)
def get_graph_youtube(): """Returns the undirected graph of Youtube dataset. Returns ------- get_graph_youtube : easygraph.Graph The undirected graph instance of Youtube from dataset: http://socialnetworks.mpi-sws.mpg.de/data/youtube-links.txt.gz References ---------- .. [1] http://socialnetworks.mpi-sws.mpg.de/data/youtube-links.txt.gz """ import gzip from urllib import request url = "http://socialnetworks.mpi-sws.mpg.de/data/youtube-links.txt.gz" zipped_data_path = "./samples/youtube-links.txt.gz" unzipped_data_path = "./samples/youtube-links.txt" # Download .gz file print("Downloading Youtube dataset...") request.urlretrieve(url, zipped_data_path, _show_progress) # Unzip unzipped_data = gzip.GzipFile(zipped_data_path) open(unzipped_data_path, "wb+").write(unzipped_data.read()) unzipped_data.close() # Returns graph G = eg.Graph() G.add_edges_from_file(file=unzipped_data_path) return G
def test_specials(self): from math import isnan inf, nan = float("inf"), float("nan") G = eg.Graph() G.add_node(1, testattr=inf, strdata="inf", key="a") G.add_node(2, testattr=nan, strdata="nan", key="b") G.add_node(3, testattr=-inf, strdata="-inf", key="c") fh = io.BytesIO() eg.write_gexf(G, fh) fh.seek(0) filetext = fh.read() fh.seek(0) H = eg.read_gexf(fh, node_type=int) assert b"INF" in filetext assert b"NaN" in filetext assert b"-INF" in filetext assert H.nodes[1]["testattr"] == inf assert isnan(H.nodes[2]["testattr"]) assert H.nodes[3]["testattr"] == -inf assert H.nodes[1]["strdata"] == "inf" assert H.nodes[2]["strdata"] == "nan" assert H.nodes[3]["strdata"] == "-inf" assert H.nodes[1]["easygraph_key"] == "a" assert H.nodes[2]["easygraph_key"] == "b" assert H.nodes[3]["easygraph_key"] == "c"
def get_graph_flickr(): """Returns the undirected graph of Flickr dataset. Returns ------- get_graph_flickr : easygraph.Graph The undirected graph instance of Flickr from dataset: http://socialnetworks.mpi-sws.mpg.de/data/flickr-links.txt.gz References ---------- .. [1] http://socialnetworks.mpi-sws.mpg.de/data/flickr-links.txt.gz """ from urllib import request import gzip url = 'http://socialnetworks.mpi-sws.mpg.de/data/flickr-links.txt.gz' zipped_data_path = './samples/flickr-links.txt.gz' unzipped_data_path = './samples/flickr-links.txt' # Download .gz file print("Downloading Flickr dataset...") request.urlretrieve(url, zipped_data_path, _show_progress) # Unzip unzipped_data = gzip.GzipFile(zipped_data_path) open(unzipped_data_path, 'wb+').write(unzipped_data.read()) unzipped_data.close() # Returns graph G = eg.Graph() G.add_edges_from_file(file=unzipped_data_path) return G
def test_bool(self): G = eg.Graph() G.add_node(1, testattr=True) fh = io.BytesIO() eg.write_gexf(G, fh) fh.seek(0) H = eg.read_gexf(fh, node_type=int) assert H.nodes[1]["testattr"]
def test_serialize_ints_to_strings(self): G = eg.Graph() G.add_node(1, id=7, label=77) fh = io.BytesIO() eg.write_gexf(G, fh) fh.seek(0) H = eg.read_gexf(fh, node_type=int) assert list(H) == [7] assert H.nodes[7]["label"] == "77"
def test_simple_list(self): G = eg.Graph() list_value = [(1, 2, 3), (9, 1, 2)] G.add_node(1, key=list_value) fh = io.BytesIO() eg.write_gexf(G, fh) fh.seek(0) H = eg.read_gexf(fh, node_type=int) assert H.nodes[1]["easygraph_key"] == list_value
def test_add_parent(self): G = eg.Graph() G.add_node(0, label="1", color="green", parents=[1, 2]) fh = io.BytesIO() eg.write_gexf(G, fh) fh.seek(0) H = eg.read_gexf(fh, node_type=int) assert sorted(G.nodes) == sorted(H.nodes) assert sorted(sorted(e) for e in G.edges) == sorted(sorted(e) for e in H.edges)
def test_dynamic_mode(self): G = eg.Graph() G.add_node(1, label="1", color="green") G.graph["mode"] = "dynamic" fh = io.BytesIO() eg.write_gexf(G, fh) fh.seek(0) H = eg.read_gexf(fh, node_type=int) assert sorted(G.nodes) == sorted(H.nodes) assert sorted(sorted(e) for e in G.edges) == sorted(sorted(e) for e in H.edges)
def test_labels_are_strings(self): # GML requires labels to be strings (i.e., in quotes) answer = """graph [ node [ id 0 label "1203" ] ]""" G = eg.Graph() G.add_node(1203) data = "\n".join(eg.generate_gml(G, stringizer=literal_stringizer)) assert data == answer
def CombineNodes(records, G, label_dict, score_dict, node_dict, Next_label_dict, nodes, degrees, distance_dict): onerecord = dict() for node, label in label_dict.items(): if label in onerecord: onerecord[label].append(node) else: onerecord[label] = [node] records.append(onerecord) Gx = eg.Graph() label_dictx = dict() score_dictx = dict() node_dictx = dict() nodesx = [] cnt = 0 for record_label in onerecord: nodesx.append(cnt) label_dictx[cnt] = record_label score_dictx[record_label] = score_dict[record_label] node_dictx[record_label] = cnt cnt += 1 record_labels = list(onerecord.keys()) i = 0 edge = dict() adj = G.adj for i in range(0, len(record_labels)): edge[i] = dict() for j in range(0, len(record_labels)): if i == j: continue inodes = onerecord[record_labels[i]] jnodes = onerecord[record_labels[j]] for unode in inodes: for vnode in jnodes: if unode in adj and vnode in adj[unode]: if j not in edge[i]: edge[i][j] = 0 edge[i][j] += adj[unode][vnode].get("weight", 1) for unode in edge: for vnode, w in edge[unode].items(): if unode < vnode: Gx.add_edge(unode, vnode, weight=w) G = Gx label_dict = label_dictx score_dict = score_dictx node_dict = node_dictx Next_label_dict = label_dictx nodes = nodesx degrees = G.degree() distance_dict = eg.Floyd(G) return records, G, label_dict, score_dict, node_dict, Next_label_dict, nodes, degrees, distance_dict
def test_slice_and_spell(self): # Test spell first, so version = 1.2 G = eg.Graph() G.add_node(0, label="1", color="green") G.nodes[0]["spells"] = [(1, 2)] fh = io.BytesIO() eg.write_gexf(G, fh) fh.seek(0) H = eg.read_gexf(fh, node_type=int) assert sorted(G.nodes) == sorted(H.nodes) assert sorted(sorted(e) for e in G.edges) == sorted(sorted(e) for e in H.edges) G = eg.Graph() G.add_node(0, label="1", color="green") G.nodes[0]["slices"] = [(1, 2)] fh = io.BytesIO() eg.write_gexf(G, fh, version="1.1draft") fh.seek(0) H = eg.read_gexf(fh, node_type=int) assert sorted(G.nodes) == sorted(H.nodes) assert sorted(sorted(e) for e in G.edges) == sorted(sorted(e) for e in H.edges)
def graph_to_d_atleast2(G): n = len(G) LG = eg.Graph() LG = G.copy() new_node = n degree = LG.degree() node = LG.nodes.copy() for i in node: if degree[i] == 1: for neighbors in LG.neighbors(node=i): LG.add_edge(i, new_node) LG.add_edge(new_node, neighbors) break new_node = new_node + 1 return LG
def test_float_label(self): node = 1.0 G = eg.Graph() G.add_node(node) fobj = tempfile.NamedTemporaryFile() eg.write_gml(G, fobj) fobj.seek(0) # Should be bytes in 2.x and 3.x data = fobj.read().strip().decode("ascii") answer = """graph [ node [ id 0 label "1.0" ] ]""" assert data == answer
def test_unicode_node(self): node = "node" + chr(169) G = eg.Graph() G.add_node(node) fobj = tempfile.NamedTemporaryFile() eg.write_gml(G, fobj) fobj.seek(0) # Should be bytes in 2.x and 3.x data = fobj.read().strip().decode("ascii") answer = """graph [ node [ id 0 label "node©" ] ]""" assert data == answer
def _find_separation_nodes(G): G_s = condensation(G) SCC_mapping = {} incoming_info = G_s.graph["incoming_info"] G_s_undirected = eg.Graph() sep_nodes = set() for node in (G_s.nodes).keys(): SCC_mapping[node] = G_s.nodes[node]["member"] if len(G_s.nodes[node]["member"]) == 1: sep_nodes.add(node) G_s_undirected.add_node(node, member=G_s.nodes[node]["member"]) for edge in G_s.edges: G_s_undirected.add_edge(edge[0], edge[1]) cut_nodes = eg.generator_articulation_points(G_s_undirected) out_degree = G_s.out_degree() in_degree = G_s.in_degree() separations = set() for cut_node in cut_nodes: if cut_node in sep_nodes: if out_degree[cut_node] >= 1 and in_degree[cut_node] >= 1: CC_u = eg.connected_component_of_node(G_s_undirected, node=cut_node) G_CC = G_s_undirected.nodes_subgraph(list(CC_u)) G_CC.remove_node(cut_node) successors = G_s.neighbors(node=cut_node) predecessors = G_s.predecessors(node=cut_node) CC_removal = eg.connected_components(G_CC) flag = True for group in CC_removal: flag_succ = False flag_pred = False for node in group: if node in successors: flag_succ = True if flag_pred: flag = False break elif node in predecessors: flag_pred = True if flag_succ: flag = False break if not flag: break if flag: separations.add(list(SCC_mapping[cut_node])[0]) return separations, SCC_mapping, incoming_info
def test_writing_graph_with_one_element_property_list(self): g = eg.Graph() g.add_node("n1", properties=["element"]) with byte_file() as f: eg.write_gml(g, f) result = f.read().decode() assert result == dedent("""\ graph [ node [ id 0 label "n1" properties "_easygraph_list_start" properties "element" ] ] """)
def test_default_attribute(self): G = eg.Graph() G.add_node(1, label="1", color="green") eg.add_path(G, [0, 1, 2, 3]) G.add_edge(1, 2, foo=3) G.graph["node_default"] = {"color": "yellow"} G.graph["edge_default"] = {"foo": 7} fh = io.BytesIO() eg.write_gexf(G, fh) fh.seek(0) H = eg.read_gexf(fh, node_type=int) assert sorted(G.nodes) == sorted(H.nodes) # Reading a gexf graph always sets mode attribute to either # 'static' or 'dynamic'. Remove the mode attribute from the # read graph for the sake of comparing remaining attributes. del H.graph["mode"] assert G.graph == H.graph
def _get_spanning_tree_of_component(G): spanning_tree = eg.Graph() seen = set() def _plain_dfs(u): for v, edge_data in G.adj[u].items(): if v not in seen: seen.add(v) spanning_tree.add_edge(u, v) _plain_dfs(v) random_node = list(G.nodes)[0] seen.add(random_node) spanning_tree.add_node(random_node) _plain_dfs(random_node) return spanning_tree
def make_graph(self, graph_xml, graphml_keys, defaults, G=None): # set default graph type edgedefault = graph_xml.get("edgedefault", None) if G is None: if edgedefault == "directed": G = eg.MultiDiGraph() else: G = eg.MultiGraph() # set defaults for graph attributes G.graph["node_default"] = {} G.graph["edge_default"] = {} for key_id, value in defaults.items(): key_for = graphml_keys[key_id]["for"] name = graphml_keys[key_id]["name"] python_type = graphml_keys[key_id]["type"] if key_for == "node": G.graph["node_default"].update({name: python_type(value)}) if key_for == "edge": G.graph["edge_default"].update({name: python_type(value)}) # hyperedges are not supported hyperedge = graph_xml.find(f"{{{self.NS_GRAPHML}}}hyperedge") if hyperedge is not None: raise eg.EasyGraphError("GraphML reader doesn't support hyperedges") # add nodes for node_xml in graph_xml.findall(f"{{{self.NS_GRAPHML}}}node"): self.add_node(G, node_xml, graphml_keys, defaults) # add edges for edge_xml in graph_xml.findall(f"{{{self.NS_GRAPHML}}}edge"): self.add_edge(G, edge_xml, graphml_keys) # add graph data data = self.decode_data_elements(graphml_keys, graph_xml) G.graph.update(data) # switch to Graph or DiGraph if no parallel edges were found if self.multigraph: return G G = eg.DiGraph(G) if G.is_directed() else eg.Graph(G) # add explicit edge "id" from file as attribute in eg graph. eg.set_edge_attributes(G, values=self.edge_ids, name="id") return G
def test_writing_graph_with_multi_element_property_list(self): g = eg.Graph() g.add_node("n1", properties=["element", 0, 1, 2.5, True, False]) with byte_file() as f: eg.write_gml(g, f) result = f.read().decode() assert result == dedent("""\ graph [ node [ id 0 label "n1" properties "element" properties 0 properties 1 properties 2.5 properties 1 properties 0 ] ] """)
def _commonUpdate(G, node_u, node_v, threshold, score_dict): for node_w in G.neighbors(node=node_u): _computeTieStrength(G, node_u, node_w) for node_w in G.predecessors(node=node_u): _computeTieStrength(G, node_w, node_u) G_un = eg.Graph() for node in G.nodes: G_un.add_node(node) for edge in G.edges: if not G_un.has_edge(edge[0], edge[1]): G_un.add_edge(edge[0], edge[1]) u_2hop = _get2hop(G_un, node_u) G_u = G.nodes_subgraph(from_nodes=u_2hop) v_2hop = _get2hop(G_un, node_v) G_v = G.nodes_subgraph(from_nodes=v_2hop) score_u = _updateScore(node_u, G_u, threshold) score_v = _updateScore(node_v, G_v, threshold) score_dict[node_u] = score_u score_dict[node_v] = score_v all_neigh_u = list(set(G.all_neighbors(node=node_u))) # print("all_neigh:",all_neigh_u) all_neigh_v = list(set(G.all_neighbors(node=node_v))) for node_w in all_neigh_u: if node_w in all_neigh_v: w_2hop = _get2hop(G_un, node_w) G_w = G.nodes_subgraph(from_nodes=w_2hop) score_w = _updateScore(node_w, G_w, threshold) else: score_w = 0 w_2hop = _get2hop(G_un, node_w) G_w = G.nodes_subgraph(from_nodes=w_2hop) for c in _strongly_connected_components(G_w, threshold): if node_u in c: length = len(c) closeness_c_w = _computeCloseness(G, c, node_w, threshold, length) if closeness_c_w < 0: score_w -= closeness_c_w score_dict[node_w] = score_w
def parse_gml_lines(lines, label, destringizer): """Parse GML `lines` into a graph.""" def tokenize(): patterns = [ r"[A-Za-z][0-9A-Za-z_]*\b", # keys # reals r"[+-]?(?:[0-9]*\.[0-9]+|[0-9]+\.[0-9]*|INF)(?:[Ee][+-]?[0-9]+)?", r"[+-]?[0-9]+", # ints r'".*?"', # strings r"\[", # dict start r"\]", # dict end r"#.*$|\s+", # comments and whitespaces ] tokens = re.compile("|".join(f"({pattern})" for pattern in patterns)) lineno = 0 for line in lines: length = len(line) pos = 0 while pos < length: match = tokens.match(line, pos) if match is None: m = f"cannot tokenize {line[pos:]} at ({lineno + 1}, {pos + 1})" raise EasyGraphError(m) for i in range(len(patterns)): group = match.group(i + 1) if group is not None: if i == 0: # keys value = group.rstrip() elif i == 1: # reals value = float(group) elif i == 2: # ints value = int(group) else: value = group if i != 6: # comments and whitespaces yield Token(Pattern(i), value, lineno + 1, pos + 1) pos += len(group) break lineno += 1 yield Token(None, None, lineno + 1, 1) # EOF def unexpected(curr_token, expected): category, value, lineno, pos = curr_token value = repr(value) if value is not None else "EOF" raise EasyGraphError( f"expected {expected}, found {value} at ({lineno}, {pos})") def consume(curr_token, category, expected): if curr_token.category == category: return next(tokens) unexpected(curr_token, expected) def parse_dict(curr_token): # dict start curr_token = consume(curr_token, Pattern.DICT_START, "'['") # dict contents curr_token, dct = parse_kv(curr_token) # dict end curr_token = consume(curr_token, Pattern.DICT_END, "']'") return curr_token, dct def parse_kv(curr_token): dct = defaultdict(list) while curr_token.category == Pattern.KEYS: key = curr_token.value curr_token = next(tokens) category = curr_token.category if category == Pattern.REALS or category == Pattern.INTS: value = curr_token.value curr_token = next(tokens) elif category == Pattern.STRINGS: value = unescape(curr_token.value[1:-1]) if destringizer: try: value = destringizer(value) except ValueError: pass curr_token = next(tokens) elif category == Pattern.DICT_START: curr_token, value = parse_dict(curr_token) else: if key in ("id", "label", "source", "target"): try: # String convert the token value value = unescape(str(curr_token.value)) if destringizer: try: value = destringizer(value) except ValueError: pass curr_token = next(tokens) except Exception: msg = ("an int, float, string, '[' or string" + " convertable ASCII value for node id or label") unexpected(curr_token, msg) elif curr_token.value in {"NAN", "INF"}: value = float(curr_token.value) curr_token = next(tokens) else: # Otherwise error out unexpected(curr_token, "an int, float, string or '['") dct[key].append(value) def clean_dict_value(value): if not isinstance(value, list): return value if len(value) == 1: return value[0] if value[0] == LIST_START_VALUE: return value[1:] return value dct = {key: clean_dict_value(value) for key, value in dct.items()} return curr_token, dct def parse_graph(): curr_token, dct = parse_kv(next(tokens)) if curr_token.category is not None: # EOF unexpected(curr_token, "EOF") if "graph" not in dct: raise EasyGraphError("input contains no graph") graph = dct["graph"] if isinstance(graph, list): raise EasyGraphError("input contains more than one graph") return graph tokens = tokenize() graph = parse_graph() directed = graph.pop("directed", False) multigraph = graph.pop("multigraph", False) if not multigraph: G = eg.DiGraph() if directed else eg.Graph() else: G = eg.MultiDiGraph() if directed else eg.MultiGraph() graph_attr = {k: v for k, v in graph.items() if k not in ("node", "edge")} G.graph.update(graph_attr) def pop_attr(dct, category, attr, i): try: return dct.pop(attr) except KeyError as err: raise EasyGraphError( f"{category} #{i} has no {attr!r} attribute") from err nodes = graph.get("node", []) mapping = {} node_labels = set() for i, node in enumerate(nodes if isinstance(nodes, list) else [nodes]): id = pop_attr(node, "node", "id", i) if id in G: raise EasyGraphError(f"node id {id!r} is duplicated") if label is not None and label != "id": node_label = pop_attr(node, "node", label, i) if node_label in node_labels: raise EasyGraphError( f"node label {node_label!r} is duplicated") node_labels.add(node_label) mapping[id] = node_label G.add_node(id, **node) edges = graph.get("edge", []) for i, edge in enumerate(edges if isinstance(edges, list) else [edges]): source = pop_attr(edge, "edge", "source", i) target = pop_attr(edge, "edge", "target", i) if source not in G: raise EasyGraphError(f"edge #{i} has undefined source {source!r}") if target not in G: raise EasyGraphError(f"edge #{i} has undefined target {target!r}") if not multigraph: if not G.has_edge(source, target): G.add_edge(source, target, **edge) else: arrow = "->" if directed else "--" msg = f"edge #{i} ({source!r}{arrow}{target!r}) is duplicated" raise EasyGraphError(msg) else: key = edge.pop("key", None) if key is not None and G.has_edge(source, target, key): arrow = "->" if directed else "--" msg = f"edge #{i} ({source!r}{arrow}{target!r}, {key!r})" msg2 = 'Hint: If multigraph add "multigraph 1" to file header.' raise EasyGraphError(msg + " is duplicated\n" + msg2) G.add_edge(source, target, key, **edge) if label is not None and label != "id": G = eg.relabel_nodes(G, mapping) return G
def test_clustering(self): G = eg.Graph() assert list(eg.clustering(G).values()) == [] assert eg.clustering(G) == {}
def test_from_agraph_name(self): G = eg.Graph(name="test") A = eg.to_agraph(G) H = eg.from_agraph(A) assert G.name == "test"
def test_undirected(self): self.agraph_checks(eg.Graph())
def test_exceptions(self): pytest.raises(ValueError, literal_destringizer, "(") pytest.raises(ValueError, literal_destringizer, "frozenset([1, 2, 3])") pytest.raises(ValueError, literal_destringizer, literal_destringizer) pytest.raises(ValueError, literal_stringizer, frozenset([1, 2, 3])) pytest.raises(ValueError, literal_stringizer, literal_stringizer) with tempfile.TemporaryFile() as f: f.write(codecs.BOM_UTF8 + b"graph[]") f.seek(0) pytest.raises(eg.EasyGraphError, eg.read_gml, f) def assert_parse_error(gml): pytest.raises(eg.EasyGraphError, eg.parse_gml, gml) assert_parse_error(["graph [\n\n", "]"]) assert_parse_error("") assert_parse_error('Creator ""') assert_parse_error("0") assert_parse_error("graph ]") assert_parse_error("graph [ 1 ]") assert_parse_error("graph [ 1.E+2 ]") assert_parse_error('graph [ "A" ]') assert_parse_error("graph [ ] graph ]") assert_parse_error("graph [ ] graph [ ]") assert_parse_error("graph [ data [1, 2, 3] ]") assert_parse_error("graph [ node [ ] ]") assert_parse_error("graph [ node [ id 0 ] ]") eg.parse_gml('graph [ node [ id "a" ] ]', label="id") assert_parse_error( "graph [ node [ id 0 label 0 ] node [ id 0 label 1 ] ]") assert_parse_error( "graph [ node [ id 0 label 0 ] node [ id 1 label 0 ] ]") assert_parse_error("graph [ node [ id 0 label 0 ] edge [ ] ]") assert_parse_error("graph [ node [ id 0 label 0 ] edge [ source 0 ] ]") eg.parse_gml( "graph [edge [ source 0 target 0 ] node [ id 0 label 0 ] ]") assert_parse_error( "graph [ node [ id 0 label 0 ] edge [ source 1 target 0 ] ]") assert_parse_error( "graph [ node [ id 0 label 0 ] edge [ source 0 target 1 ] ]") assert_parse_error( "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] " "edge [ source 0 target 1 ] edge [ source 1 target 0 ] ]") eg.parse_gml("graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] " "edge [ source 0 target 1 ] edge [ source 1 target 0 ] " "directed 1 ]") eg.parse_gml("graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] " "edge [ source 0 target 1 ] edge [ source 0 target 1 ]" "multigraph 1 ]") eg.parse_gml( "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] " "edge [ source 0 target 1 key 0 ] edge [ source 0 target 1 ]" "multigraph 1 ]") assert_parse_error( "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] " "edge [ source 0 target 1 key 0 ] edge [ source 0 target 1 key 0 ]" "multigraph 1 ]") eg.parse_gml( "graph [ node [ id 0 label 0 ] node [ id 1 label 1 ] " "edge [ source 0 target 1 key 0 ] edge [ source 1 target 0 key 0 ]" "directed 1 multigraph 1 ]") # Tests for string convertable alphanumeric id and label values eg.parse_gml( "graph [edge [ source a target a ] node [ id a label b ] ]") eg.parse_gml("graph [ node [ id n42 label 0 ] node [ id x43 label 1 ]" "edge [ source n42 target x43 key 0 ]" "edge [ source x43 target n42 key 0 ]" "directed 1 multigraph 1 ]") assert_parse_error( "graph [edge [ source u'u\4200' target u'u\4200' ] " + "node [ id u'u\4200' label b ] ]") def assert_generate_error(*args, **kwargs): pytest.raises(eg.EasyGraphError, lambda: list(eg.generate_gml(*args, **kwargs))) G = eg.Graph() G.graph[3] = 3 assert_generate_error(G) G = eg.Graph() G.graph["3"] = 3 assert_generate_error(G) G = eg.Graph() G.graph["data"] = frozenset([1, 2, 3]) assert_generate_error(G, stringizer=literal_stringizer) G = eg.Graph() G.graph["data"] = [] assert_generate_error(G) assert_generate_error(G, stringizer=len)