find_default_config_files.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  2. # For details: https://github.com/pylint-dev/pylint/blob/main/LICENSE
  3. # Copyright (c) https://github.com/pylint-dev/pylint/blob/main/CONTRIBUTORS.txt
  4. from __future__ import annotations
  5. import configparser
  6. import os
  7. import sys
  8. import warnings
  9. from collections.abc import Iterator
  10. from pathlib import Path
  11. if sys.version_info >= (3, 11):
  12. import tomllib
  13. else:
  14. import tomli as tomllib
  15. RC_NAMES = (Path("pylintrc"), Path(".pylintrc"))
  16. CONFIG_NAMES = RC_NAMES + (Path("pyproject.toml"), Path("setup.cfg"))
  17. def _toml_has_config(path: Path | str) -> bool:
  18. with open(path, mode="rb") as toml_handle:
  19. try:
  20. content = tomllib.load(toml_handle)
  21. except tomllib.TOMLDecodeError as error:
  22. print(f"Failed to load '{path}': {error}")
  23. return False
  24. return "pylint" in content.get("tool", [])
  25. def _cfg_has_config(path: Path | str) -> bool:
  26. parser = configparser.ConfigParser()
  27. try:
  28. parser.read(path, encoding="utf-8")
  29. except configparser.Error:
  30. return False
  31. return any(section.startswith("pylint.") for section in parser.sections())
  32. def _yield_default_files() -> Iterator[Path]:
  33. """Iterate over the default config file names and see if they exist."""
  34. for config_name in CONFIG_NAMES:
  35. try:
  36. if config_name.is_file():
  37. if config_name.suffix == ".toml" and not _toml_has_config(config_name):
  38. continue
  39. if config_name.suffix == ".cfg" and not _cfg_has_config(config_name):
  40. continue
  41. yield config_name.resolve()
  42. except OSError:
  43. pass
  44. def _find_project_config() -> Iterator[Path]:
  45. """Traverse up the directory tree to find a config file.
  46. Stop if no '__init__' is found and thus we are no longer in a package.
  47. """
  48. if Path("__init__.py").is_file():
  49. curdir = Path(os.getcwd()).resolve()
  50. while (curdir / "__init__.py").is_file():
  51. curdir = curdir.parent
  52. for rc_name in RC_NAMES:
  53. rc_path = curdir / rc_name
  54. if rc_path.is_file():
  55. yield rc_path.resolve()
  56. def _find_config_in_home_or_environment() -> Iterator[Path]:
  57. """Find a config file in the specified environment var or the home directory."""
  58. if "PYLINTRC" in os.environ and Path(os.environ["PYLINTRC"]).exists():
  59. if Path(os.environ["PYLINTRC"]).is_file():
  60. yield Path(os.environ["PYLINTRC"]).resolve()
  61. else:
  62. try:
  63. user_home = Path.home()
  64. except RuntimeError:
  65. # If the home directory does not exist a RuntimeError will be raised
  66. user_home = None
  67. if user_home is not None and str(user_home) not in ("~", "/root"):
  68. home_rc = user_home / ".pylintrc"
  69. if home_rc.is_file():
  70. yield home_rc.resolve()
  71. home_rc = user_home / ".config" / "pylintrc"
  72. if home_rc.is_file():
  73. yield home_rc.resolve()
  74. def find_default_config_files() -> Iterator[Path]:
  75. """Find all possible config files."""
  76. yield from _yield_default_files()
  77. try:
  78. yield from _find_project_config()
  79. except OSError:
  80. pass
  81. try:
  82. yield from _find_config_in_home_or_environment()
  83. except OSError:
  84. pass
  85. try:
  86. if os.path.isfile("/etc/pylintrc"):
  87. yield Path("/etc/pylintrc").resolve()
  88. except OSError:
  89. pass
  90. def find_pylintrc() -> str | None:
  91. """Search the pylint rc file and return its path if it finds it, else return
  92. None.
  93. """
  94. # TODO: 3.0: Remove deprecated function
  95. warnings.warn(
  96. "find_pylintrc and the PYLINTRC constant have been deprecated. "
  97. "Use find_default_config_files if you want access to pylint's configuration file "
  98. "finding logic.",
  99. DeprecationWarning,
  100. stacklevel=2,
  101. )
  102. for config_file in find_default_config_files():
  103. if str(config_file).endswith("pylintrc"):
  104. return str(config_file)
  105. return None