Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

newstyle.py 7.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. # Copyright (c) 2006, 2008-2011, 2013-2014 LOGILAB S.A. (Paris, FRANCE) <contact@logilab.fr>
  2. # Copyright (c) 2012-2014 Google, Inc.
  3. # Copyright (c) 2013-2017 Claudiu Popa <pcmanticore@gmail.com>
  4. # Copyright (c) 2014 Michal Nowikowski <godfryd@gmail.com>
  5. # Copyright (c) 2014 Brett Cannon <brett@python.org>
  6. # Copyright (c) 2014 Arun Persaud <arun@nubati.net>
  7. # Copyright (c) 2015 Ionel Cristian Maries <contact@ionelmc.ro>
  8. # Copyright (c) 2016 Alexander Todorov <atodorov@otb.bg>
  9. # Copyright (c) 2016 Jakub Wilk <jwilk@jwilk.net>
  10. # Licensed under the GPL: https://www.gnu.org/licenses/old-licenses/gpl-2.0.html
  11. # For details: https://github.com/PyCQA/pylint/blob/master/COPYING
  12. """check for new / old style related problems
  13. """
  14. import sys
  15. import astroid
  16. from pylint.interfaces import IAstroidChecker, INFERENCE, INFERENCE_FAILURE, HIGH
  17. from pylint.checkers import BaseChecker
  18. from pylint.checkers.utils import (
  19. check_messages,
  20. node_frame_class,
  21. has_known_bases
  22. )
  23. MSGS = {
  24. 'E1001': ('Use of __slots__ on an old style class',
  25. 'slots-on-old-class',
  26. 'Used when an old style class uses the __slots__ attribute.',
  27. {'maxversion': (3, 0)}),
  28. 'E1002': ('Use of super on an old style class',
  29. 'super-on-old-class',
  30. 'Used when an old style class uses the super builtin.',
  31. {'maxversion': (3, 0)}),
  32. 'E1003': ('Bad first argument %r given to super()',
  33. 'bad-super-call',
  34. 'Used when another argument than the current class is given as \
  35. first argument of the super builtin.'),
  36. 'E1004': ('Missing argument to super()',
  37. 'missing-super-argument',
  38. 'Used when the super builtin didn\'t receive an \
  39. argument.',
  40. {'maxversion': (3, 0)}),
  41. 'W1001': ('Use of "property" on an old style class',
  42. 'property-on-old-class',
  43. 'Used when Pylint detect the use of the builtin "property" \
  44. on an old style class while this is relying on new style \
  45. classes features.',
  46. {'maxversion': (3, 0)}),
  47. 'C1001': ('Old-style class defined.',
  48. 'old-style-class',
  49. 'Used when a class is defined that does not inherit from another '
  50. 'class and does not inherit explicitly from "object".',
  51. {'maxversion': (3, 0)})
  52. }
  53. class NewStyleConflictChecker(BaseChecker):
  54. """checks for usage of new style capabilities on old style classes and
  55. other new/old styles conflicts problems
  56. * use of property, __slots__, super
  57. * "super" usage
  58. """
  59. __implements__ = (IAstroidChecker,)
  60. # configuration section name
  61. name = 'newstyle'
  62. # messages
  63. msgs = MSGS
  64. priority = -2
  65. # configuration options
  66. options = ()
  67. @check_messages('slots-on-old-class', 'old-style-class')
  68. def visit_classdef(self, node):
  69. """ Check __slots__ in old style classes and old
  70. style class definition.
  71. """
  72. if '__slots__' in node and not node.newstyle:
  73. confidence = (INFERENCE if has_known_bases(node)
  74. else INFERENCE_FAILURE)
  75. self.add_message('slots-on-old-class', node=node,
  76. confidence=confidence)
  77. # The node type could be class, exception, metaclass, or
  78. # interface. Presumably, the non-class-type nodes would always
  79. # have an explicit base class anyway.
  80. if not node.bases and node.type == 'class' and not node.metaclass():
  81. # We use confidence HIGH here because this message should only ever
  82. # be emitted for classes at the root of the inheritance hierarchyself.
  83. self.add_message('old-style-class', node=node, confidence=HIGH)
  84. @check_messages('property-on-old-class')
  85. def visit_call(self, node):
  86. """check property usage"""
  87. parent = node.parent.frame()
  88. if (isinstance(parent, astroid.ClassDef) and
  89. not parent.newstyle and
  90. isinstance(node.func, astroid.Name)):
  91. confidence = (INFERENCE if has_known_bases(parent)
  92. else INFERENCE_FAILURE)
  93. name = node.func.name
  94. if name == 'property':
  95. self.add_message('property-on-old-class', node=node,
  96. confidence=confidence)
  97. @check_messages('super-on-old-class', 'bad-super-call', 'missing-super-argument')
  98. def visit_functiondef(self, node):
  99. """check use of super"""
  100. # ignore actual functions or method within a new style class
  101. if not node.is_method():
  102. return
  103. klass = node.parent.frame()
  104. for stmt in node.nodes_of_class(astroid.Call):
  105. if node_frame_class(stmt) != node_frame_class(node):
  106. # Don't look down in other scopes.
  107. continue
  108. expr = stmt.func
  109. if not isinstance(expr, astroid.Attribute):
  110. continue
  111. call = expr.expr
  112. # skip the test if using super
  113. if not (isinstance(call, astroid.Call) and
  114. isinstance(call.func, astroid.Name) and
  115. call.func.name == 'super'):
  116. continue
  117. if not klass.newstyle and has_known_bases(klass):
  118. # super should not be used on an old style class
  119. self.add_message('super-on-old-class', node=node)
  120. else:
  121. # super first arg should be the class
  122. if not call.args:
  123. if sys.version_info[0] == 3:
  124. # unless Python 3
  125. continue
  126. else:
  127. self.add_message('missing-super-argument', node=call)
  128. continue
  129. # calling super(type(self), self) can lead to recursion loop
  130. # in derived classes
  131. arg0 = call.args[0]
  132. if isinstance(arg0, astroid.Call) and \
  133. isinstance(arg0.func, astroid.Name) and \
  134. arg0.func.name == 'type':
  135. self.add_message('bad-super-call', node=call, args=('type', ))
  136. continue
  137. # calling super(self.__class__, self) can lead to recursion loop
  138. # in derived classes
  139. if len(call.args) >= 2 and \
  140. isinstance(call.args[1], astroid.Name) and \
  141. call.args[1].name == 'self' and \
  142. isinstance(arg0, astroid.Attribute) and \
  143. arg0.attrname == '__class__':
  144. self.add_message('bad-super-call', node=call, args=('self.__class__', ))
  145. continue
  146. try:
  147. supcls = call.args and next(call.args[0].infer(), None)
  148. except astroid.InferenceError:
  149. continue
  150. if klass is not supcls:
  151. name = None
  152. # if supcls is not YES, then supcls was infered
  153. # and use its name. Otherwise, try to look
  154. # for call.args[0].name
  155. if supcls:
  156. name = supcls.name
  157. elif call.args and hasattr(call.args[0], 'name'):
  158. name = call.args[0].name
  159. if name:
  160. self.add_message('bad-super-call', node=call, args=(name, ))
  161. visit_asyncfunctiondef = visit_functiondef
  162. def register(linter):
  163. """required method to auto register this checker """
  164. linter.register_checker(NewStyleConflictChecker(linter))