1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
|
import pickle
import pytest
import networkx as nx
sp = pytest.importorskip("scipy")
pytest.importorskip("numpy")
def test_dispatch_kwds_vs_args():
G = nx.path_graph(4)
nx.pagerank(G)
nx.pagerank(G=G)
with pytest.raises(TypeError):
nx.pagerank()
def test_pickle():
count = 0
for name, func in nx.utils.backends._registered_algorithms.items():
pickled = pickle.dumps(func.__wrapped__)
assert pickle.loads(pickled) is func.__wrapped__
try:
# Some functions can't be pickled, but it's not b/c of _dispatchable
pickled = pickle.dumps(func)
except pickle.PicklingError:
continue
assert pickle.loads(pickled) is func
count += 1
assert count > 0
assert pickle.loads(pickle.dumps(nx.inverse_line_graph)) is nx.inverse_line_graph
@pytest.mark.skipif(
"not nx.config.backend_priority.algos "
"or nx.config.backend_priority.algos[0] != 'nx_loopback'"
)
def test_graph_converter_needs_backend():
# When testing, `nx.from_scipy_sparse_array` will *always* call the backend
# implementation if it's implemented. If `backend=` isn't given, then the result
# will be converted back to NetworkX via `convert_to_nx`.
# If not testing, then calling `nx.from_scipy_sparse_array` w/o `backend=` will
# always call the original version. `backend=` is *required* to call the backend.
from networkx.classes.tests.dispatch_interface import (
LoopbackBackendInterface,
LoopbackGraph,
)
A = sp.sparse.coo_array([[0, 3, 2], [3, 0, 1], [2, 1, 0]])
side_effects = []
def from_scipy_sparse_array(self, *args, **kwargs):
side_effects.append(1) # Just to prove this was called
return self.convert_from_nx(
self.__getattr__("from_scipy_sparse_array")(*args, **kwargs),
preserve_edge_attrs=True,
preserve_node_attrs=True,
preserve_graph_attrs=True,
)
@staticmethod
def convert_to_nx(obj, *, name=None):
if type(obj) is nx.Graph:
return obj
return nx.Graph(obj)
# *This mutates LoopbackBackendInterface!*
orig_convert_to_nx = LoopbackBackendInterface.convert_to_nx
LoopbackBackendInterface.convert_to_nx = convert_to_nx
LoopbackBackendInterface.from_scipy_sparse_array = from_scipy_sparse_array
try:
assert side_effects == []
assert type(nx.from_scipy_sparse_array(A)) is nx.Graph
assert side_effects == [1]
assert (
type(nx.from_scipy_sparse_array(A, backend="nx_loopback")) is LoopbackGraph
)
assert side_effects == [1, 1]
# backend="networkx" is default implementation
assert type(nx.from_scipy_sparse_array(A, backend="networkx")) is nx.Graph
assert side_effects == [1, 1]
finally:
LoopbackBackendInterface.convert_to_nx = staticmethod(orig_convert_to_nx)
del LoopbackBackendInterface.from_scipy_sparse_array
with pytest.raises(ImportError, match="backend is not installed"):
nx.from_scipy_sparse_array(A, backend="bad-backend-name")
@pytest.mark.skipif(
"not nx.config.backend_priority.algos "
"or nx.config.backend_priority.algos[0] != 'nx_loopback'"
)
def test_networkx_backend():
"""Test using `backend="networkx"` in a dispatchable function."""
# (Implementing this test is harder than it should be)
from networkx.classes.tests.dispatch_interface import (
LoopbackBackendInterface,
LoopbackGraph,
)
G = LoopbackGraph()
G.add_edges_from([(0, 1), (1, 2), (1, 3), (2, 4)])
@staticmethod
def convert_to_nx(obj, *, name=None):
if isinstance(obj, LoopbackGraph):
new_graph = nx.Graph()
new_graph.__dict__.update(obj.__dict__)
return new_graph
return obj
# *This mutates LoopbackBackendInterface!*
# This uses the same trick as in the previous test.
orig_convert_to_nx = LoopbackBackendInterface.convert_to_nx
LoopbackBackendInterface.convert_to_nx = convert_to_nx
try:
G2 = nx.ego_graph(G, 0, backend="networkx")
assert type(G2) is nx.Graph
finally:
LoopbackBackendInterface.convert_to_nx = staticmethod(orig_convert_to_nx)
def test_dispatchable_are_functions():
assert type(nx.pagerank) is type(nx.pagerank.orig_func)
@pytest.mark.skipif("not nx.utils.backends.backends")
def test_mixing_backend_graphs():
from networkx.classes.tests import dispatch_interface
G = nx.Graph()
G.add_edge(1, 2)
G.add_edge(2, 3)
H = nx.Graph()
H.add_edge(2, 3)
rv = nx.intersection(G, H)
assert set(nx.intersection(G, H)) == {2, 3}
G2 = dispatch_interface.convert(G)
H2 = dispatch_interface.convert(H)
if "nx_loopback" in nx.config.backend_priority:
# Auto-convert
assert set(nx.intersection(G2, H)) == {2, 3}
assert set(nx.intersection(G, H2)) == {2, 3}
elif not nx.config.backend_priority and "nx_loopback" not in nx.config.backends:
# G2 and H2 are backend objects for a backend that is not registered!
with pytest.raises(ImportError, match="backend is not installed"):
nx.intersection(G2, H)
with pytest.raises(ImportError, match="backend is not installed"):
nx.intersection(G, H2)
# It would be nice to test passing graphs from *different* backends,
# but we are not set up to do this yet.
def test_bad_backend_name():
"""Using `backend=` raises with unknown backend even if there are no backends."""
with pytest.raises(
ImportError, match="'this_backend_does_not_exist' backend is not installed"
):
nx.null_graph(backend="this_backend_does_not_exist")
def test_fallback_to_nx():
with pytest.warns(DeprecationWarning, match="_fallback_to_nx"):
# Check as class property
assert nx._dispatchable._fallback_to_nx == nx.config.fallback_to_nx
# Check as instance property
assert nx.pagerank.__wrapped__._fallback_to_nx == nx.config.fallback_to_nx
|