Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
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
37 changes: 19 additions & 18 deletions doc/user_guide/run.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Comment on lines +114 to +117
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if two or more valid files are found in the same location?
Maybe the doc should explain this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added that they were listed in order or priority. So if .pylintrc and setup.cfg are in the same location it'll pick .pylintrc. Is that clearer or do I need to expand more than just the note?


The ``--generate-rcfile`` option will generate a commented configuration file
on standard output according to the current configuration and exit. This
Expand Down
2 changes: 2 additions & 0 deletions doc/whatsnew/2.7.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
50 changes: 26 additions & 24 deletions pylint/config/find_default_config_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did the else statement disappear?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the else is redundant when there is a yield or return statement inside and if block?

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)
14 changes: 14 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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])