newstyle.py 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  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. """Check for new / old style related problems."""
  5. from __future__ import annotations
  6. from typing import TYPE_CHECKING
  7. import astroid
  8. from astroid import nodes
  9. from pylint.checkers import BaseChecker
  10. from pylint.checkers.utils import node_frame_class, only_required_for_messages
  11. from pylint.typing import MessageDefinitionTuple
  12. if TYPE_CHECKING:
  13. from pylint.lint import PyLinter
  14. MSGS: dict[str, MessageDefinitionTuple] = {
  15. "E1003": (
  16. "Bad first argument %r given to super()",
  17. "bad-super-call",
  18. "Used when another argument than the current class is given as "
  19. "first argument of the super builtin.",
  20. )
  21. }
  22. class NewStyleConflictChecker(BaseChecker):
  23. """Checks for usage of new style capabilities on old style classes and
  24. other new/old styles conflicts problems.
  25. * use of property, __slots__, super
  26. * "super" usage
  27. """
  28. # configuration section name
  29. name = "newstyle"
  30. # messages
  31. msgs = MSGS
  32. # configuration options
  33. options = ()
  34. @only_required_for_messages("bad-super-call")
  35. def visit_functiondef(self, node: nodes.FunctionDef) -> None:
  36. """Check use of super."""
  37. # ignore actual functions or method within a new style class
  38. if not node.is_method():
  39. return
  40. klass = node.parent.frame()
  41. for stmt in node.nodes_of_class(nodes.Call):
  42. if node_frame_class(stmt) != node_frame_class(node):
  43. # Don't look down in other scopes.
  44. continue
  45. expr = stmt.func
  46. if not isinstance(expr, nodes.Attribute):
  47. continue
  48. match call := expr.expr:
  49. case nodes.Call(func=nodes.Name(name="super"), args=[arg0, *_]):
  50. pass
  51. case _:
  52. # skip the test if using super
  53. # super first arg should not be the class
  54. continue
  55. # calling super(type(self), self) can lead to recursion loop
  56. # in derived classes
  57. match arg0:
  58. case nodes.Call(func=nodes.Name(name="type")):
  59. self.add_message("bad-super-call", node=call, args=("type",))
  60. continue
  61. # calling super(self.__class__, self) can lead to recursion loop
  62. # in derived classes
  63. match call.args:
  64. case [
  65. nodes.Attribute(attrname="__class__"),
  66. nodes.Name(name="self"),
  67. *_,
  68. ]:
  69. self.add_message(
  70. "bad-super-call", node=call, args=("self.__class__",)
  71. )
  72. continue
  73. try:
  74. supcls = call.args and next(call.args[0].infer(), None)
  75. except astroid.InferenceError:
  76. continue
  77. # If the supcls is in the ancestors of klass super can be used to skip
  78. # a step in the mro() and get a method from a higher parent
  79. if klass is not supcls and all(i != supcls for i in klass.ancestors()):
  80. name = None
  81. # if supcls is not Uninferable, then supcls was inferred
  82. # and use its name. Otherwise, try to look
  83. # for call.args[0].name
  84. if supcls:
  85. name = supcls.name
  86. elif call.args and hasattr(call.args[0], "name"):
  87. name = call.args[0].name
  88. if name:
  89. self.add_message("bad-super-call", node=call, args=(name,))
  90. visit_asyncfunctiondef = visit_functiondef
  91. def register(linter: PyLinter) -> None:
  92. linter.register_checker(NewStyleConflictChecker(linter))