dunder.py 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  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. from typing import TYPE_CHECKING
  6. from astroid import nodes
  7. from pylint.checkers import BaseChecker
  8. from pylint.constants import DUNDER_METHODS, DUNDER_PROPERTIES, EXTRA_DUNDER_METHODS
  9. from pylint.interfaces import HIGH
  10. if TYPE_CHECKING:
  11. from pylint.lint import PyLinter
  12. class DunderChecker(BaseChecker):
  13. """Checks related to dunder methods."""
  14. name = "dunder"
  15. msgs = {
  16. "W3201": (
  17. "Bad or misspelled dunder method name %s.",
  18. "bad-dunder-name",
  19. "Used when a dunder method is misspelled or defined with a name "
  20. "not within the predefined list of dunder names.",
  21. ),
  22. }
  23. options = (
  24. (
  25. "good-dunder-names",
  26. {
  27. "default": [],
  28. "type": "csv",
  29. "metavar": "<comma-separated names>",
  30. "help": "Good dunder names which should always be accepted.",
  31. },
  32. ),
  33. )
  34. def open(self) -> None:
  35. self._dunder_methods = DUNDER_PROPERTIES + self.linter.config.good_dunder_names
  36. for since_vers, extra_dunder_methods in EXTRA_DUNDER_METHODS.items():
  37. if since_vers <= self.linter.config.py_version:
  38. self._dunder_methods.extend(extra_dunder_methods)
  39. for since_vers, dunder_methods in DUNDER_METHODS.items():
  40. if since_vers <= self.linter.config.py_version:
  41. self._dunder_methods.extend(list(dunder_methods.keys()))
  42. def visit_functiondef(self, node: nodes.FunctionDef) -> None:
  43. """Check if known dunder method is misspelled or dunder name is not one
  44. of the pre-defined names.
  45. """
  46. # ignore module-level functions
  47. if not node.is_method():
  48. return
  49. # Detect something that could be a bad dunder method
  50. if (
  51. node.name.startswith("_")
  52. and node.name.endswith("_")
  53. and node.name not in self._dunder_methods
  54. ):
  55. self.add_message(
  56. "bad-dunder-name",
  57. node=node,
  58. args=(node.name),
  59. confidence=HIGH,
  60. )
  61. def register(linter: PyLinter) -> None:
  62. linter.register_checker(DunderChecker(linter))