summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPierre Sassoulas <pierre.sassoulas@gmail.com>2022-05-05 23:02:24 +0200
committerGitHub <noreply@github.com>2022-05-05 23:02:24 +0200
commit58c4f370c7395d9d4e202ba83623768abcc3ac24 (patch)
tree30087c371bcfbf2debd5cc191fc2b3cf5d04061f
parent40f50af81c493be83ba89bef814e117626dfbb25 (diff)
downloadpylint-git-58c4f370c7395d9d4e202ba83623768abcc3ac24.tar.gz
Move caching to pylint.lint from pylint.config and deprecate (#6468)
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com> Co-authored-by: Daniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>
-rw-r--r--pylint/config/__init__.py54
-rw-r--r--pylint/lint/caching.py63
-rw-r--r--tests/lint/test_caching.py68
-rw-r--r--tests/lint/unittest_expand_modules.py9
-rw-r--r--tests/test_deprecation.py9
5 files changed, 173 insertions, 30 deletions
diff --git a/pylint/config/__init__.py b/pylint/config/__init__.py
index 485a08edd..7dc96f0cf 100644
--- a/pylint/config/__init__.py
+++ b/pylint/config/__init__.py
@@ -4,11 +4,6 @@
from __future__ import annotations
-import os
-import pathlib
-import pickle
-import sys
-
__all__ = [
"ConfigurationMixIn", # Deprecated
"find_default_config_files",
@@ -21,8 +16,12 @@ __all__ = [
"PYLINTRC",
"USER_HOME", # Compatibility with the old API
"PYLINT_HOME", # Compatibility with the old API
+ "save_results", # Compatibility with the old API # Deprecated
+ "load_results", # Compatibility with the old API # Deprecated
]
+import warnings
+
from pylint.config.arguments_provider import UnsupportedAction
from pylint.config.configuration_mixin import ConfigurationMixIn
from pylint.config.environment_variable import PYLINTRC
@@ -38,32 +37,27 @@ from pylint.constants import PYLINT_HOME, USER_HOME
from pylint.utils import LinterStats
-def _get_pdata_path(base_name: str, recurs: int) -> pathlib.Path:
- base_name = base_name.replace(os.sep, "_")
- return pathlib.Path(PYLINT_HOME) / f"{base_name}{recurs}.stats"
-
-
def load_results(base: str) -> LinterStats | None:
- data_file = _get_pdata_path(base, 1)
- try:
- with open(data_file, "rb") as stream:
- data = pickle.load(stream)
- if not isinstance(data, LinterStats):
- raise TypeError
- return data
- except Exception: # pylint: disable=broad-except
- return None
+ # TODO: 3.0: Remove deprecated function
+ # pylint: disable=import-outside-toplevel
+ from pylint.lint.caching import load_results as _real_load_results
+
+ warnings.warn(
+ "'pylint.config.load_results' is deprecated, please use "
+ "'pylint.lint.load_results' instead. This will be removed in 3.0.",
+ DeprecationWarning,
+ )
+ return _real_load_results(base, PYLINT_HOME)
def save_results(results: LinterStats, base: str) -> None:
- if not os.path.exists(PYLINT_HOME):
- try:
- os.makedirs(PYLINT_HOME)
- except OSError:
- print(f"Unable to create directory {PYLINT_HOME}", file=sys.stderr)
- data_file = _get_pdata_path(base, 1)
- try:
- with open(data_file, "wb") as stream:
- pickle.dump(results, stream)
- except OSError as ex:
- print(f"Unable to create file {data_file}: {ex}", file=sys.stderr)
+ # TODO: 3.0: Remove deprecated function
+ # pylint: disable=import-outside-toplevel
+ from pylint.lint.caching import save_results as _real_save_results
+
+ warnings.warn(
+ "'pylint.config.save_results' is deprecated, please use "
+ "'pylint.lint.save_results' instead. This will be removed in 3.0.",
+ DeprecationWarning,
+ )
+ return _real_save_results(results, base, PYLINT_HOME)
diff --git a/pylint/lint/caching.py b/pylint/lint/caching.py
new file mode 100644
index 000000000..ec5a9f78a
--- /dev/null
+++ b/pylint/lint/caching.py
@@ -0,0 +1,63 @@
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+
+from __future__ import annotations
+
+import pickle
+import sys
+import warnings
+from pathlib import Path
+
+from pylint.constants import PYLINT_HOME
+from pylint.utils import LinterStats
+
+
+def _get_pdata_path(
+ base_name: Path, recurs: int, pylint_home: Path = Path(PYLINT_HOME)
+) -> Path:
+ underscored_name = "_".join(str(p) for p in base_name.parts)
+ return pylint_home / f"{underscored_name}_{recurs}.stats"
+
+
+def load_results(
+ base: str | Path, pylint_home: str | Path = PYLINT_HOME
+) -> LinterStats | None:
+ base = Path(base)
+ pylint_home = Path(pylint_home)
+ data_file = _get_pdata_path(base, 1, pylint_home)
+
+ if not data_file.exists():
+ return None
+
+ try:
+ with open(data_file, "rb") as stream:
+ data = pickle.load(stream)
+ if not isinstance(data, LinterStats):
+ warnings.warn(
+ "You're using an old pylint cache with invalid data following "
+ f"an upgrade, please delete '{data_file}'.",
+ UserWarning,
+ )
+ raise TypeError
+ return data
+ except Exception: # pylint: disable=broad-except
+ # There's an issue with the cache but we just continue as if it isn't there
+ return None
+
+
+def save_results(
+ results: LinterStats, base: str | Path, pylint_home: str | Path = PYLINT_HOME
+) -> None:
+ base = Path(base)
+ pylint_home = Path(pylint_home)
+ try:
+ pylint_home.mkdir(parents=True, exist_ok=True)
+ except OSError: # pragma: no cover
+ print(f"Unable to create directory {pylint_home}", file=sys.stderr)
+ data_file = _get_pdata_path(base, 1)
+ try:
+ with open(data_file, "wb") as stream:
+ pickle.dump(results, stream)
+ except OSError as ex: # pragma: no cover
+ print(f"Unable to create file {data_file}: {ex}", file=sys.stderr)
diff --git a/tests/lint/test_caching.py b/tests/lint/test_caching.py
new file mode 100644
index 000000000..8d3f7afd4
--- /dev/null
+++ b/tests/lint/test_caching.py
@@ -0,0 +1,68 @@
+# Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+# For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
+# Copyright (c) https://github.com/PyCQA/pylint/blob/main/CONTRIBUTORS.txt
+
+# Pytest fixtures work like this by design
+# pylint: disable=redefined-outer-name
+
+from pathlib import Path
+
+import pytest
+
+from pylint.constants import PYLINT_HOME
+from pylint.lint.caching import _get_pdata_path, load_results, save_results
+from pylint.utils import LinterStats
+from pylint.utils.linterstats import BadNames
+
+PYLINT_HOME_PATH = Path(PYLINT_HOME)
+
+
+@pytest.mark.parametrize(
+ "path,recur,expected",
+ [
+ ["", 1, PYLINT_HOME_PATH / "_1.stats"],
+ ["", 2, PYLINT_HOME_PATH / "_2.stats"],
+ ["a/path", 42, PYLINT_HOME_PATH / "a_path_42.stats"],
+ ],
+)
+def test__get_pdata_path(path: str, recur: int, expected: Path) -> None:
+ assert _get_pdata_path(Path(path), recur) == expected
+
+
+@pytest.fixture
+def linter_stats() -> LinterStats:
+ return LinterStats(
+ bad_names=BadNames(
+ argument=1,
+ attr=2,
+ klass=3,
+ class_attribute=4,
+ class_const=5,
+ const=6,
+ inlinevar=7,
+ function=8,
+ method=9,
+ module=10,
+ variable=11,
+ typevar=12,
+ )
+ )
+
+
+@pytest.mark.parametrize("path", [".tests/", ".tests/a/path/"])
+def test_save_and_load_result(path: str, linter_stats: LinterStats) -> None:
+ save_results(linter_stats, path)
+ loaded = load_results(path)
+ assert loaded is not None
+ assert loaded.bad_names == linter_stats.bad_names
+
+
+@pytest.mark.parametrize("path", [".tests", ".tests/a/path/"])
+def test_save_and_load_not_a_linter_stats(path: str) -> None:
+ # type ignore because this is what we're testing
+ save_results(1, path) # type: ignore[arg-type]
+ with pytest.warns(UserWarning) as warn:
+ loaded = load_results(path)
+ assert loaded is None
+ warn_str = str(warn.pop().message)
+ assert "old pylint cache with invalid data" in warn_str
diff --git a/tests/lint/unittest_expand_modules.py b/tests/lint/unittest_expand_modules.py
index 36fd7ed4d..1ce5b6c4e 100644
--- a/tests/lint/unittest_expand_modules.py
+++ b/tests/lint/unittest_expand_modules.py
@@ -69,6 +69,14 @@ test_pylinter = {
"path": str(TEST_DIRECTORY / "lint/test_pylinter.py"),
}
+test_caching = {
+ "basename": "lint",
+ "basepath": INIT_PATH,
+ "isarg": False,
+ "name": "lint.test_caching",
+ "path": str(TEST_DIRECTORY / "lint/test_caching.py"),
+}
+
test_namespace_packages = {
"basename": "lint",
"basepath": INIT_PATH,
@@ -106,6 +114,7 @@ class TestExpandModules(CheckerTestCase):
[str(Path(__file__).parent)],
[
init_of_package,
+ test_caching,
test_namespace_packages,
test_pylinter,
test_utils,
diff --git a/tests/test_deprecation.py b/tests/test_deprecation.py
index a8d857b3a..2fd0a56c3 100644
--- a/tests/test_deprecation.py
+++ b/tests/test_deprecation.py
@@ -12,6 +12,7 @@ import pytest
from pylint.checkers import BaseChecker
from pylint.checkers.mapreduce_checker import MapReduceMixin
+from pylint.config import load_results, save_results
from pylint.interfaces import (
IAstroidChecker,
IChecker,
@@ -78,3 +79,11 @@ def test_interfaces() -> None:
IChecker()
with pytest.warns(DeprecationWarning):
ITokenChecker()
+
+
+def test_load_and_save_results() -> None:
+ """Test that load_results and save_results are deprecated."""
+ with pytest.warns(DeprecationWarning):
+ save_results(object(), "") # type: ignore[arg-type]
+ with pytest.warns(DeprecationWarning):
+ load_results("")