summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniƫl van Noord <13665637+DanielNoord@users.noreply.github.com>2022-04-01 20:34:43 +0200
committerGitHub <noreply@github.com>2022-04-01 20:34:43 +0200
commita96c50cba6026ddbdf5915b91fd785f721ca4e15 (patch)
treedefa5c2448699fd6347bfc0e58ca1bfbfc2b93af
parente01fa86c00b2cd879c44570b383115a9407547f1 (diff)
downloadpylint-git-a96c50cba6026ddbdf5915b91fd785f721ca4e15.tar.gz
Refactor ``find_default_config_files`` (#6067)
Co-authored-by: Pierre Sassoulas <pierre.sassoulas@gmail.com>
-rw-r--r--pylint/config/find_default_config_files.py61
-rw-r--r--pylint/testutils/__init__.py2
-rw-r--r--pylint/testutils/utils.py44
-rw-r--r--tests/config/test_find_default_config_files.py132
-rw-r--r--tests/lint/unittest_lint.py41
5 files changed, 211 insertions, 69 deletions
diff --git a/pylint/config/find_default_config_files.py b/pylint/config/find_default_config_files.py
index 70e867ad6..71ba222b3 100644
--- a/pylint/config/find_default_config_files.py
+++ b/pylint/config/find_default_config_files.py
@@ -14,6 +14,9 @@ if sys.version_info >= (3, 11):
else:
import tomli as tomllib
+RC_NAMES = (Path("pylintrc"), Path(".pylintrc"))
+CONFIG_NAMES = RC_NAMES + (Path("pyproject.toml"), Path("setup.cfg"))
+
def _toml_has_config(path: Union[Path, str]) -> bool:
with open(path, mode="rb") as toml_handle:
@@ -34,43 +37,41 @@ def _cfg_has_config(path: Union[Path, str]) -> bool:
return any(section.startswith("pylint.") for section in parser.sections())
-def find_default_config_files() -> Iterator[str]:
+def find_default_config_files() -> Iterator[Path]:
"""Find all possible config files."""
- rc_names = ("pylintrc", ".pylintrc")
- config_names = rc_names + ("pyproject.toml", "setup.cfg")
- for config_name in config_names:
- if os.path.isfile(config_name):
- if config_name.endswith(".toml") and not _toml_has_config(config_name):
+ for config_name in CONFIG_NAMES:
+ if config_name.is_file():
+ if config_name.suffix == ".toml" and not _toml_has_config(config_name):
continue
- if config_name.endswith(".cfg") and not _cfg_has_config(config_name):
+ if config_name.suffix == ".cfg" and not _cfg_has_config(config_name):
continue
- yield os.path.abspath(config_name)
+ yield config_name.resolve()
- if os.path.isfile("__init__.py"):
- curdir = os.path.abspath(os.getcwd())
- while os.path.isfile(os.path.join(curdir, "__init__.py")):
- curdir = os.path.abspath(os.path.join(curdir, ".."))
- for rc_name in rc_names:
- rc_path = os.path.join(curdir, rc_name)
- if os.path.isfile(rc_path):
- yield rc_path
+ if Path("__init__.py").is_file():
+ curdir = Path(os.getcwd()).resolve()
+ while (curdir / "__init__.py").is_file():
+ curdir = curdir.parent
+ for rc_name in RC_NAMES:
+ rc_path = curdir / rc_name
+ if rc_path.is_file():
+ yield rc_path.resolve()
- if "PYLINTRC" in os.environ and os.path.exists(os.environ["PYLINTRC"]):
- if os.path.isfile(os.environ["PYLINTRC"]):
- yield os.environ["PYLINTRC"]
+ if "PYLINTRC" in os.environ and Path(os.environ["PYLINTRC"]).exists():
+ if Path(os.environ["PYLINTRC"]).is_file():
+ yield Path(os.environ["PYLINTRC"]).resolve()
else:
- user_home = os.path.expanduser("~")
- if user_home not in ("~", "/root"):
- home_rc = os.path.join(user_home, ".pylintrc")
- if os.path.isfile(home_rc):
- yield home_rc
- home_rc = os.path.join(user_home, ".config", "pylintrc")
- if os.path.isfile(home_rc):
- yield home_rc
+ user_home = Path.home()
+ if str(user_home) not in ("~", "/root"):
+ home_rc = user_home / ".pylintrc"
+ if home_rc.is_file():
+ yield home_rc.resolve()
+ home_rc = user_home / ".config" / "pylintrc"
+ if home_rc.is_file():
+ yield home_rc.resolve()
if os.path.isfile("/etc/pylintrc"):
- yield "/etc/pylintrc"
+ yield Path("/etc/pylintrc").resolve()
def find_pylintrc() -> Optional[str]:
@@ -84,6 +85,6 @@ def find_pylintrc() -> Optional[str]:
DeprecationWarning,
)
for config_file in find_default_config_files():
- if config_file.endswith("pylintrc"):
- return config_file
+ if str(config_file).endswith("pylintrc"):
+ return str(config_file)
return None
diff --git a/pylint/testutils/__init__.py b/pylint/testutils/__init__.py
index 0b55fc8e9..e9b8de1de 100644
--- a/pylint/testutils/__init__.py
+++ b/pylint/testutils/__init__.py
@@ -18,6 +18,7 @@ __all__ = [
"UPDATE_FILE",
"UPDATE_OPTION",
"UnittestLinter",
+ "create_files",
]
from pylint.testutils.checker_test_case import CheckerTestCase
@@ -31,3 +32,4 @@ from pylint.testutils.output_line import MessageTest
from pylint.testutils.reporter_for_tests import GenericTestReporter, MinimalTestReporter
from pylint.testutils.tokenize_str import _tokenize_str
from pylint.testutils.unittest_linter import UnittestLinter
+from pylint.testutils.utils import create_files
diff --git a/pylint/testutils/utils.py b/pylint/testutils/utils.py
new file mode 100644
index 000000000..a6a588194
--- /dev/null
+++ b/pylint/testutils/utils.py
@@ -0,0 +1,44 @@
+# 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
+
+import os
+from typing import List
+
+
+def create_files(paths: List[str], chroot: str = ".") -> None:
+ """Creates directories and files found in <path>.
+
+ :param list paths: list of relative paths to files or directories
+ :param str chroot: the root directory in which paths will be created
+
+ >>> from os.path import isdir, isfile
+ >>> isdir('/tmp/a')
+ False
+ >>> create_files(['a/b/foo.py', 'a/b/c/', 'a/b/c/d/e.py'], '/tmp')
+ >>> isdir('/tmp/a')
+ True
+ >>> isdir('/tmp/a/b/c')
+ True
+ >>> isfile('/tmp/a/b/c/d/e.py')
+ True
+ >>> isfile('/tmp/a/b/foo.py')
+ True
+ """
+ dirs, files = set(), set()
+ for path in paths:
+ path = os.path.join(chroot, path)
+ filename = os.path.basename(path)
+ # path is a directory path
+ if filename == "":
+ dirs.add(path)
+ # path is a filename path
+ else:
+ dirs.add(os.path.dirname(path))
+ files.add(path)
+ for dirpath in dirs:
+ if not os.path.isdir(dirpath):
+ os.makedirs(dirpath)
+ for filepath in files:
+ with open(filepath, "w", encoding="utf-8"):
+ pass
diff --git a/tests/config/test_find_default_config_files.py b/tests/config/test_find_default_config_files.py
index 04392781b..ca1461bad 100644
--- a/tests/config/test_find_default_config_files.py
+++ b/tests/config/test_find_default_config_files.py
@@ -1,13 +1,145 @@
# 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
+
+import contextlib
+import importlib
+import os
+import shutil
+import sys
+import tempfile
from pathlib import Path
+from typing import Iterator
import pytest
+from pylint import config, testutils
from pylint.config.find_default_config_files import _cfg_has_config, _toml_has_config
+@pytest.fixture
+def pop_pylintrc() -> None:
+ """Remove the PYLINTRC environment variable"""
+ os.environ.pop("PYLINTRC", None)
+
+
+if os.name == "java":
+ if os.name == "nt":
+ HOME = "USERPROFILE"
+ else:
+ HOME = "HOME"
+elif sys.platform == "win32":
+ HOME = "USERPROFILE"
+else:
+ HOME = "HOME"
+
+
+@contextlib.contextmanager
+def fake_home() -> Iterator[None]:
+ """Fake a home directory."""
+ folder = tempfile.mkdtemp("fake-home")
+ old_home = os.environ.get(HOME)
+ try:
+ os.environ[HOME] = folder
+ yield
+ finally:
+ os.environ.pop("PYLINTRC", "")
+ if old_home is None:
+ del os.environ[HOME]
+ else:
+ os.environ[HOME] = old_home
+ shutil.rmtree(folder, ignore_errors=True)
+
+
+@contextlib.contextmanager
+def tempdir() -> Iterator[str]:
+ """Create a temp directory and change the current location to it.
+
+ This is supposed to be used with a *with* statement.
+ """
+ tmp = tempfile.mkdtemp()
+
+ # Get real path of tempfile, otherwise test fail on mac os x
+ current_dir = os.getcwd()
+ os.chdir(tmp)
+ abs_tmp = os.path.abspath(".")
+
+ try:
+ yield abs_tmp
+ finally:
+ os.chdir(current_dir)
+ shutil.rmtree(abs_tmp)
+
+
+@pytest.mark.usefixtures("pop_pylintrc")
+def test_pylintrc() -> None:
+ """Test that the environment variable is checked for existence."""
+ with fake_home():
+ current_dir = os.getcwd()
+ os.chdir(os.path.dirname(os.path.abspath(sys.executable)))
+ try:
+ assert not list(config.find_default_config_files())
+ os.environ["PYLINTRC"] = os.path.join(tempfile.gettempdir(), ".pylintrc")
+ assert not list(config.find_default_config_files())
+ os.environ["PYLINTRC"] = "."
+ assert not list(config.find_default_config_files())
+ finally:
+ os.chdir(current_dir)
+ importlib.reload(config)
+
+
+@pytest.mark.usefixtures("pop_pylintrc")
+def test_pylintrc_parentdir() -> None:
+ """Test that the first pylintrc we find is the first parent directory."""
+ with tempdir() as chroot:
+ chroot_path = Path(chroot)
+ testutils.create_files(
+ [
+ "a/pylintrc",
+ "a/b/__init__.py",
+ "a/b/pylintrc",
+ "a/b/c/__init__.py",
+ "a/b/c/d/__init__.py",
+ "a/b/c/d/e/.pylintrc",
+ ]
+ )
+
+ with fake_home():
+ assert not list(config.find_default_config_files())
+
+ results = {
+ "a": chroot_path / "a" / "pylintrc",
+ "a/b": chroot_path / "a" / "b" / "pylintrc",
+ "a/b/c": chroot_path / "a" / "b" / "pylintrc",
+ "a/b/c/d": chroot_path / "a" / "b" / "pylintrc",
+ "a/b/c/d/e": chroot_path / "a" / "b" / "c" / "d" / "e" / ".pylintrc",
+ }
+ for basedir, expected in results.items():
+ os.chdir(chroot_path / basedir)
+ assert next(config.find_default_config_files()) == expected
+
+
+@pytest.mark.usefixtures("pop_pylintrc")
+def test_pylintrc_parentdir_no_package() -> None:
+ """Test that we don't find a pylintrc in sub-packages."""
+ with tempdir() as chroot:
+ with fake_home():
+ chroot_path = Path(chroot)
+ testutils.create_files(
+ ["a/pylintrc", "a/b/pylintrc", "a/b/c/d/__init__.py"]
+ )
+ assert config.find_pylintrc() is None
+ results = {
+ "a": chroot_path / "a" / "pylintrc",
+ "a/b": chroot_path / "a" / "b" / "pylintrc",
+ "a/b/c": None,
+ "a/b/c/d": None,
+ }
+ for basedir, expected in results.items():
+ os.chdir(chroot_path / basedir)
+ assert next(config.find_default_config_files(), None) == expected
+
+
@pytest.mark.parametrize(
"content,expected",
[
diff --git a/tests/lint/unittest_lint.py b/tests/lint/unittest_lint.py
index 90faa1c2f..d973cf892 100644
--- a/tests/lint/unittest_lint.py
+++ b/tests/lint/unittest_lint.py
@@ -12,7 +12,7 @@ from contextlib import contextmanager
from importlib import reload
from io import StringIO
from os import chdir, getcwd
-from os.path import abspath, basename, dirname, isdir, join, sep
+from os.path import abspath, dirname, join, sep
from shutil import rmtree
from typing import Iterable, Iterator, List, Optional, Tuple
@@ -32,6 +32,7 @@ from pylint.exceptions import InvalidMessageError
from pylint.lint import ArgumentPreprocessingError, PyLinter, Run, preprocess_options
from pylint.message import Message
from pylint.reporters import text
+from pylint.testutils import create_files
from pylint.typing import MessageLocationTuple
from pylint.utils import FileState, print_full_documentation, tokenize_module
@@ -95,44 +96,6 @@ def tempdir() -> Iterator[str]:
rmtree(abs_tmp)
-def create_files(paths: List[str], chroot: str = ".") -> None:
- """Creates directories and files found in <path>.
-
- :param list paths: list of relative paths to files or directories
- :param str chroot: the root directory in which paths will be created
-
- >>> from os.path import isdir, isfile
- >>> isdir('/tmp/a')
- False
- >>> create_files(['a/b/foo.py', 'a/b/c/', 'a/b/c/d/e.py'], '/tmp')
- >>> isdir('/tmp/a')
- True
- >>> isdir('/tmp/a/b/c')
- True
- >>> isfile('/tmp/a/b/c/d/e.py')
- True
- >>> isfile('/tmp/a/b/foo.py')
- True
- """
- dirs, files = set(), set()
- for path in paths:
- path = join(chroot, path)
- filename = basename(path)
- # path is a directory path
- if filename == "":
- dirs.add(path)
- # path is a filename path
- else:
- dirs.add(dirname(path))
- files.add(path)
- for dirpath in dirs:
- if not isdir(dirpath):
- os.makedirs(dirpath)
- for filepath in files:
- with open(filepath, "w", encoding="utf-8"):
- pass
-
-
@pytest.fixture
def fake_path() -> Iterator[Iterable[str]]:
orig = list(sys.path)