Repository: businho/pytest-ruff (language: python)

Issue:
Assertion Error on version > 0.2.1 with pytest option '-p no:cacheprovider'
Since pytest-ruff 0.2.1, I've noticed an issue with an assertion error relating to the `get_stash` method within the pytest compatibility layer.

https://github.com/businho/pytest-ruff/blob/main/pytest_ruff/_pytest_compat.py#L41

```
/opt/hostedtoolcache/Python/3.12.4/x64/lib/python3.12/site-packages/pytest_ruff/_pytest_compat.py:41: AssertionError
=========================== short test summary info ============================
ERROR src/hello/__init__.py::ruff - assert <object object at 0x7fd14fd28df0> is not <object object at 0x7fd14fd28df0>
ERROR src/hello/ruff.py::ruff - assert <object object at 0x7fd14fd2be10> is not <object object at 0x7fd14fd2be10>
============================== 2 errors in 0.20s ===============================
```

I've created a cut down project to demonstrate the issue, based around this pyproject.toml
```toml
[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "hello"
version = "0.0.1"
dependencies = ["pytest", "pytest-ruff"]

[project.scripts]
ruff-hello = "hello.ruff:world"

[tool.pytest.ini_options]
pythonpath = "src"
addopts = "-p no:cacheprovider --ruff"

[tool.ruff]
preview = true

[tool.ruff.lint]
select = [
    # flake8
    "A", "ARG", "B", "BLE", "C4", "PIE", "RET", "SIM", "S",
    "F",  # pyflakes
    "N",  # pep8-nameing
    "PL", # pylint
    "E",  # error
    "W",  # warning
    "PTH", # pathlib
    "RUF", # ruff
]
```

Examples
- 0.2.1 - working -> https://github.com/techman83/test-pytest-ruff/actions/runs/9870232427/job/27255534792
- 0.3 assertion error -> https://github.com/techman83/test-pytest-ruff/actions/runs/9870232427/job/27255535102
- 0.3.1 assertion error -> https://github.com/techman83/test-pytest-ruff/actions/runs/9870232427/job/27255535354
- 0.3.2 assertion error -> https://github.com/techman83/test-pytest-ruff/actions/runs/9870232427/job/27255535647
- 0.4.0 assertion error -> https://github.com/techman83/test-pytest-ruff/actions/runs/9870232427/job/27255535898

It appears to be related to config file discovery, as commenting out `assert stash is not missing` the tests do run, but use the default ruff config.


Discussion / hints:
I've updated the title to reflect the problem. In 0.2.1, if no cache is set an empty dict is the default -> https://github.com/businho/pytest-ruff/blob/v0.2.1/pytest_ruff.py#L95
```python
ruffmtimes = self.config.stash.get(_MTIMES_STASH_KEY, {})
```
But there was a change in behaviour when the compatibility layer was introduced in 37b8e736063f61ca4b5118ac499ee63fb3943b17
https://github.com/businho/pytest-ruff/blob/v0.4.0/pytest_ruff/_pytest_compat.py#L39
```python
    missing = object()
    stash = get_stash_object(config).get(_MTIMES_STASH_KEY, default=missing)
    assert stash is not missing
```

That if the cache is missing, throw an assertion error. Setting the default back to an empty dict and removing the assertion check resolves the problem. However there might be a reason why @iurisilvio has done it this way.

TL;DR - the work around is to enable caching (or rather, not disable it, as it is enabled by default and likely why this is an edge case).
If I remember correctly, I added this assert just to make mypy happy. If it is breaking something, we can remove it and implement this code path. 👍🏻 
That makes total sense! There is a method that Pytest has that might omit the need for setting up the stash or doing any checks for cache that might help -> https://docs.pytest.org/en/8.1.x/reference/reference.html#pytest.Stash.setdefault

Though until yesterday I hadn't looked too deeply at the inner workings of pytest :grinning: 

Fix patch:
diff --git a/tox.ini b/tox.ini
index 6c5de3c..1428482 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 isolated_build = true
-envlist = py{38,39,310,311,312}-pytest{5,6,7,8,810,81},lint
+envlist = py{38,39,310,311,312}-pytest{5,6,7,8,810,latest},lint
 
 [testenv]
 allowlist_externals = poetry
@@ -9,10 +9,10 @@ commands =
     pytest5: pip install "pytest<6"
     pytest6: pip install "pytest>=6,<7"
     pytest7: pip install "pytest>=7,<8"
-    pytest8: pip install "pytest>=8,<8.1"
+    pytest8: pip install "pytest>=8,<9"
     # pytest 8.1.0 was yanked, but their "broken behaviour" will comeback soon
     pytest810: pip install "pytest==8.1.0"
-    pytest81: pip install "pytest>=8.1,<9"
+    pytestlatest: pip install pytest
     # Disable ruff plugin to generate better coverage results
     poetry run pytest -p no:ruff -vvv --cov --cov-append --cov-report term --cov-report xml {posargs}
 


Test patch:
diff --git a/pytest_ruff/_pytest_compat.py b/pytest_ruff/_pytest_compat.py
index 2d2db7d..ab9f839 100644
--- a/pytest_ruff/_pytest_compat.py
+++ b/pytest_ruff/_pytest_compat.py
@@ -6,7 +6,7 @@
 import pytest
 
 try:
-    from pytest import Stash, StashKey
+    from pytest import Stash as Stash, StashKey
 except ImportError:
     import _pytest.store
 
@@ -36,10 +36,7 @@ def get_stash_object(config):
 
 
 def get_stash(config):
-    missing = object()
-    stash = get_stash_object(config).get(_MTIMES_STASH_KEY, default=missing)
-    assert stash is not missing
-    return stash
+    return get_stash_object(config).get(_MTIMES_STASH_KEY, default=None)
 
 
 def set_stash(config, value):
diff --git a/tests/assets/broken_config/empty.py b/tests/assets/config_broken/empty.py
similarity index 100%
rename from tests/assets/broken_config/empty.py
rename to tests/assets/config_broken/empty.py
diff --git a/tests/assets/broken_config/ruff.toml b/tests/assets/config_broken/ruff.toml
similarity index 100%
rename from tests/assets/broken_config/ruff.toml
rename to tests/assets/config_broken/ruff.toml
diff --git a/tests/assets/ok.py b/tests/assets/ok.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/test_plugin.py b/tests/test_plugin.py
index fd1c8c2..a3facd8 100644
--- a/tests/test_plugin.py
+++ b/tests/test_plugin.py
@@ -88,7 +88,7 @@ def test_broken_ruff_config():
             "-m",
             "pytest",
             "--ruff",
-            "tests/assets/broken_config/empty.py",
+            "tests/assets/config_broken/empty.py",
         ],
         stdout=subprocess.PIPE,
         stderr=subprocess.PIPE,
@@ -96,3 +96,21 @@ def test_broken_ruff_config():
     out, err = process.communicate()
     assert err.decode() == ""
     assert "unknown field `broken`" in out.decode()
+
+
+def test_without_pytest_cache():
+    process = subprocess.Popen(
+        [
+            sys.executable,
+            "-m",
+            "pytest",
+            "--ruff",
+            "-pno:cacheprovider",
+            "tests/assets/ok.py",
+        ],
+        stdout=subprocess.PIPE,
+        stderr=subprocess.PIPE,
+    )
+    out, err = process.communicate()
+    assert err.decode() == ""
+    assert "tests/assets/ok.py ." in out.decode()