From e7a79bf90542dade0d0b9f91a334c95bd0164497 Mon Sep 17 00:00:00 2001 From: "Daniel R. Neal" Date: Fri, 7 Aug 2020 22:06:07 -0700 Subject: [PATCH 1/6] feat: Allow all config formats in all config locations --- doc/user_guide/run.rst | 29 ++++++------ pylint/config/find_default_config_files.py | 55 ++++++++++++---------- tests/test_config.py | 14 ++++++ 3 files changed, 60 insertions(+), 38 deletions(-) diff --git a/doc/user_guide/run.rst b/doc/user_guide/run.rst index d8268c36d4..ecb05e9ae3 100644 --- a/doc/user_guide/run.rst +++ b/doc/user_guide/run.rst @@ -85,26 +85,27 @@ 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 +configuration file 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 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 \ + up the hierarchy of Python modules until it finds a valid configuration \ + 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 \ 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: -#. ``/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/pylint/config/find_default_config_files.py b/pylint/config/find_default_config_files.py index 92d333f0fd..a1764044e8 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 @@ -24,40 +26,45 @@ 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.""" + for path in _get_config_paths(os.path.abspath(".")): + yield (path) 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 + for path in _get_config_paths(curdir): + yield (path) 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"): + for path in _get_config_paths(user_home): + yield path + for path in _get_config_paths(os.path.join(user_home, ".config")): + yield path + + curdir = os.path.abspath("/etc") + for path in _get_config_paths(curdir): + yield path 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]) From d866dfa08242361bb16fa8f3a98fdb53f9330a12 Mon Sep 17 00:00:00 2001 From: Pierre Sassoulas Date: Sat, 20 Feb 2021 16:29:24 +0100 Subject: [PATCH 2/6] Upgrade the documentation --- doc/whatsnew/2.7.rst | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/doc/whatsnew/2.7.rst b/doc/whatsnew/2.7.rst index 520c68cd5a..55d48d1eff 100644 --- a/doc/whatsnew/2.7.rst +++ b/doc/whatsnew/2.7.rst @@ -15,18 +15,18 @@ Summary -- Release highlights New checkers ============ -* Add `nan-comparison` check for comparison of NaN values +* Add ``nan-comparison`` check for comparison of NaN values * Add support to ``ignored-argument-names`` in DocstringParameterChecker and - adds `useless-param-doc` and `useless-type-doc` messages. + adds ``useless-param-doc`` and ``useless-type-doc`` messages. -* Add `empty-comment` check for empty comments. +* Add ``empty-comment`` check for empty comments. -* Add `simplifiable-condition` check for extraneous constants in conditionals using and/or. +* Add ``simplifiable-condition`` check for extraneous constants in conditionals using and/or. -* Add `condition-evals-to-constant` check for conditionals using and/or that evaluate to a constant. +* Add ``condition-evals-to-constant`` check for conditionals using and/or that evaluate to a constant. -* Add `consider-using-generator` check for the use of list comprehension inside ``any`` or ``all`` function. +* Add ``consider-using-generator`` check for the use of list comprehension inside ``any`` or ``all`` function. Other Changes ============= @@ -49,6 +49,8 @@ Other Changes * Fix vulnerable regular expressions in ``pyreverse``. The ambiguities of vulnerable regular expressions are removed, making the repaired regular expressions safer and faster matching. -* `len-as-conditions` is now triggered only for classes that are inheriting directly from list, dict, or set and not implementing the `__bool__` function, or from generators like range or list/dict/set comprehension. This should reduce the false positive for other classes, like pandas's DataFrame or numpy's Array. +* ``len-as-conditions`` is now triggered only for classes that are inheriting directly from list, dict, or set and not implementing the ``__bool__`` function, or from generators like range or list/dict/set comprehension. This should reduce the false positive for other classes, like pandas's DataFrame or numpy's Array. * 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) From 1869189789249894b96a85af31947bb4f19780fd Mon Sep 17 00:00:00 2001 From: Daniel Neal Date: Fri, 9 Apr 2021 21:20:07 -0700 Subject: [PATCH 3/6] Update run.rst --- doc/user_guide/run.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/user_guide/run.rst b/doc/user_guide/run.rst index ecb05e9ae3..cb13fd58e3 100644 --- a/doc/user_guide/run.rst +++ b/doc/user_guide/run.rst @@ -85,8 +85,8 @@ 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 valid configuration file (defined below) in the following -order and uses the first one it finds: +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 module, Pylint searches \ @@ -100,7 +100,7 @@ order and uses the first one it finds: isn't ``/root`` #. In ``/etc/`` -A valid configuration file is one of the following: +A valid configuration file is one of the following (in order of priority): #. ``pylintrc`` #. ``.pylintrc`` From 1a947583e39da84ef77e134c9e672ace77e63a5a Mon Sep 17 00:00:00 2001 From: Daniel Neal Date: Fri, 9 Apr 2021 21:29:13 -0700 Subject: [PATCH 4/6] Update run.rst --- doc/user_guide/run.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/user_guide/run.rst b/doc/user_guide/run.rst index cb13fd58e3..b8361f751c 100644 --- a/doc/user_guide/run.rst +++ b/doc/user_guide/run.rst @@ -89,10 +89,10 @@ 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 module, Pylint searches \ - up the hierarchy of Python modules until it finds a valid configuration \ - 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 \ +#. 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`` #. In your home directory if it isn't ``/root`` From e7d1f5e4d5eac56ecf29a9782156c73e8f82b9c9 Mon Sep 17 00:00:00 2001 From: Daniel Neal Date: Fri, 9 Apr 2021 21:47:38 -0700 Subject: [PATCH 5/6] Update run.rst --- doc/user_guide/run.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/user_guide/run.rst b/doc/user_guide/run.rst index 2894870390..08b0819e2c 100644 --- a/doc/user_guide/run.rst +++ b/doc/user_guide/run.rst @@ -92,8 +92,8 @@ 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 +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: From 13d3f4e8703bbc2b8efc811d1d46c0f4236552a9 Mon Sep 17 00:00:00 2001 From: Dan Neal Date: Fri, 9 Apr 2021 21:56:07 -0700 Subject: [PATCH 6/6] fix pyupgrade precommit --- pylint/config/find_default_config_files.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/pylint/config/find_default_config_files.py b/pylint/config/find_default_config_files.py index 540372dd41..f1acbc0dba 100644 --- a/pylint/config/find_default_config_files.py +++ b/pylint/config/find_default_config_files.py @@ -50,15 +50,13 @@ def _get_config_paths(curdir: Union[Path, str]) -> List[str]: def find_default_config_files() -> Generator: """Find all possible config files.""" - for path in _get_config_paths(os.path.abspath(".")): - yield (path) + 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 path in _get_config_paths(curdir): - yield (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"]): @@ -66,11 +64,8 @@ def find_default_config_files() -> Generator: user_home = os.path.expanduser("~") if user_home not in ("~", "/root"): - for path in _get_config_paths(user_home): - yield path - for path in _get_config_paths(os.path.join(user_home, ".config")): - yield path + yield from _get_config_paths(user_home) + yield from _get_config_paths(os.path.join(user_home, ".config")) curdir = os.path.abspath("/etc") - for path in _get_config_paths(curdir): - yield path + yield from _get_config_paths(curdir)