dunder.py 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  2. # For details: https://github.com/PyCQA/pylint/blob/main/LICENSE
  3. # Copyright (c) https://github.com/PyCQA/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. priority = -1
  16. msgs = {
  17. "W3201": (
  18. "Bad or misspelled dunder method name %s.",
  19. "bad-dunder-name",
  20. "Used when a dunder method is misspelled or defined with a name "
  21. "not within the predefined list of dunder names.",
  22. ),
  23. }
  24. options = (
  25. (
  26. "good-dunder-names",
  27. {
  28. "default": [],
  29. "type": "csv",
  30. "metavar": "<comma-separated names>",
  31. "help": "Good dunder names which should always be accepted.",
  32. },
  33. ),
  34. )
  35. def open(self) -> None:
  36. self._dunder_methods = (
  37. EXTRA_DUNDER_METHODS
  38. + DUNDER_PROPERTIES
  39. + self.linter.config.good_dunder_names
  40. )
  41. for since_vers, dunder_methods in DUNDER_METHODS.items():
  42. if since_vers <= self.linter.config.py_version:
  43. self._dunder_methods.extend(list(dunder_methods.keys()))
  44. def visit_functiondef(self, node: nodes.FunctionDef) -> None:
  45. """Check if known dunder method is misspelled or dunder name is not one
  46. of the pre-defined names.
  47. """
  48. # ignore module-level functions
  49. if not node.is_method():
  50. return
  51. # Detect something that could be a bad dunder method
  52. if (
  53. node.name.startswith("_")
  54. and node.name.endswith("_")
  55. and node.name not in self._dunder_methods
  56. ):
  57. self.add_message(
  58. "bad-dunder-name",
  59. node=node,
  60. args=(node.name),
  61. confidence=HIGH,
  62. )
  63. def register(linter: PyLinter) -> None:
  64. linter.register_checker(DunderChecker(linter))