diff --git a/doc/user_guide/run.rst b/doc/user_guide/run.rst index 43e6e21138..08b0819e2c 100644 --- a/doc/user_guide/run.rst +++ b/doc/user_guide/run.rst @@ -92,28 +92,29 @@ expression in special cases). For a full list of options, use ``--help`` Specifying all the options suitable for your setup and coding standards can be tedious, so it is possible to use a configuration file to -specify the default values. You can specify a configuration file on the -command line using the ``--rcfile`` option. Otherwise, Pylint searches for a -configuration file in the following order and uses the first one it finds: - -#. ``pylintrc`` in the current working directory -#. ``.pylintrc`` in the current working directory -#. ``pyproject.toml`` in the current working directory, - providing it has at least one ``tool.pylint.`` section. -#. ``setup.cfg`` in the current working directory, - providing it has at least one ``pylint.`` section -#. If the current working directory is in a Python module, Pylint searches \ - up the hierarchy of Python modules until it finds a ``pylintrc`` file. \ - This allows you to specify coding standards on a module-by-module \ - basis. Of course, a directory is judged to be a Python module if it \ +specify the default values. You can specify a configuration file on the +command line using the ``--rcfile`` option. Otherwise, Pylint searches for a +valid configuration file (defined below) in the following order and uses the +first one it finds: + +#. In the current working directory +#. If the current working directory is in a Python package, Pylint searches \ + up the hierarchy of Python packages until it finds a valid configuration \ + file. This allows you to specify coding standards on a package-by-package \ + basis. Of course, a directory is judged to be a Python package if it \ contains an ``__init__.py`` file. #. The file named by environment variable ``PYLINTRC`` -#. if you have a home directory which isn't ``/root``: +#. In your home directory if it isn't ``/root`` +#. In a ``.config/`` directory in your home directory if your home directory \ + isn't ``/root`` +#. In ``/etc/`` - #. ``.pylintrc`` in your home directory - #. ``.config/pylintrc`` in your home directory +A valid configuration file is one of the following (in order of priority): -#. ``/etc/pylintrc`` +#. ``pylintrc`` +#. ``.pylintrc`` +#. ``pyproject.toml``, provided it has at least one ``tool.pylint.`` section. +#. ``setup.cfg``, provided it has at least one ``pylint.`` section. The ``--generate-rcfile`` option will generate a commented configuration file on standard output according to the current configuration and exit. This diff --git a/doc/whatsnew/2.7.rst b/doc/whatsnew/2.7.rst index 35bdb9348b..2307334288 100644 --- a/doc/whatsnew/2.7.rst +++ b/doc/whatsnew/2.7.rst @@ -55,6 +55,8 @@ Other Changes * Fixes duplicate code detection for --jobs=2+ +* Add handling of all format of conf file from all locations (for example ``pyproject.toml`` in home directory) + * New option ``allowed-redefined-builtins`` defines variable names allowed to shadow builtins. * Improved protected access checks to allow access inside class methods diff --git a/pylint/config/find_default_config_files.py b/pylint/config/find_default_config_files.py index 126ac4a1e7..f1acbc0dba 100644 --- a/pylint/config/find_default_config_files.py +++ b/pylint/config/find_default_config_files.py @@ -3,6 +3,8 @@ import configparser import os +from pathlib import Path +from typing import Generator, List, Union import toml from toml.decoder import TomlDecodeError @@ -30,40 +32,40 @@ def _cfg_has_config(path): return any(section.startswith("pylint.") for section in parser.sections()) -def find_default_config_files(): - """Find all possible config files.""" - rc_names = ("pylintrc", ".pylintrc") - config_names = rc_names + ("pyproject.toml", "setup.cfg") +def _get_config_paths(curdir: Union[Path, str]) -> List[str]: + paths = [] + config_names = ("pylintrc", ".pylintrc", "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): + config_path = os.path.join(curdir, config_name) + if os.path.isfile(config_path): + if config_name.endswith(".toml") and not _toml_has_config(config_path): continue - if config_name.endswith(".cfg") and not _cfg_has_config(config_name): + if config_name.endswith(".cfg") and not _cfg_has_config(config_path): continue - yield os.path.abspath(config_name) + paths.append(config_path) + + return paths + + +def find_default_config_files() -> Generator: + """Find all possible config files.""" + yield from _get_config_paths(os.path.abspath(".")) 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 + yield from _get_config_paths(curdir) if "PYLINTRC" in os.environ and os.path.exists(os.environ["PYLINTRC"]): if os.path.isfile(os.environ["PYLINTRC"]): yield os.environ["PYLINTRC"] - 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 - - if os.path.isfile("/etc/pylintrc"): - yield "/etc/pylintrc" + + user_home = os.path.expanduser("~") + if user_home not in ("~", "/root"): + yield from _get_config_paths(user_home) + yield from _get_config_paths(os.path.join(user_home, ".config")) + + curdir = os.path.abspath("/etc") + yield from _get_config_paths(curdir) diff --git a/tests/test_config.py b/tests/test_config.py index ecfac3328c..3c857f98b1 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -2,6 +2,7 @@ import unittest.mock import pylint.lint +from pylint.config.find_default_config_files import _get_config_paths def check_configuration_file_reader(config_file): @@ -91,3 +92,16 @@ def test_can_read_toml_rich_types(tmp_path): """ ) check_configuration_file_reader(config_file) + + +def test_gets_correct_config_files(tmp_path): + toml = tmp_path / "pyproject.toml" + toml.write_text("[tool.pylint]") + rc = tmp_path / ".pylintrc" + rc.write_text("[]") + cfg = tmp_path / "not_a_pylint_config.cfg" + cfg.write_text("[pylint]") + paths = _get_config_paths(tmp_path) + assert len(paths) == 2 + assert rc.samefile(paths[0]) + assert toml.samefile(paths[1])