Repository: pytest-dev/pytest-randomly (language: python)

Issue:
AttributeError: 'Config' object has no attribute 'cache' with cacheprovider disabled
### Python Version

3.8.10

### pytest Version

6.2.5

### Package Version

3.10.1

### Description

pytest fails to run tests if `cacheprovider` plugin is disabled during test execution.

## Steps to reproduce error
1. Sample test:
```
$ cat test_pass.py
def test_pass1():
    assert True

def test_pass2():
    assert True
```
2. Run test with pytest caching disabled:
```
$ pytest -p no:cacheprovider test_pass.py -v
================================================================================= test session starts =================================================================================
platform linux -- Python 3.8.10, pytest-6.2.5, py-1.10.0, pluggy-0.13.1 -- /usr/bin/python3
rootdir: /tmp/pytest-of-mandeepsandhu/mytest
collected 2 items                                                                                                                                                                     

test_pass.py::test_pass1 PASSED                                                                                                                                                 [ 50%]
test_pass.py::test_pass2 PASSED                                                                                                                                                 [100%]

================================================================================== 2 passed in 0.00s ==================================================================================

```
3. Install `pytest-randomly` and repeat step 2
```
$ pip install --user pytest-randomly
...
Successfully installed pytest-randomly-3.10.1
$ pytest -p no:cacheprovider test_pass.py -v
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/home/mandeepsandhu/.local/lib/python3.8/site-packages/_pytest/main.py", line 265, in wrap_session
INTERNALERROR>     config._do_configure()
INTERNALERROR>   File "/home/mandeepsandhu/.local/lib/python3.8/site-packages/_pytest/config/__init__.py", line 982, in _do_configure
INTERNALERROR>     self.hook.pytest_configure.call_historic(kwargs=dict(config=self))
INTERNALERROR>   File "/home/mandeepsandhu/.local/lib/python3.8/site-packages/pluggy/hooks.py", line 308, in call_historic
INTERNALERROR>     res = self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/home/mandeepsandhu/.local/lib/python3.8/site-packages/pluggy/manager.py", line 93, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/mandeepsandhu/.local/lib/python3.8/site-packages/pluggy/manager.py", line 84, in <lambda>
INTERNALERROR>     self._inner_hookexec = lambda hook, methods, kwargs: hook.multicall(
INTERNALERROR>   File "/home/mandeepsandhu/.local/lib/python3.8/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/mandeepsandhu/.local/lib/python3.8/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/mandeepsandhu/.local/lib/python3.8/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/mandeepsandhu/.local/lib/python3.8/site-packages/pytest_randomly/__init__.py", line 116, in pytest_configure
INTERNALERROR>     assert config.cache is not None
INTERNALERROR> AttributeError: 'Config' object has no attribute 'cache'
```
4. Running with `randomly` disabled, works:
```
$ pytest -p no:cacheprovider -p no:randomly test_pass.py -v
================================================================================= test session starts =================================================================================
platform linux -- Python 3.8.10, pytest-6.2.5, py-1.10.0, pluggy-0.13.1 -- /usr/bin/python3
rootdir: /tmp/pytest-of-mandeepsandhu/mytest
collected 2 items                                                                                                                                                                     

test_pass.py::test_pass1 PASSED                                                                                                                                                 [ 50%]
test_pass.py::test_pass2 PASSED                                                                                                                                                 [100%]

================================================================================== 2 passed in 0.00s ==================================================================================

```



Discussion / hints:
Yeah we could rework to not need the cache.
Thanks. In my case I have caching disabled since my tests run inside a read-only container filesystem (run by AWS lambda), although having the tests execute randomly is still desirable.

Fix patch:
diff --git a/HISTORY.rst b/HISTORY.rst
index 1caf11d..ab25918 100644
--- a/HISTORY.rst
+++ b/HISTORY.rst
@@ -2,6 +2,11 @@
 History
 =======
 
+* Fix crash when pytest’s cacheprovider is disabled.
+
+  Thanks to Mandeep Sandhu for the report in `Issue #408
+  <https://github.com/pytest-dev/pytest-randomly/issues/408>`__.
+
 * Improve group name in ``pytest --help``.
 
 3.10.1 (2021-08-13)
diff --git a/README.rst b/README.rst
index 3be93a7..25eb1f7 100644
--- a/README.rst
+++ b/README.rst
@@ -134,6 +134,8 @@ Or more conveniently, use the special value ``last``:
 
     pytest --randomly-seed=last
 
+(This only works if pytest’s cacheprovider plugin has not been disabled.)
+
 Since the ordering is by module, then by class, you can debug inter-test
 pollution failures by narrowing down which tests are being run to find the bad
 interaction by rerunning just the module/class:


Test patch:
diff --git a/src/pytest_randomly/__init__.py b/src/pytest_randomly/__init__.py
index bdb00e7..36d41b7 100644
--- a/src/pytest_randomly/__init__.py
+++ b/src/pytest_randomly/__init__.py
@@ -103,6 +103,9 @@ def pytest_configure(config: Config) -> None:
 
     seed_value = config.getoption("randomly_seed")
     if seed_value == "last":
+        assert hasattr(
+            config, "cache"
+        ), "The cacheprovider plugin is required to use 'last'"
         assert config.cache is not None
         seed = config.cache.get("randomly_seed", default_seed)
     elif seed_value == "default":
@@ -113,8 +116,9 @@ def pytest_configure(config: Config) -> None:
             seed = default_seed
     else:
         seed = seed_value
-    assert config.cache is not None
-    config.cache.set("randomly_seed", seed)
+    if hasattr(config, "cache"):
+        assert config.cache is not None
+        config.cache.set("randomly_seed", seed)
     config.option.randomly_seed = seed
 
 
diff --git a/tests/test_pytest_randomly.py b/tests/test_pytest_randomly.py
index 6b5eb9a..703043f 100644
--- a/tests/test_pytest_randomly.py
+++ b/tests/test_pytest_randomly.py
@@ -76,6 +76,17 @@ def test_b():
     out.assert_outcomes(passed=2, failed=0)
 
 
+def test_without_cacheprovider(ourtestdir):
+    ourtestdir.makepyfile(
+        test_one="""
+        def test_a():
+            pass
+        """
+    )
+    out = ourtestdir.runpytest("-p", "no:cacheprovider")
+    out.assert_outcomes(passed=1, failed=0)
+
+
 def test_using_last_seed(ourtestdir):
     ourtestdir.makepyfile(
         test_one="""