aboutsummaryrefslogtreecommitdiff
path: root/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph
diff options
context:
space:
mode:
Diffstat (limited to '.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph')
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/__init__.py19
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/adjacency.py156
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/cytoscape.py178
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/node_link.py330
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/__init__.py0
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_adjacency.py78
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_cytoscape.py78
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_node_link.py175
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_tree.py48
-rw-r--r--.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tree.py137
10 files changed, 1199 insertions, 0 deletions
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/__init__.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/__init__.py
new file mode 100644
index 00000000..532c71d7
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/__init__.py
@@ -0,0 +1,19 @@
+"""
+*********
+JSON data
+*********
+Generate and parse JSON serializable data for NetworkX graphs.
+
+These formats are suitable for use with the d3.js examples https://d3js.org/
+
+The three formats that you can generate with NetworkX are:
+
+ - node-link like in the d3.js example https://bl.ocks.org/mbostock/4062045
+ - tree like in the d3.js example https://bl.ocks.org/mbostock/4063550
+ - adjacency like in the d3.js example https://bost.ocks.org/mike/miserables/
+"""
+
+from networkx.readwrite.json_graph.node_link import *
+from networkx.readwrite.json_graph.adjacency import *
+from networkx.readwrite.json_graph.tree import *
+from networkx.readwrite.json_graph.cytoscape import *
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/adjacency.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/adjacency.py
new file mode 100644
index 00000000..3b057475
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/adjacency.py
@@ -0,0 +1,156 @@
+import networkx as nx
+
+__all__ = ["adjacency_data", "adjacency_graph"]
+
+_attrs = {"id": "id", "key": "key"}
+
+
+def adjacency_data(G, attrs=_attrs):
+ """Returns data in adjacency format that is suitable for JSON serialization
+ and use in JavaScript documents.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+
+ attrs : dict
+ A dictionary that contains two keys 'id' and 'key'. The corresponding
+ values provide the attribute names for storing NetworkX-internal graph
+ data. The values should be unique. Default value:
+ :samp:`dict(id='id', key='key')`.
+
+ If some user-defined graph data use these attribute names as data keys,
+ they may be silently dropped.
+
+ Returns
+ -------
+ data : dict
+ A dictionary with adjacency formatted data.
+
+ Raises
+ ------
+ NetworkXError
+ If values in attrs are not unique.
+
+ Examples
+ --------
+ >>> from networkx.readwrite import json_graph
+ >>> G = nx.Graph([(1, 2)])
+ >>> data = json_graph.adjacency_data(G)
+
+ To serialize with json
+
+ >>> import json
+ >>> s = json.dumps(data)
+
+ Notes
+ -----
+ Graph, node, and link attributes will be written when using this format
+ but attribute keys must be strings if you want to serialize the resulting
+ data with JSON.
+
+ The default value of attrs will be changed in a future release of NetworkX.
+
+ See Also
+ --------
+ adjacency_graph, node_link_data, tree_data
+ """
+ multigraph = G.is_multigraph()
+ id_ = attrs["id"]
+ # Allow 'key' to be omitted from attrs if the graph is not a multigraph.
+ key = None if not multigraph else attrs["key"]
+ if id_ == key:
+ raise nx.NetworkXError("Attribute names are not unique.")
+ data = {}
+ data["directed"] = G.is_directed()
+ data["multigraph"] = multigraph
+ data["graph"] = list(G.graph.items())
+ data["nodes"] = []
+ data["adjacency"] = []
+ for n, nbrdict in G.adjacency():
+ data["nodes"].append({**G.nodes[n], id_: n})
+ adj = []
+ if multigraph:
+ for nbr, keys in nbrdict.items():
+ for k, d in keys.items():
+ adj.append({**d, id_: nbr, key: k})
+ else:
+ for nbr, d in nbrdict.items():
+ adj.append({**d, id_: nbr})
+ data["adjacency"].append(adj)
+ return data
+
+
+@nx._dispatchable(graphs=None, returns_graph=True)
+def adjacency_graph(data, directed=False, multigraph=True, attrs=_attrs):
+ """Returns graph from adjacency data format.
+
+ Parameters
+ ----------
+ data : dict
+ Adjacency list formatted graph data
+
+ directed : bool
+ If True, and direction not specified in data, return a directed graph.
+
+ multigraph : bool
+ If True, and multigraph not specified in data, return a multigraph.
+
+ attrs : dict
+ A dictionary that contains two keys 'id' and 'key'. The corresponding
+ values provide the attribute names for storing NetworkX-internal graph
+ data. The values should be unique. Default value:
+ :samp:`dict(id='id', key='key')`.
+
+ Returns
+ -------
+ G : NetworkX graph
+ A NetworkX graph object
+
+ Examples
+ --------
+ >>> from networkx.readwrite import json_graph
+ >>> G = nx.Graph([(1, 2)])
+ >>> data = json_graph.adjacency_data(G)
+ >>> H = json_graph.adjacency_graph(data)
+
+ Notes
+ -----
+ The default value of attrs will be changed in a future release of NetworkX.
+
+ See Also
+ --------
+ adjacency_graph, node_link_data, tree_data
+ """
+ multigraph = data.get("multigraph", multigraph)
+ directed = data.get("directed", directed)
+ if multigraph:
+ graph = nx.MultiGraph()
+ else:
+ graph = nx.Graph()
+ if directed:
+ graph = graph.to_directed()
+ id_ = attrs["id"]
+ # Allow 'key' to be omitted from attrs if the graph is not a multigraph.
+ key = None if not multigraph else attrs["key"]
+ graph.graph = dict(data.get("graph", []))
+ mapping = []
+ for d in data["nodes"]:
+ node_data = d.copy()
+ node = node_data.pop(id_)
+ mapping.append(node)
+ graph.add_node(node)
+ graph.nodes[node].update(node_data)
+ for i, d in enumerate(data["adjacency"]):
+ source = mapping[i]
+ for tdata in d:
+ target_data = tdata.copy()
+ target = target_data.pop(id_)
+ if not multigraph:
+ graph.add_edge(source, target)
+ graph[source][target].update(target_data)
+ else:
+ ky = target_data.pop(key, None)
+ graph.add_edge(source, target, key=ky)
+ graph[source][target][ky].update(target_data)
+ return graph
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/cytoscape.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/cytoscape.py
new file mode 100644
index 00000000..2f3b2176
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/cytoscape.py
@@ -0,0 +1,178 @@
+import networkx as nx
+
+__all__ = ["cytoscape_data", "cytoscape_graph"]
+
+
+def cytoscape_data(G, name="name", ident="id"):
+ """Returns data in Cytoscape JSON format (cyjs).
+
+ Parameters
+ ----------
+ G : NetworkX Graph
+ The graph to convert to cytoscape format
+ name : string
+ A string which is mapped to the 'name' node element in cyjs format.
+ Must not have the same value as `ident`.
+ ident : string
+ A string which is mapped to the 'id' node element in cyjs format.
+ Must not have the same value as `name`.
+
+ Returns
+ -------
+ data: dict
+ A dictionary with cyjs formatted data.
+
+ Raises
+ ------
+ NetworkXError
+ If the values for `name` and `ident` are identical.
+
+ See Also
+ --------
+ cytoscape_graph: convert a dictionary in cyjs format to a graph
+
+ References
+ ----------
+ .. [1] Cytoscape user's manual:
+ http://manual.cytoscape.org/en/stable/index.html
+
+ Examples
+ --------
+ >>> G = nx.path_graph(2)
+ >>> nx.cytoscape_data(G) # doctest: +SKIP
+ {'data': [],
+ 'directed': False,
+ 'multigraph': False,
+ 'elements': {'nodes': [{'data': {'id': '0', 'value': 0, 'name': '0'}},
+ {'data': {'id': '1', 'value': 1, 'name': '1'}}],
+ 'edges': [{'data': {'source': 0, 'target': 1}}]}}
+ """
+ if name == ident:
+ raise nx.NetworkXError("name and ident must be different.")
+
+ jsondata = {"data": list(G.graph.items())}
+ jsondata["directed"] = G.is_directed()
+ jsondata["multigraph"] = G.is_multigraph()
+ jsondata["elements"] = {"nodes": [], "edges": []}
+ nodes = jsondata["elements"]["nodes"]
+ edges = jsondata["elements"]["edges"]
+
+ for i, j in G.nodes.items():
+ n = {"data": j.copy()}
+ n["data"]["id"] = j.get(ident) or str(i)
+ n["data"]["value"] = i
+ n["data"]["name"] = j.get(name) or str(i)
+ nodes.append(n)
+
+ if G.is_multigraph():
+ for e in G.edges(keys=True):
+ n = {"data": G.adj[e[0]][e[1]][e[2]].copy()}
+ n["data"]["source"] = e[0]
+ n["data"]["target"] = e[1]
+ n["data"]["key"] = e[2]
+ edges.append(n)
+ else:
+ for e in G.edges():
+ n = {"data": G.adj[e[0]][e[1]].copy()}
+ n["data"]["source"] = e[0]
+ n["data"]["target"] = e[1]
+ edges.append(n)
+ return jsondata
+
+
+@nx._dispatchable(graphs=None, returns_graph=True)
+def cytoscape_graph(data, name="name", ident="id"):
+ """
+ Create a NetworkX graph from a dictionary in cytoscape JSON format.
+
+ Parameters
+ ----------
+ data : dict
+ A dictionary of data conforming to cytoscape JSON format.
+ name : string
+ A string which is mapped to the 'name' node element in cyjs format.
+ Must not have the same value as `ident`.
+ ident : string
+ A string which is mapped to the 'id' node element in cyjs format.
+ Must not have the same value as `name`.
+
+ Returns
+ -------
+ graph : a NetworkX graph instance
+ The `graph` can be an instance of `Graph`, `DiGraph`, `MultiGraph`, or
+ `MultiDiGraph` depending on the input data.
+
+ Raises
+ ------
+ NetworkXError
+ If the `name` and `ident` attributes are identical.
+
+ See Also
+ --------
+ cytoscape_data: convert a NetworkX graph to a dict in cyjs format
+
+ References
+ ----------
+ .. [1] Cytoscape user's manual:
+ http://manual.cytoscape.org/en/stable/index.html
+
+ Examples
+ --------
+ >>> data_dict = {
+ ... "data": [],
+ ... "directed": False,
+ ... "multigraph": False,
+ ... "elements": {
+ ... "nodes": [
+ ... {"data": {"id": "0", "value": 0, "name": "0"}},
+ ... {"data": {"id": "1", "value": 1, "name": "1"}},
+ ... ],
+ ... "edges": [{"data": {"source": 0, "target": 1}}],
+ ... },
+ ... }
+ >>> G = nx.cytoscape_graph(data_dict)
+ >>> G.name
+ ''
+ >>> G.nodes()
+ NodeView((0, 1))
+ >>> G.nodes(data=True)[0]
+ {'id': '0', 'value': 0, 'name': '0'}
+ >>> G.edges(data=True)
+ EdgeDataView([(0, 1, {'source': 0, 'target': 1})])
+ """
+ if name == ident:
+ raise nx.NetworkXError("name and ident must be different.")
+
+ multigraph = data.get("multigraph")
+ directed = data.get("directed")
+ if multigraph:
+ graph = nx.MultiGraph()
+ else:
+ graph = nx.Graph()
+ if directed:
+ graph = graph.to_directed()
+ graph.graph = dict(data.get("data"))
+ for d in data["elements"]["nodes"]:
+ node_data = d["data"].copy()
+ node = d["data"]["value"]
+
+ if d["data"].get(name):
+ node_data[name] = d["data"].get(name)
+ if d["data"].get(ident):
+ node_data[ident] = d["data"].get(ident)
+
+ graph.add_node(node)
+ graph.nodes[node].update(node_data)
+
+ for d in data["elements"]["edges"]:
+ edge_data = d["data"].copy()
+ sour = d["data"]["source"]
+ targ = d["data"]["target"]
+ if multigraph:
+ key = d["data"].get("key", 0)
+ graph.add_edge(sour, targ, key=key)
+ graph.edges[sour, targ, key].update(edge_data)
+ else:
+ graph.add_edge(sour, targ)
+ graph.edges[sour, targ].update(edge_data)
+ return graph
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/node_link.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/node_link.py
new file mode 100644
index 00000000..63ca9789
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/node_link.py
@@ -0,0 +1,330 @@
+import warnings
+from itertools import count
+
+import networkx as nx
+
+__all__ = ["node_link_data", "node_link_graph"]
+
+
+def _to_tuple(x):
+ """Converts lists to tuples, including nested lists.
+
+ All other non-list inputs are passed through unmodified. This function is
+ intended to be used to convert potentially nested lists from json files
+ into valid nodes.
+
+ Examples
+ --------
+ >>> _to_tuple([1, 2, [3, 4]])
+ (1, 2, (3, 4))
+ """
+ if not isinstance(x, tuple | list):
+ return x
+ return tuple(map(_to_tuple, x))
+
+
+def node_link_data(
+ G,
+ *,
+ source="source",
+ target="target",
+ name="id",
+ key="key",
+ edges=None,
+ nodes="nodes",
+ link=None,
+):
+ """Returns data in node-link format that is suitable for JSON serialization
+ and use in JavaScript documents.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+ source : string
+ A string that provides the 'source' attribute name for storing NetworkX-internal graph data.
+ target : string
+ A string that provides the 'target' attribute name for storing NetworkX-internal graph data.
+ name : string
+ A string that provides the 'name' attribute name for storing NetworkX-internal graph data.
+ key : string
+ A string that provides the 'key' attribute name for storing NetworkX-internal graph data.
+ edges : string
+ A string that provides the 'edges' attribute name for storing NetworkX-internal graph data.
+ nodes : string
+ A string that provides the 'nodes' attribute name for storing NetworkX-internal graph data.
+ link : string
+ .. deprecated:: 3.4
+
+ The `link` argument is deprecated and will be removed in version `3.6`.
+ Use the `edges` keyword instead.
+
+ A string that provides the 'edges' attribute name for storing NetworkX-internal graph data.
+
+ Returns
+ -------
+ data : dict
+ A dictionary with node-link formatted data.
+
+ Raises
+ ------
+ NetworkXError
+ If the values of 'source', 'target' and 'key' are not unique.
+
+ Examples
+ --------
+ >>> from pprint import pprint
+ >>> G = nx.Graph([("A", "B")])
+ >>> data1 = nx.node_link_data(G, edges="edges")
+ >>> pprint(data1)
+ {'directed': False,
+ 'edges': [{'source': 'A', 'target': 'B'}],
+ 'graph': {},
+ 'multigraph': False,
+ 'nodes': [{'id': 'A'}, {'id': 'B'}]}
+
+ To serialize with JSON
+
+ >>> import json
+ >>> s1 = json.dumps(data1)
+ >>> s1
+ '{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": "A"}, {"id": "B"}], "edges": [{"source": "A", "target": "B"}]}'
+
+ A graph can also be serialized by passing `node_link_data` as an encoder function.
+
+ >>> s1 = json.dumps(G, default=nx.node_link_data)
+ >>> s1
+ '{"directed": false, "multigraph": false, "graph": {}, "nodes": [{"id": "A"}, {"id": "B"}], "links": [{"source": "A", "target": "B"}]}'
+
+ The attribute names for storing NetworkX-internal graph data can
+ be specified as keyword options.
+
+ >>> H = nx.gn_graph(2)
+ >>> data2 = nx.node_link_data(
+ ... H, edges="links", source="from", target="to", nodes="vertices"
+ ... )
+ >>> pprint(data2)
+ {'directed': True,
+ 'graph': {},
+ 'links': [{'from': 1, 'to': 0}],
+ 'multigraph': False,
+ 'vertices': [{'id': 0}, {'id': 1}]}
+
+ Notes
+ -----
+ Graph, node, and link attributes are stored in this format. Note that
+ attribute keys will be converted to strings in order to comply with JSON.
+
+ Attribute 'key' is only used for multigraphs.
+
+ To use `node_link_data` in conjunction with `node_link_graph`,
+ the keyword names for the attributes must match.
+
+ See Also
+ --------
+ node_link_graph, adjacency_data, tree_data
+ """
+ # TODO: Remove between the lines when `link` deprecation expires
+ # -------------------------------------------------------------
+ if link is not None:
+ warnings.warn(
+ "Keyword argument 'link' is deprecated; use 'edges' instead",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ if edges is not None:
+ raise ValueError(
+ "Both 'edges' and 'link' are specified. Use 'edges', 'link' will be remove in a future release"
+ )
+ else:
+ edges = link
+ else:
+ if edges is None:
+ warnings.warn(
+ (
+ '\nThe default value will be `edges="edges" in NetworkX 3.6.\n\n'
+ "To make this warning go away, explicitly set the edges kwarg, e.g.:\n\n"
+ ' nx.node_link_data(G, edges="links") to preserve current behavior, or\n'
+ ' nx.node_link_data(G, edges="edges") for forward compatibility.'
+ ),
+ FutureWarning,
+ )
+ edges = "links"
+ # ------------------------------------------------------------
+
+ multigraph = G.is_multigraph()
+
+ # Allow 'key' to be omitted from attrs if the graph is not a multigraph.
+ key = None if not multigraph else key
+ if len({source, target, key}) < 3:
+ raise nx.NetworkXError("Attribute names are not unique.")
+ data = {
+ "directed": G.is_directed(),
+ "multigraph": multigraph,
+ "graph": G.graph,
+ nodes: [{**G.nodes[n], name: n} for n in G],
+ }
+ if multigraph:
+ data[edges] = [
+ {**d, source: u, target: v, key: k}
+ for u, v, k, d in G.edges(keys=True, data=True)
+ ]
+ else:
+ data[edges] = [{**d, source: u, target: v} for u, v, d in G.edges(data=True)]
+ return data
+
+
+@nx._dispatchable(graphs=None, returns_graph=True)
+def node_link_graph(
+ data,
+ directed=False,
+ multigraph=True,
+ *,
+ source="source",
+ target="target",
+ name="id",
+ key="key",
+ edges=None,
+ nodes="nodes",
+ link=None,
+):
+ """Returns graph from node-link data format.
+
+ Useful for de-serialization from JSON.
+
+ Parameters
+ ----------
+ data : dict
+ node-link formatted graph data
+
+ directed : bool
+ If True, and direction not specified in data, return a directed graph.
+
+ multigraph : bool
+ If True, and multigraph not specified in data, return a multigraph.
+
+ source : string
+ A string that provides the 'source' attribute name for storing NetworkX-internal graph data.
+ target : string
+ A string that provides the 'target' attribute name for storing NetworkX-internal graph data.
+ name : string
+ A string that provides the 'name' attribute name for storing NetworkX-internal graph data.
+ key : string
+ A string that provides the 'key' attribute name for storing NetworkX-internal graph data.
+ edges : string
+ A string that provides the 'edges' attribute name for storing NetworkX-internal graph data.
+ nodes : string
+ A string that provides the 'nodes' attribute name for storing NetworkX-internal graph data.
+ link : string
+ .. deprecated:: 3.4
+
+ The `link` argument is deprecated and will be removed in version `3.6`.
+ Use the `edges` keyword instead.
+
+ A string that provides the 'edges' attribute name for storing NetworkX-internal graph data.
+
+ Returns
+ -------
+ G : NetworkX graph
+ A NetworkX graph object
+
+ Examples
+ --------
+
+ Create data in node-link format by converting a graph.
+
+ >>> from pprint import pprint
+ >>> G = nx.Graph([("A", "B")])
+ >>> data = nx.node_link_data(G, edges="edges")
+ >>> pprint(data)
+ {'directed': False,
+ 'edges': [{'source': 'A', 'target': 'B'}],
+ 'graph': {},
+ 'multigraph': False,
+ 'nodes': [{'id': 'A'}, {'id': 'B'}]}
+
+ Revert data in node-link format to a graph.
+
+ >>> H = nx.node_link_graph(data, edges="edges")
+ >>> print(H.edges)
+ [('A', 'B')]
+
+ To serialize and deserialize a graph with JSON,
+
+ >>> import json
+ >>> d = json.dumps(nx.node_link_data(G, edges="edges"))
+ >>> H = nx.node_link_graph(json.loads(d), edges="edges")
+ >>> print(G.edges, H.edges)
+ [('A', 'B')] [('A', 'B')]
+
+
+ Notes
+ -----
+ Attribute 'key' is only used for multigraphs.
+
+ To use `node_link_data` in conjunction with `node_link_graph`,
+ the keyword names for the attributes must match.
+
+ See Also
+ --------
+ node_link_data, adjacency_data, tree_data
+ """
+ # TODO: Remove between the lines when `link` deprecation expires
+ # -------------------------------------------------------------
+ if link is not None:
+ warnings.warn(
+ "Keyword argument 'link' is deprecated; use 'edges' instead",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ if edges is not None:
+ raise ValueError(
+ "Both 'edges' and 'link' are specified. Use 'edges', 'link' will be remove in a future release"
+ )
+ else:
+ edges = link
+ else:
+ if edges is None:
+ warnings.warn(
+ (
+ '\nThe default value will be changed to `edges="edges" in NetworkX 3.6.\n\n'
+ "To make this warning go away, explicitly set the edges kwarg, e.g.:\n\n"
+ ' nx.node_link_graph(data, edges="links") to preserve current behavior, or\n'
+ ' nx.node_link_graph(data, edges="edges") for forward compatibility.'
+ ),
+ FutureWarning,
+ )
+ edges = "links"
+ # -------------------------------------------------------------
+
+ multigraph = data.get("multigraph", multigraph)
+ directed = data.get("directed", directed)
+ if multigraph:
+ graph = nx.MultiGraph()
+ else:
+ graph = nx.Graph()
+ if directed:
+ graph = graph.to_directed()
+
+ # Allow 'key' to be omitted from attrs if the graph is not a multigraph.
+ key = None if not multigraph else key
+ graph.graph = data.get("graph", {})
+ c = count()
+ for d in data[nodes]:
+ node = _to_tuple(d.get(name, next(c)))
+ nodedata = {str(k): v for k, v in d.items() if k != name}
+ graph.add_node(node, **nodedata)
+ for d in data[edges]:
+ src = tuple(d[source]) if isinstance(d[source], list) else d[source]
+ tgt = tuple(d[target]) if isinstance(d[target], list) else d[target]
+ if not multigraph:
+ edgedata = {str(k): v for k, v in d.items() if k != source and k != target}
+ graph.add_edge(src, tgt, **edgedata)
+ else:
+ ky = d.get(key, None)
+ edgedata = {
+ str(k): v
+ for k, v in d.items()
+ if k != source and k != target and k != key
+ }
+ graph.add_edge(src, tgt, ky, **edgedata)
+ return graph
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/__init__.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/__init__.py
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/__init__.py
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_adjacency.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_adjacency.py
new file mode 100644
index 00000000..37506382
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_adjacency.py
@@ -0,0 +1,78 @@
+import copy
+import json
+
+import pytest
+
+import networkx as nx
+from networkx.readwrite.json_graph import adjacency_data, adjacency_graph
+from networkx.utils import graphs_equal
+
+
+class TestAdjacency:
+ def test_graph(self):
+ G = nx.path_graph(4)
+ H = adjacency_graph(adjacency_data(G))
+ assert graphs_equal(G, H)
+
+ def test_graph_attributes(self):
+ G = nx.path_graph(4)
+ G.add_node(1, color="red")
+ G.add_edge(1, 2, width=7)
+ G.graph["foo"] = "bar"
+ G.graph[1] = "one"
+
+ H = adjacency_graph(adjacency_data(G))
+ assert graphs_equal(G, H)
+ assert H.graph["foo"] == "bar"
+ assert H.nodes[1]["color"] == "red"
+ assert H[1][2]["width"] == 7
+
+ d = json.dumps(adjacency_data(G))
+ H = adjacency_graph(json.loads(d))
+ assert graphs_equal(G, H)
+ assert H.graph["foo"] == "bar"
+ assert H.graph[1] == "one"
+ assert H.nodes[1]["color"] == "red"
+ assert H[1][2]["width"] == 7
+
+ def test_digraph(self):
+ G = nx.DiGraph()
+ nx.add_path(G, [1, 2, 3])
+ H = adjacency_graph(adjacency_data(G))
+ assert H.is_directed()
+ assert graphs_equal(G, H)
+
+ def test_multidigraph(self):
+ G = nx.MultiDiGraph()
+ nx.add_path(G, [1, 2, 3])
+ H = adjacency_graph(adjacency_data(G))
+ assert H.is_directed()
+ assert H.is_multigraph()
+ assert graphs_equal(G, H)
+
+ def test_multigraph(self):
+ G = nx.MultiGraph()
+ G.add_edge(1, 2, key="first")
+ G.add_edge(1, 2, key="second", color="blue")
+ H = adjacency_graph(adjacency_data(G))
+ assert graphs_equal(G, H)
+ assert H[1][2]["second"]["color"] == "blue"
+
+ def test_input_data_is_not_modified_when_building_graph(self):
+ G = nx.path_graph(4)
+ input_data = adjacency_data(G)
+ orig_data = copy.deepcopy(input_data)
+ # Ensure input is unmodified by deserialisation
+ assert graphs_equal(G, adjacency_graph(input_data))
+ assert input_data == orig_data
+
+ def test_adjacency_form_json_serialisable(self):
+ G = nx.path_graph(4)
+ H = adjacency_graph(json.loads(json.dumps(adjacency_data(G))))
+ assert graphs_equal(G, H)
+
+ def test_exception(self):
+ with pytest.raises(nx.NetworkXError):
+ G = nx.MultiDiGraph()
+ attrs = {"id": "node", "key": "node"}
+ adjacency_data(G, attrs)
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_cytoscape.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_cytoscape.py
new file mode 100644
index 00000000..5d47f21f
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_cytoscape.py
@@ -0,0 +1,78 @@
+import copy
+import json
+
+import pytest
+
+import networkx as nx
+from networkx.readwrite.json_graph import cytoscape_data, cytoscape_graph
+
+
+def test_graph():
+ G = nx.path_graph(4)
+ H = cytoscape_graph(cytoscape_data(G))
+ assert nx.is_isomorphic(G, H)
+
+
+def test_input_data_is_not_modified_when_building_graph():
+ G = nx.path_graph(4)
+ input_data = cytoscape_data(G)
+ orig_data = copy.deepcopy(input_data)
+ # Ensure input is unmodified by cytoscape_graph (gh-4173)
+ cytoscape_graph(input_data)
+ assert input_data == orig_data
+
+
+def test_graph_attributes():
+ G = nx.path_graph(4)
+ G.add_node(1, color="red")
+ G.add_edge(1, 2, width=7)
+ G.graph["foo"] = "bar"
+ G.graph[1] = "one"
+ G.add_node(3, name="node", id="123")
+
+ H = cytoscape_graph(cytoscape_data(G))
+ assert H.graph["foo"] == "bar"
+ assert H.nodes[1]["color"] == "red"
+ assert H[1][2]["width"] == 7
+ assert H.nodes[3]["name"] == "node"
+ assert H.nodes[3]["id"] == "123"
+
+ d = json.dumps(cytoscape_data(G))
+ H = cytoscape_graph(json.loads(d))
+ assert H.graph["foo"] == "bar"
+ assert H.graph[1] == "one"
+ assert H.nodes[1]["color"] == "red"
+ assert H[1][2]["width"] == 7
+ assert H.nodes[3]["name"] == "node"
+ assert H.nodes[3]["id"] == "123"
+
+
+def test_digraph():
+ G = nx.DiGraph()
+ nx.add_path(G, [1, 2, 3])
+ H = cytoscape_graph(cytoscape_data(G))
+ assert H.is_directed()
+ assert nx.is_isomorphic(G, H)
+
+
+def test_multidigraph():
+ G = nx.MultiDiGraph()
+ nx.add_path(G, [1, 2, 3])
+ H = cytoscape_graph(cytoscape_data(G))
+ assert H.is_directed()
+ assert H.is_multigraph()
+
+
+def test_multigraph():
+ G = nx.MultiGraph()
+ G.add_edge(1, 2, key="first")
+ G.add_edge(1, 2, key="second", color="blue")
+ H = cytoscape_graph(cytoscape_data(G))
+ assert nx.is_isomorphic(G, H)
+ assert H[1][2]["second"]["color"] == "blue"
+
+
+def test_exception():
+ with pytest.raises(nx.NetworkXError):
+ G = nx.MultiDiGraph()
+ cytoscape_data(G, name="foo", ident="foo")
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_node_link.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_node_link.py
new file mode 100644
index 00000000..f903f606
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_node_link.py
@@ -0,0 +1,175 @@
+import json
+
+import pytest
+
+import networkx as nx
+from networkx.readwrite.json_graph import node_link_data, node_link_graph
+
+
+def test_node_link_edges_default_future_warning():
+ "Test FutureWarning is raised when `edges=None` in node_link_data and node_link_graph"
+ G = nx.Graph([(1, 2)])
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ data = nx.node_link_data(G) # edges=None, the default
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ H = nx.node_link_graph(data) # edges=None, the default
+
+
+def test_node_link_deprecated_link_param():
+ G = nx.Graph([(1, 2)])
+ with pytest.warns(DeprecationWarning, match="Keyword argument 'link'"):
+ data = nx.node_link_data(G, link="links")
+ with pytest.warns(DeprecationWarning, match="Keyword argument 'link'"):
+ H = nx.node_link_graph(data, link="links")
+
+
+class TestNodeLink:
+ # TODO: To be removed when signature change complete
+ def test_custom_attrs_dep(self):
+ G = nx.path_graph(4)
+ G.add_node(1, color="red")
+ G.add_edge(1, 2, width=7)
+ G.graph[1] = "one"
+ G.graph["foo"] = "bar"
+
+ attrs = {
+ "source": "c_source",
+ "target": "c_target",
+ "name": "c_id",
+ "key": "c_key",
+ "link": "c_links",
+ }
+
+ H = node_link_graph(node_link_data(G, **attrs), multigraph=False, **attrs)
+ assert nx.is_isomorphic(G, H)
+ assert H.graph["foo"] == "bar"
+ assert H.nodes[1]["color"] == "red"
+ assert H[1][2]["width"] == 7
+
+ # provide only a partial dictionary of keywords.
+ # This is similar to an example in the doc string
+ attrs = {
+ "link": "c_links",
+ "source": "c_source",
+ "target": "c_target",
+ }
+ H = node_link_graph(node_link_data(G, **attrs), multigraph=False, **attrs)
+ assert nx.is_isomorphic(G, H)
+ assert H.graph["foo"] == "bar"
+ assert H.nodes[1]["color"] == "red"
+ assert H[1][2]["width"] == 7
+
+ def test_exception_dep(self):
+ G = nx.MultiDiGraph()
+ with pytest.raises(nx.NetworkXError):
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ node_link_data(G, name="node", source="node", target="node", key="node")
+
+ def test_graph(self):
+ G = nx.path_graph(4)
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ H = node_link_graph(node_link_data(G))
+ assert nx.is_isomorphic(G, H)
+
+ def test_graph_attributes(self):
+ G = nx.path_graph(4)
+ G.add_node(1, color="red")
+ G.add_edge(1, 2, width=7)
+ G.graph[1] = "one"
+ G.graph["foo"] = "bar"
+
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ H = node_link_graph(node_link_data(G))
+ assert H.graph["foo"] == "bar"
+ assert H.nodes[1]["color"] == "red"
+ assert H[1][2]["width"] == 7
+
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ d = json.dumps(node_link_data(G))
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ H = node_link_graph(json.loads(d))
+ assert H.graph["foo"] == "bar"
+ assert H.graph["1"] == "one"
+ assert H.nodes[1]["color"] == "red"
+ assert H[1][2]["width"] == 7
+
+ def test_digraph(self):
+ G = nx.DiGraph()
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ H = node_link_graph(node_link_data(G))
+ assert H.is_directed()
+
+ def test_multigraph(self):
+ G = nx.MultiGraph()
+ G.add_edge(1, 2, key="first")
+ G.add_edge(1, 2, key="second", color="blue")
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ H = node_link_graph(node_link_data(G))
+ assert nx.is_isomorphic(G, H)
+ assert H[1][2]["second"]["color"] == "blue"
+
+ def test_graph_with_tuple_nodes(self):
+ G = nx.Graph()
+ G.add_edge((0, 0), (1, 0), color=[255, 255, 0])
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ d = node_link_data(G)
+ dumped_d = json.dumps(d)
+ dd = json.loads(dumped_d)
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ H = node_link_graph(dd)
+ assert H.nodes[(0, 0)] == G.nodes[(0, 0)]
+ assert H[(0, 0)][(1, 0)]["color"] == [255, 255, 0]
+
+ def test_unicode_keys(self):
+ q = "qualité"
+ G = nx.Graph()
+ G.add_node(1, **{q: q})
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ s = node_link_data(G)
+ output = json.dumps(s, ensure_ascii=False)
+ data = json.loads(output)
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ H = node_link_graph(data)
+ assert H.nodes[1][q] == q
+
+ def test_exception(self):
+ G = nx.MultiDiGraph()
+ attrs = {"name": "node", "source": "node", "target": "node", "key": "node"}
+ with pytest.raises(nx.NetworkXError):
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ node_link_data(G, **attrs)
+
+ def test_string_ids(self):
+ q = "qualité"
+ G = nx.DiGraph()
+ G.add_node("A")
+ G.add_node(q)
+ G.add_edge("A", q)
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ data = node_link_data(G)
+ assert data["links"][0]["source"] == "A"
+ assert data["links"][0]["target"] == q
+ with pytest.warns(FutureWarning, match="\nThe default value will be"):
+ H = node_link_graph(data)
+ assert nx.is_isomorphic(G, H)
+
+ def test_custom_attrs(self):
+ G = nx.path_graph(4)
+ G.add_node(1, color="red")
+ G.add_edge(1, 2, width=7)
+ G.graph[1] = "one"
+ G.graph["foo"] = "bar"
+
+ attrs = {
+ "source": "c_source",
+ "target": "c_target",
+ "name": "c_id",
+ "key": "c_key",
+ "link": "c_links",
+ }
+
+ H = node_link_graph(node_link_data(G, **attrs), multigraph=False, **attrs)
+ assert nx.is_isomorphic(G, H)
+ assert H.graph["foo"] == "bar"
+ assert H.nodes[1]["color"] == "red"
+ assert H[1][2]["width"] == 7
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_tree.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_tree.py
new file mode 100644
index 00000000..643a14d8
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tests/test_tree.py
@@ -0,0 +1,48 @@
+import json
+
+import pytest
+
+import networkx as nx
+from networkx.readwrite.json_graph import tree_data, tree_graph
+
+
+def test_graph():
+ G = nx.DiGraph()
+ G.add_nodes_from([1, 2, 3], color="red")
+ G.add_edge(1, 2, foo=7)
+ G.add_edge(1, 3, foo=10)
+ G.add_edge(3, 4, foo=10)
+ H = tree_graph(tree_data(G, 1))
+ assert nx.is_isomorphic(G, H)
+
+
+def test_graph_attributes():
+ G = nx.DiGraph()
+ G.add_nodes_from([1, 2, 3], color="red")
+ G.add_edge(1, 2, foo=7)
+ G.add_edge(1, 3, foo=10)
+ G.add_edge(3, 4, foo=10)
+ H = tree_graph(tree_data(G, 1))
+ assert H.nodes[1]["color"] == "red"
+
+ d = json.dumps(tree_data(G, 1))
+ H = tree_graph(json.loads(d))
+ assert H.nodes[1]["color"] == "red"
+
+
+def test_exceptions():
+ with pytest.raises(TypeError, match="is not a tree."):
+ G = nx.complete_graph(3)
+ tree_data(G, 0)
+ with pytest.raises(TypeError, match="is not directed."):
+ G = nx.path_graph(3)
+ tree_data(G, 0)
+ with pytest.raises(TypeError, match="is not weakly connected."):
+ G = nx.path_graph(3, create_using=nx.DiGraph)
+ G.add_edge(2, 0)
+ G.add_node(3)
+ tree_data(G, 0)
+ with pytest.raises(nx.NetworkXError, match="must be different."):
+ G = nx.MultiDiGraph()
+ G.add_node(0)
+ tree_data(G, 0, ident="node", children="node")
diff --git a/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tree.py b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tree.py
new file mode 100644
index 00000000..22b07b09
--- /dev/null
+++ b/.venv/lib/python3.12/site-packages/networkx/readwrite/json_graph/tree.py
@@ -0,0 +1,137 @@
+from itertools import chain
+
+import networkx as nx
+
+__all__ = ["tree_data", "tree_graph"]
+
+
+def tree_data(G, root, ident="id", children="children"):
+ """Returns data in tree format that is suitable for JSON serialization
+ and use in JavaScript documents.
+
+ Parameters
+ ----------
+ G : NetworkX graph
+ G must be an oriented tree
+
+ root : node
+ The root of the tree
+
+ ident : string
+ Attribute name for storing NetworkX-internal graph data. `ident` must
+ have a different value than `children`. The default is 'id'.
+
+ children : string
+ Attribute name for storing NetworkX-internal graph data. `children`
+ must have a different value than `ident`. The default is 'children'.
+
+ Returns
+ -------
+ data : dict
+ A dictionary with node-link formatted data.
+
+ Raises
+ ------
+ NetworkXError
+ If `children` and `ident` attributes are identical.
+
+ Examples
+ --------
+ >>> from networkx.readwrite import json_graph
+ >>> G = nx.DiGraph([(1, 2)])
+ >>> data = json_graph.tree_data(G, root=1)
+
+ To serialize with json
+
+ >>> import json
+ >>> s = json.dumps(data)
+
+ Notes
+ -----
+ Node attributes are stored in this format but keys
+ for attributes must be strings if you want to serialize with JSON.
+
+ Graph and edge attributes are not stored.
+
+ See Also
+ --------
+ tree_graph, node_link_data, adjacency_data
+ """
+ if G.number_of_nodes() != G.number_of_edges() + 1:
+ raise TypeError("G is not a tree.")
+ if not G.is_directed():
+ raise TypeError("G is not directed.")
+ if not nx.is_weakly_connected(G):
+ raise TypeError("G is not weakly connected.")
+
+ if ident == children:
+ raise nx.NetworkXError("The values for `id` and `children` must be different.")
+
+ def add_children(n, G):
+ nbrs = G[n]
+ if len(nbrs) == 0:
+ return []
+ children_ = []
+ for child in nbrs:
+ d = {**G.nodes[child], ident: child}
+ c = add_children(child, G)
+ if c:
+ d[children] = c
+ children_.append(d)
+ return children_
+
+ return {**G.nodes[root], ident: root, children: add_children(root, G)}
+
+
+@nx._dispatchable(graphs=None, returns_graph=True)
+def tree_graph(data, ident="id", children="children"):
+ """Returns graph from tree data format.
+
+ Parameters
+ ----------
+ data : dict
+ Tree formatted graph data
+
+ ident : string
+ Attribute name for storing NetworkX-internal graph data. `ident` must
+ have a different value than `children`. The default is 'id'.
+
+ children : string
+ Attribute name for storing NetworkX-internal graph data. `children`
+ must have a different value than `ident`. The default is 'children'.
+
+ Returns
+ -------
+ G : NetworkX DiGraph
+
+ Examples
+ --------
+ >>> from networkx.readwrite import json_graph
+ >>> G = nx.DiGraph([(1, 2)])
+ >>> data = json_graph.tree_data(G, root=1)
+ >>> H = json_graph.tree_graph(data)
+
+ See Also
+ --------
+ tree_data, node_link_data, adjacency_data
+ """
+ graph = nx.DiGraph()
+
+ def add_children(parent, children_):
+ for data in children_:
+ child = data[ident]
+ graph.add_edge(parent, child)
+ grandchildren = data.get(children, [])
+ if grandchildren:
+ add_children(child, grandchildren)
+ nodedata = {
+ str(k): v for k, v in data.items() if k != ident and k != children
+ }
+ graph.add_node(child, **nodedata)
+
+ root = data[ident]
+ children_ = data.get(children, [])
+ nodedata = {str(k): v for k, v in data.items() if k != ident and k != children}
+ graph.add_node(root, **nodedata)
+ add_children(root, children_)
+ return graph