Spaces:
Running
Running
""" | |
Testing | |
======= | |
General guidelines for writing good tests: | |
- doctests always assume ``import networkx as nx`` so don't add that | |
- prefer pytest fixtures over classes with setup methods. | |
- use the ``@pytest.mark.parametrize`` decorator | |
- use ``pytest.importorskip`` for numpy, scipy, pandas, and matplotlib b/c of PyPy. | |
and add the module to the relevant entries below. | |
""" | |
import os | |
import sys | |
import warnings | |
from importlib.metadata import entry_points | |
import pytest | |
import networkx | |
def pytest_addoption(parser): | |
parser.addoption( | |
"--runslow", action="store_true", default=False, help="run slow tests" | |
) | |
parser.addoption( | |
"--backend", | |
action="store", | |
default=None, | |
help="Run tests with a backend by auto-converting nx graphs to backend graphs", | |
) | |
parser.addoption( | |
"--fallback-to-nx", | |
action="store_true", | |
default=False, | |
help="Run nx function if a backend doesn't implement a dispatchable function" | |
" (use with --backend)", | |
) | |
def pytest_configure(config): | |
config.addinivalue_line("markers", "slow: mark test as slow to run") | |
backend = config.getoption("--backend") | |
if backend is None: | |
backend = os.environ.get("NETWORKX_TEST_BACKEND") | |
if backend: | |
networkx.utils.backends._dispatch._automatic_backends = [backend] | |
fallback_to_nx = config.getoption("--fallback-to-nx") | |
if not fallback_to_nx: | |
fallback_to_nx = os.environ.get("NETWORKX_FALLBACK_TO_NX") | |
networkx.utils.backends._dispatch._fallback_to_nx = bool(fallback_to_nx) | |
# nx-loopback backend is only available when testing | |
if sys.version_info < (3, 10): | |
backends = ( | |
ep for ep in entry_points()["networkx.backends"] if ep.name == "nx-loopback" | |
) | |
else: | |
backends = entry_points(name="nx-loopback", group="networkx.backends") | |
if backends: | |
networkx.utils.backends.backends["nx-loopback"] = next(iter(backends)) | |
else: | |
warnings.warn( | |
"\n\n WARNING: Mixed NetworkX configuration! \n\n" | |
" This environment has mixed configuration for networkx.\n" | |
" The test object nx-loopback is not configured correctly.\n" | |
" You should not be seeing this message.\n" | |
" Try `pip install -e .`, or change your PYTHONPATH\n" | |
" Make sure python finds the networkx repo you are testing\n\n" | |
) | |
def pytest_collection_modifyitems(config, items): | |
# Setting this to True here allows tests to be set up before dispatching | |
# any function call to a backend. | |
networkx.utils.backends._dispatch._is_testing = True | |
if automatic_backends := networkx.utils.backends._dispatch._automatic_backends: | |
# Allow pluggable backends to add markers to tests (such as skip or xfail) | |
# when running in auto-conversion test mode | |
backend = networkx.utils.backends.backends[automatic_backends[0]].load() | |
if hasattr(backend, "on_start_tests"): | |
getattr(backend, "on_start_tests")(items) | |
if config.getoption("--runslow"): | |
# --runslow given in cli: do not skip slow tests | |
return | |
skip_slow = pytest.mark.skip(reason="need --runslow option to run") | |
for item in items: | |
if "slow" in item.keywords: | |
item.add_marker(skip_slow) | |
# TODO: The warnings below need to be dealt with, but for now we silence them. | |
def set_warnings(): | |
warnings.filterwarnings( | |
"ignore", category=DeprecationWarning, message="nx.nx_pydot" | |
) | |
warnings.filterwarnings( | |
"ignore", | |
category=DeprecationWarning, | |
message="single_target_shortest_path_length will", | |
) | |
warnings.filterwarnings( | |
"ignore", | |
category=DeprecationWarning, | |
message="shortest_path for all_pairs", | |
) | |
warnings.filterwarnings( | |
"ignore", category=DeprecationWarning, message="\nforest_str is deprecated" | |
) | |
warnings.filterwarnings( | |
"ignore", category=DeprecationWarning, message="\n\nrandom_tree" | |
) | |
warnings.filterwarnings( | |
"ignore", category=DeprecationWarning, message="Edmonds has been deprecated" | |
) | |
warnings.filterwarnings( | |
"ignore", | |
category=DeprecationWarning, | |
message="MultiDiGraph_EdgeKey has been deprecated", | |
) | |
warnings.filterwarnings( | |
"ignore", category=DeprecationWarning, message="\n\nThe `normalized`" | |
) | |
warnings.filterwarnings( | |
"ignore", category=DeprecationWarning, message="function `join` is deprecated" | |
) | |
warnings.filterwarnings( | |
"ignore", | |
category=DeprecationWarning, | |
message="\n\nstrongly_connected_components_recursive", | |
) | |
def add_nx(doctest_namespace): | |
doctest_namespace["nx"] = networkx | |
# TODO: remove the try-except block when we require numpy >= 2 | |
try: | |
import numpy as np | |
np.set_printoptions(legacy="1.21") | |
except ImportError: | |
pass | |
# What dependencies are installed? | |
try: | |
import numpy | |
has_numpy = True | |
except ImportError: | |
has_numpy = False | |
try: | |
import scipy | |
has_scipy = True | |
except ImportError: | |
has_scipy = False | |
try: | |
import matplotlib | |
has_matplotlib = True | |
except ImportError: | |
has_matplotlib = False | |
try: | |
import pandas | |
has_pandas = True | |
except ImportError: | |
has_pandas = False | |
try: | |
import pygraphviz | |
has_pygraphviz = True | |
except ImportError: | |
has_pygraphviz = False | |
try: | |
import pydot | |
has_pydot = True | |
except ImportError: | |
has_pydot = False | |
try: | |
import sympy | |
has_sympy = True | |
except ImportError: | |
has_sympy = False | |
# List of files that pytest should ignore | |
collect_ignore = [] | |
needs_numpy = [ | |
"algorithms/approximation/traveling_salesman.py", | |
"algorithms/centrality/current_flow_closeness.py", | |
"algorithms/node_classification.py", | |
"algorithms/non_randomness.py", | |
"algorithms/shortest_paths/dense.py", | |
"linalg/bethehessianmatrix.py", | |
"linalg/laplacianmatrix.py", | |
"utils/misc.py", | |
"algorithms/centrality/laplacian.py", | |
] | |
needs_scipy = [ | |
"algorithms/approximation/traveling_salesman.py", | |
"algorithms/assortativity/correlation.py", | |
"algorithms/assortativity/mixing.py", | |
"algorithms/assortativity/pairs.py", | |
"algorithms/bipartite/matrix.py", | |
"algorithms/bipartite/spectral.py", | |
"algorithms/centrality/current_flow_betweenness.py", | |
"algorithms/centrality/current_flow_betweenness_subset.py", | |
"algorithms/centrality/eigenvector.py", | |
"algorithms/centrality/katz.py", | |
"algorithms/centrality/second_order.py", | |
"algorithms/centrality/subgraph_alg.py", | |
"algorithms/communicability_alg.py", | |
"algorithms/link_analysis/hits_alg.py", | |
"algorithms/link_analysis/pagerank_alg.py", | |
"algorithms/node_classification.py", | |
"algorithms/similarity.py", | |
"convert_matrix.py", | |
"drawing/layout.py", | |
"generators/spectral_graph_forge.py", | |
"linalg/algebraicconnectivity.py", | |
"linalg/attrmatrix.py", | |
"linalg/bethehessianmatrix.py", | |
"linalg/graphmatrix.py", | |
"linalg/modularitymatrix.py", | |
"linalg/spectrum.py", | |
"utils/rcm.py", | |
"algorithms/centrality/laplacian.py", | |
] | |
needs_matplotlib = ["drawing/nx_pylab.py"] | |
needs_pandas = ["convert_matrix.py"] | |
needs_pygraphviz = ["drawing/nx_agraph.py"] | |
needs_pydot = ["drawing/nx_pydot.py"] | |
needs_sympy = ["algorithms/polynomials.py"] | |
if not has_numpy: | |
collect_ignore += needs_numpy | |
if not has_scipy: | |
collect_ignore += needs_scipy | |
if not has_matplotlib: | |
collect_ignore += needs_matplotlib | |
if not has_pandas: | |
collect_ignore += needs_pandas | |
if not has_pygraphviz: | |
collect_ignore += needs_pygraphviz | |
if not has_pydot: | |
collect_ignore += needs_pydot | |
if not has_sympy: | |
collect_ignore += needs_sympy | |