| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- import collections
- import pickle
- import pytest
- import networkx as nx
- from networkx.utils.configs import BackendPriorities, Config
- # Define this at module level so we can test pickling
- class ExampleConfig(Config):
- """Example configuration."""
- x: int
- y: str
- def _on_setattr(self, key, value):
- if key == "x" and value <= 0:
- raise ValueError("x must be positive")
- if key == "y" and not isinstance(value, str):
- raise TypeError("y must be a str")
- return value
- class EmptyConfig(Config):
- pass
- @pytest.mark.parametrize("cfg", [EmptyConfig(), Config()])
- def test_config_empty(cfg):
- assert dir(cfg) == []
- with pytest.raises(AttributeError):
- cfg.x = 1
- with pytest.raises(KeyError):
- cfg["x"] = 1
- with pytest.raises(AttributeError):
- cfg.x
- with pytest.raises(KeyError):
- cfg["x"]
- assert len(cfg) == 0
- assert "x" not in cfg
- assert cfg == cfg
- assert cfg.get("x", 2) == 2
- assert set(cfg.keys()) == set()
- assert set(cfg.values()) == set()
- assert set(cfg.items()) == set()
- cfg2 = pickle.loads(pickle.dumps(cfg))
- assert cfg == cfg2
- assert isinstance(cfg, collections.abc.Collection)
- assert isinstance(cfg, collections.abc.Mapping)
- def test_config_subclass():
- with pytest.raises(TypeError, match="missing 2 required keyword-only"):
- ExampleConfig()
- with pytest.raises(ValueError, match="x must be positive"):
- ExampleConfig(x=0, y="foo")
- with pytest.raises(TypeError, match="unexpected keyword"):
- ExampleConfig(x=1, y="foo", z="bad config")
- with pytest.raises(TypeError, match="unexpected keyword"):
- EmptyConfig(z="bad config")
- cfg = ExampleConfig(x=1, y="foo")
- assert cfg.x == 1
- assert cfg["x"] == 1
- assert cfg["y"] == "foo"
- assert cfg.y == "foo"
- assert "x" in cfg
- assert "y" in cfg
- assert "z" not in cfg
- assert len(cfg) == 2
- assert set(iter(cfg)) == {"x", "y"}
- assert set(cfg.keys()) == {"x", "y"}
- assert set(cfg.values()) == {1, "foo"}
- assert set(cfg.items()) == {("x", 1), ("y", "foo")}
- assert dir(cfg) == ["x", "y"]
- cfg.x = 2
- cfg["y"] = "bar"
- assert cfg["x"] == 2
- assert cfg.y == "bar"
- with pytest.raises(TypeError, match="can't be deleted"):
- del cfg.x
- with pytest.raises(TypeError, match="can't be deleted"):
- del cfg["y"]
- assert cfg.x == 2
- assert cfg == cfg
- assert cfg == ExampleConfig(x=2, y="bar")
- assert cfg != ExampleConfig(x=3, y="baz")
- assert cfg != Config(x=2, y="bar")
- with pytest.raises(TypeError, match="y must be a str"):
- cfg["y"] = 5
- with pytest.raises(ValueError, match="x must be positive"):
- cfg.x = -5
- assert cfg.get("x", 10) == 2
- with pytest.raises(AttributeError):
- cfg.z = 5
- with pytest.raises(KeyError):
- cfg["z"] = 5
- with pytest.raises(AttributeError):
- cfg.z
- with pytest.raises(KeyError):
- cfg["z"]
- cfg2 = pickle.loads(pickle.dumps(cfg))
- assert cfg == cfg2
- assert cfg.__doc__ == "Example configuration."
- assert cfg2.__doc__ == "Example configuration."
- def test_config_defaults():
- class DefaultConfig(Config):
- x: int = 0
- y: int
- cfg = DefaultConfig(y=1)
- assert cfg.x == 0
- cfg = DefaultConfig(x=2, y=1)
- assert cfg.x == 2
- def test_nxconfig():
- assert isinstance(nx.config.backend_priority, BackendPriorities)
- assert isinstance(nx.config.backend_priority.algos, list)
- assert isinstance(nx.config.backends, Config)
- with pytest.raises(TypeError, match="must be a list of backend names"):
- nx.config.backend_priority.algos = "nx_loopback"
- with pytest.raises(ValueError, match="Unknown backend when setting"):
- nx.config.backend_priority.algos = ["this_almost_certainly_is_not_a_backend"]
- with pytest.raises(TypeError, match="must be a Config of backend configs"):
- nx.config.backends = {}
- with pytest.raises(TypeError, match="must be a Config of backend configs"):
- nx.config.backends = Config(plausible_backend_name={})
- with pytest.raises(ValueError, match="Unknown backend when setting"):
- nx.config.backends = Config(this_almost_certainly_is_not_a_backend=Config())
- with pytest.raises(TypeError, match="must be True or False"):
- nx.config.cache_converted_graphs = "bad value"
- with pytest.raises(TypeError, match="must be a set of "):
- nx.config.warnings_to_ignore = 7
- with pytest.raises(ValueError, match="Unknown warning "):
- nx.config.warnings_to_ignore = {"bad value"}
- prev = nx.config.backend_priority
- try:
- nx.config.backend_priority = ["networkx"]
- assert isinstance(nx.config.backend_priority, BackendPriorities)
- assert nx.config.backend_priority.algos == ["networkx"]
- finally:
- nx.config.backend_priority = prev
- def test_nxconfig_context():
- # We do some special handling so that `nx.config.backend_priority = val`
- # actually does `nx.config.backend_priority.algos = val`.
- orig = nx.config.backend_priority.algos
- val = [] if orig else ["networkx"]
- assert orig != val
- assert nx.config.backend_priority.algos != val
- with nx.config(backend_priority=val):
- assert nx.config.backend_priority.algos == val
- assert nx.config.backend_priority.algos == orig
- with nx.config.backend_priority(algos=val):
- assert nx.config.backend_priority.algos == val
- assert nx.config.backend_priority.algos == orig
- bad = ["bad-backend"]
- with pytest.raises(ValueError, match="Unknown backend"):
- nx.config.backend_priority = bad
- with pytest.raises(ValueError, match="Unknown backend"):
- with nx.config(backend_priority=bad):
- pass
- with pytest.raises(ValueError, match="Unknown backend"):
- with nx.config.backend_priority(algos=bad):
- pass
- def test_not_strict():
- class FlexibleConfig(Config, strict=False):
- x: int
- cfg = FlexibleConfig(x=1)
- assert "_strict" not in cfg
- assert len(cfg) == 1
- assert list(cfg) == ["x"]
- assert list(cfg.keys()) == ["x"]
- assert list(cfg.values()) == [1]
- assert list(cfg.items()) == [("x", 1)]
- assert cfg.x == 1
- assert cfg["x"] == 1
- assert "x" in cfg
- assert hasattr(cfg, "x")
- assert "FlexibleConfig(x=1)" in repr(cfg)
- assert cfg == FlexibleConfig(x=1)
- del cfg.x
- assert "FlexibleConfig()" in repr(cfg)
- assert len(cfg) == 0
- assert not hasattr(cfg, "x")
- assert "x" not in cfg
- assert not hasattr(cfg, "y")
- assert "y" not in cfg
- cfg.y = 2
- assert len(cfg) == 1
- assert list(cfg) == ["y"]
- assert list(cfg.keys()) == ["y"]
- assert list(cfg.values()) == [2]
- assert list(cfg.items()) == [("y", 2)]
- assert cfg.y == 2
- assert cfg["y"] == 2
- assert hasattr(cfg, "y")
- assert "y" in cfg
- del cfg["y"]
- assert len(cfg) == 0
- assert list(cfg) == []
- with pytest.raises(AttributeError, match="y"):
- del cfg.y
- with pytest.raises(KeyError, match="y"):
- del cfg["y"]
- with pytest.raises(TypeError, match="missing 1 required keyword-only"):
- FlexibleConfig()
- # Be strict when first creating the config object
- with pytest.raises(TypeError, match="unexpected keyword argument 'y'"):
- FlexibleConfig(x=1, y=2)
- class FlexibleConfigWithDefault(Config, strict=False):
- x: int = 0
- assert FlexibleConfigWithDefault().x == 0
- assert FlexibleConfigWithDefault(x=1)["x"] == 1
- def test_context():
- cfg = Config(x=1)
- with cfg(x=2) as c:
- assert c.x == 2
- c.x = 3
- assert cfg.x == 3
- assert cfg.x == 1
- with cfg(x=2) as c:
- assert c == cfg
- assert cfg.x == 2
- with cfg(x=3) as c2:
- assert c2 == cfg
- assert cfg.x == 3
- with pytest.raises(RuntimeError, match="context manager without"):
- with cfg as c3: # Forgot to call `cfg(...)`
- pass
- assert cfg.x == 3
- assert cfg.x == 2
- assert cfg.x == 1
- c = cfg(x=4) # Not yet as context (not recommended, but possible)
- assert c == cfg
- assert cfg.x == 4
- # Cheat by looking at internal data; context stack should only grow with __enter__
- assert cfg._prev is not None
- assert cfg._context_stack == []
- with c:
- assert c == cfg
- assert cfg.x == 4
- assert cfg.x == 1
- # Cheat again; there was no preceding `cfg(...)` call this time
- assert cfg._prev is None
- with pytest.raises(RuntimeError, match="context manager without"):
- with cfg:
- pass
- assert cfg.x == 1
|