Skip to content

Suppress OSError in config file discovery #7423

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 2 commits into from
Sep 6, 2022
Merged
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
3 changes: 3 additions & 0 deletions doc/whatsnew/fragments/7169.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Don't crash on ``OSError`` in config file discovery.

Closes #7169
52 changes: 42 additions & 10 deletions pylint/config/find_default_config_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,17 +39,26 @@ def _cfg_has_config(path: Path | str) -> bool:
return any(section.startswith("pylint.") for section in parser.sections())


def find_default_config_files() -> Iterator[Path]:
"""Find all possible config files."""
def _yield_default_files() -> Iterator[Path]:
"""Iterate over the default config file names and see if they exist."""
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.suffix == ".cfg" and not _cfg_has_config(config_name):
continue
try:
if config_name.is_file():
if config_name.suffix == ".toml" and not _toml_has_config(config_name):
continue
if config_name.suffix == ".cfg" and not _cfg_has_config(config_name):
continue

yield config_name.resolve()
except OSError:
pass


yield config_name.resolve()
def _find_project_config() -> Iterator[Path]:
"""Traverse up the directory tree to find a config file.

Stop if no '__init__' is found and thus we are no longer in a package.
"""
if Path("__init__.py").is_file():
curdir = Path(os.getcwd()).resolve()
while (curdir / "__init__.py").is_file():
Expand All @@ -59,6 +68,9 @@ def find_default_config_files() -> Iterator[Path]:
if rc_path.is_file():
yield rc_path.resolve()


def _find_config_in_home_or_environment() -> Iterator[Path]:
"""Find a config file in the specified environment var or the home directory."""
if "PYLINTRC" in os.environ and Path(os.environ["PYLINTRC"]).exists():
if Path(os.environ["PYLINTRC"]).is_file():
yield Path(os.environ["PYLINTRC"]).resolve()
Expand All @@ -68,16 +80,36 @@ def find_default_config_files() -> Iterator[Path]:
except RuntimeError:
# If the home directory does not exist a RuntimeError will be raised
user_home = None

if user_home is not None and 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 Path("/etc/pylintrc").resolve()

def find_default_config_files() -> Iterator[Path]:
"""Find all possible config files."""
yield from _yield_default_files()

try:
yield from _find_project_config()
except OSError:
pass

try:
yield from _find_config_in_home_or_environment()
except OSError:
pass

try:
if os.path.isfile("/etc/pylintrc"):
yield Path("/etc/pylintrc").resolve()
except OSError:
pass


def find_pylintrc() -> str | None:
Expand Down
9 changes: 9 additions & 0 deletions tests/config/test_find_default_config_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -253,3 +253,12 @@ def test_non_existent_home() -> None:
assert not list(config.find_default_config_files())

os.chdir(current_dir)


def test_permission_error() -> None:
"""Test that we handle PermissionError correctly in find_default_config_files.

Reported in https://github.com/PyCQA/pylint/issues/7169.
"""
with mock.patch("pathlib.Path.is_file", side_effect=PermissionError):
list(config.find_default_config_files())