Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion pytest_mypy_plugins/item.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,7 +401,11 @@ def remove_cache_files(self, fpath_no_suffix: Path) -> None:
pass
store.commit()
finally:
store.close()
# ``MetadataStore.close`` was added in mypy 1.20; guard for older
# mypy versions that are still within our supported range (>=1.3).
close = getattr(store, "close", None)
if close is not None:
close()

def execute_extension_hook(self) -> None:
extension_hook_fqname = self.config.option.mypy_extension_hook
Expand Down
49 changes: 48 additions & 1 deletion pytest_mypy_plugins/tests/test_mypy_cache.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import subprocess
import textwrap
from collections.abc import Generator
from pathlib import Path
from sys import version_info
Expand Down Expand Up @@ -184,6 +185,50 @@ def test_cache_files_removed(
assert not get_created_cache_files(cache_dir, module_fpaths_no_suffix)


def test_cache_cleanup_without_store_close(temp_dir: Path) -> None:
Comment thread
thodson-usgs marked this conversation as resolved.
Outdated
"""Regression test: mypy's ``MetadataStore.close`` was added in 1.20, but
``setup.py`` declares ``mypy>=1.3``. Cache cleanup must not ``AttributeError``
on older mypy versions that lack ``close``.
"""
# conftest.py is loaded by the subprocess pytest before the plugin is
# imported, stripping ``close`` from the store classes to emulate mypy <1.20.
(temp_dir / "conftest.py").write_text(textwrap.dedent("""\
from mypy.metastore import (
FilesystemMetadataStore,
MetadataStore,
SqliteMetadataStore,
)
for cls in (FilesystemMetadataStore, MetadataStore, SqliteMetadataStore):
if "close" in cls.__dict__:
delattr(cls, "close")
"""))
make_yaml_test_file(
temp_dir,
"""
- case: test_cache_cleanup_without_store_close
main: |
import my_mod
files:
- path: my_mod.py
""",
)
res = subprocess.run(
["pytest", f"--mypy-testing-base={temp_dir}"],
cwd=temp_dir,
capture_output=True,
text=True,
)
# The test case itself must pass (or at worst be a test failure), never an
# internal error from the plugin's teardown.
assert res.returncode in (
pytest.ExitCode.OK,
pytest.ExitCode.TESTS_FAILED,
), f"rc={res.returncode}\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}"
combined = res.stdout + res.stderr
assert "AttributeError" not in combined, combined
assert "has no attribute 'close'" not in combined, combined


@pytest.fixture
def temp_dir() -> Generator[Path]:
"""
Expand Down Expand Up @@ -224,6 +269,8 @@ def get_created_cache_files(cache_dir: Path, module_rel_paths_no_suffix: tuple[s
if any(entry.startswith(rel_path + ".") for rel_path in module_rel_paths_no_suffix):
created.append(entry)
finally:
store.close()
close = getattr(store, "close", None)
if close is not None:
close()

return created