class_checker.py 88 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333
  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. """Classes checker for Python code."""
  5. from __future__ import annotations
  6. import collections
  7. import sys
  8. from collections import defaultdict
  9. from collections.abc import Callable, Sequence
  10. from itertools import chain, zip_longest
  11. from re import Pattern
  12. from typing import TYPE_CHECKING, Any, Union
  13. import astroid
  14. from astroid import bases, nodes, util
  15. from astroid.nodes import LocalsDictNodeNG
  16. from astroid.typing import SuccessfulInferenceResult
  17. from pylint.checkers import BaseChecker, utils
  18. from pylint.checkers.utils import (
  19. PYMETHODS,
  20. class_is_abstract,
  21. decorated_with,
  22. decorated_with_property,
  23. get_outer_class,
  24. has_known_bases,
  25. is_attr_private,
  26. is_attr_protected,
  27. is_builtin_object,
  28. is_comprehension,
  29. is_iterable,
  30. is_property_setter,
  31. is_property_setter_or_deleter,
  32. node_frame_class,
  33. only_required_for_messages,
  34. safe_infer,
  35. unimplemented_abstract_methods,
  36. uninferable_final_decorators,
  37. )
  38. from pylint.interfaces import HIGH, INFERENCE
  39. from pylint.typing import MessageDefinitionTuple
  40. if TYPE_CHECKING:
  41. from pylint.lint.pylinter import PyLinter
  42. if sys.version_info >= (3, 8):
  43. from functools import cached_property
  44. else:
  45. from astroid.decorators import cachedproperty as cached_property
  46. _AccessNodes = Union[nodes.Attribute, nodes.AssignAttr]
  47. INVALID_BASE_CLASSES = {"bool", "range", "slice", "memoryview"}
  48. BUILTIN_DECORATORS = {"builtins.property", "builtins.classmethod"}
  49. ASTROID_TYPE_COMPARATORS = {
  50. nodes.Const: lambda a, b: a.value == b.value,
  51. nodes.ClassDef: lambda a, b: a.qname == b.qname,
  52. nodes.Tuple: lambda a, b: a.elts == b.elts,
  53. nodes.List: lambda a, b: a.elts == b.elts,
  54. nodes.Dict: lambda a, b: a.items == b.items,
  55. nodes.Name: lambda a, b: set(a.infer()) == set(b.infer()),
  56. }
  57. # Dealing with useless override detection, with regard
  58. # to parameters vs arguments
  59. _CallSignature = collections.namedtuple(
  60. "_CallSignature", "args kws starred_args starred_kws"
  61. )
  62. _ParameterSignature = collections.namedtuple(
  63. "_ParameterSignature", "args kwonlyargs varargs kwargs"
  64. )
  65. def _signature_from_call(call: nodes.Call) -> _CallSignature:
  66. kws = {}
  67. args = []
  68. starred_kws = []
  69. starred_args = []
  70. for keyword in call.keywords or []:
  71. arg, value = keyword.arg, keyword.value
  72. if arg is None and isinstance(value, nodes.Name):
  73. # Starred node, and we are interested only in names,
  74. # otherwise some transformation might occur for the parameter.
  75. starred_kws.append(value.name)
  76. elif isinstance(value, nodes.Name):
  77. kws[arg] = value.name
  78. else:
  79. kws[arg] = None
  80. for arg in call.args:
  81. if isinstance(arg, nodes.Starred) and isinstance(arg.value, nodes.Name):
  82. # Positional variadic and a name, otherwise some transformation
  83. # might have occurred.
  84. starred_args.append(arg.value.name)
  85. elif isinstance(arg, nodes.Name):
  86. args.append(arg.name)
  87. else:
  88. args.append(None)
  89. return _CallSignature(args, kws, starred_args, starred_kws)
  90. def _signature_from_arguments(arguments: nodes.Arguments) -> _ParameterSignature:
  91. kwarg = arguments.kwarg
  92. vararg = arguments.vararg
  93. args = [
  94. arg.name
  95. for arg in chain(arguments.posonlyargs, arguments.args)
  96. if arg.name != "self"
  97. ]
  98. kwonlyargs = [arg.name for arg in arguments.kwonlyargs]
  99. return _ParameterSignature(args, kwonlyargs, vararg, kwarg)
  100. def _definition_equivalent_to_call(
  101. definition: _ParameterSignature, call: _CallSignature
  102. ) -> bool:
  103. """Check if a definition signature is equivalent to a call."""
  104. if definition.kwargs:
  105. if definition.kwargs not in call.starred_kws:
  106. return False
  107. elif call.starred_kws:
  108. return False
  109. if definition.varargs:
  110. if definition.varargs not in call.starred_args:
  111. return False
  112. elif call.starred_args:
  113. return False
  114. if any(kw not in call.kws for kw in definition.kwonlyargs):
  115. return False
  116. if definition.args != call.args:
  117. return False
  118. # No extra kwargs in call.
  119. return all(kw in call.args or kw in definition.kwonlyargs for kw in call.kws)
  120. def _is_trivial_super_delegation(function: nodes.FunctionDef) -> bool:
  121. """Check whether a function definition is a method consisting only of a
  122. call to the same function on the superclass.
  123. """
  124. if (
  125. not function.is_method()
  126. # Adding decorators to a function changes behavior and
  127. # constitutes a non-trivial change.
  128. or function.decorators
  129. ):
  130. return False
  131. body = function.body
  132. if len(body) != 1:
  133. # Multiple statements, which means this overridden method
  134. # could do multiple things we are not aware of.
  135. return False
  136. statement = body[0]
  137. if not isinstance(statement, (nodes.Expr, nodes.Return)):
  138. # Doing something else than what we are interested in.
  139. return False
  140. call = statement.value
  141. if (
  142. not isinstance(call, nodes.Call)
  143. # Not a super() attribute access.
  144. or not isinstance(call.func, nodes.Attribute)
  145. ):
  146. return False
  147. # Anything other than a super call is non-trivial.
  148. super_call = safe_infer(call.func.expr)
  149. if not isinstance(super_call, astroid.objects.Super):
  150. return False
  151. # The name should be the same.
  152. if call.func.attrname != function.name:
  153. return False
  154. # Should be a super call with the MRO pointer being the
  155. # current class and the type being the current instance.
  156. current_scope = function.parent.scope()
  157. if (
  158. super_call.mro_pointer != current_scope
  159. or not isinstance(super_call.type, astroid.Instance)
  160. or super_call.type.name != current_scope.name
  161. ):
  162. return False
  163. return True
  164. # Deal with parameters overriding in two methods.
  165. def _positional_parameters(method: nodes.FunctionDef) -> list[nodes.AssignName]:
  166. positional = method.args.args
  167. if method.is_bound() and method.type in {"classmethod", "method"}:
  168. positional = positional[1:]
  169. return positional # type: ignore[no-any-return]
  170. class _DefaultMissing:
  171. """Sentinel value for missing arg default, use _DEFAULT_MISSING."""
  172. _DEFAULT_MISSING = _DefaultMissing()
  173. def _has_different_parameters_default_value(
  174. original: nodes.Arguments, overridden: nodes.Arguments
  175. ) -> bool:
  176. """Check if original and overridden methods arguments have different default values.
  177. Return True if one of the overridden arguments has a default
  178. value different from the default value of the original argument
  179. If one of the method doesn't have argument (.args is None)
  180. return False
  181. """
  182. if original.args is None or overridden.args is None:
  183. return False
  184. for param in chain(original.args, original.kwonlyargs):
  185. try:
  186. original_default = original.default_value(param.name)
  187. except astroid.exceptions.NoDefault:
  188. original_default = _DEFAULT_MISSING
  189. try:
  190. overridden_default = overridden.default_value(param.name)
  191. if original_default is _DEFAULT_MISSING:
  192. # Only the original has a default.
  193. return True
  194. except astroid.exceptions.NoDefault:
  195. if original_default is _DEFAULT_MISSING:
  196. # Both have a default, no difference
  197. continue
  198. # Only the override has a default.
  199. return True
  200. original_type = type(original_default)
  201. if not isinstance(overridden_default, original_type):
  202. # Two args with same name but different types
  203. return True
  204. is_same_fn: Callable[[Any, Any], bool] | None = ASTROID_TYPE_COMPARATORS.get(
  205. original_type
  206. )
  207. if is_same_fn is None:
  208. # If the default value comparison is unhandled, assume the value is different
  209. return True
  210. if not is_same_fn(original_default, overridden_default):
  211. # Two args with same type but different values
  212. return True
  213. return False
  214. def _has_different_parameters(
  215. original: list[nodes.AssignName],
  216. overridden: list[nodes.AssignName],
  217. dummy_parameter_regex: Pattern[str],
  218. ) -> list[str]:
  219. result: list[str] = []
  220. zipped = zip_longest(original, overridden)
  221. for original_param, overridden_param in zipped:
  222. if not overridden_param:
  223. return ["Number of parameters "]
  224. if not original_param:
  225. try:
  226. overridden_param.parent.default_value(overridden_param.name)
  227. continue
  228. except astroid.NoDefault:
  229. return ["Number of parameters "]
  230. # check for the arguments' name
  231. names = [param.name for param in (original_param, overridden_param)]
  232. if any(dummy_parameter_regex.match(name) for name in names):
  233. continue
  234. if original_param.name != overridden_param.name:
  235. result.append(
  236. f"Parameter '{original_param.name}' has been renamed "
  237. f"to '{overridden_param.name}' in"
  238. )
  239. return result
  240. def _has_different_keyword_only_parameters(
  241. original: list[nodes.AssignName],
  242. overridden: list[nodes.AssignName],
  243. ) -> list[str]:
  244. """Determine if the two methods have different keyword only parameters."""
  245. original_names = [i.name for i in original]
  246. overridden_names = [i.name for i in overridden]
  247. if any(name not in overridden_names for name in original_names):
  248. return ["Number of parameters "]
  249. for name in overridden_names:
  250. if name in original_names:
  251. continue
  252. try:
  253. overridden[0].parent.default_value(name)
  254. except astroid.NoDefault:
  255. return ["Number of parameters "]
  256. return []
  257. def _different_parameters(
  258. original: nodes.FunctionDef,
  259. overridden: nodes.FunctionDef,
  260. dummy_parameter_regex: Pattern[str],
  261. ) -> list[str]:
  262. """Determine if the two methods have different parameters.
  263. They are considered to have different parameters if:
  264. * they have different positional parameters, including different names
  265. * one of the methods is having variadics, while the other is not
  266. * they have different keyword only parameters.
  267. """
  268. output_messages = []
  269. original_parameters = _positional_parameters(original)
  270. overridden_parameters = _positional_parameters(overridden)
  271. # Copy kwonlyargs list so that we don't affect later function linting
  272. original_kwonlyargs = original.args.kwonlyargs
  273. # Allow positional/keyword variadic in overridden to match against any
  274. # positional/keyword argument in original.
  275. # Keep any arguments that are found separately in overridden to satisfy
  276. # later tests
  277. if overridden.args.vararg:
  278. overridden_names = [v.name for v in overridden_parameters]
  279. original_parameters = [
  280. v for v in original_parameters if v.name in overridden_names
  281. ]
  282. if overridden.args.kwarg:
  283. overridden_names = [v.name for v in overridden.args.kwonlyargs]
  284. original_kwonlyargs = [
  285. v for v in original.args.kwonlyargs if v.name in overridden_names
  286. ]
  287. different_positional = _has_different_parameters(
  288. original_parameters, overridden_parameters, dummy_parameter_regex
  289. )
  290. different_kwonly = _has_different_keyword_only_parameters(
  291. original_kwonlyargs, overridden.args.kwonlyargs
  292. )
  293. if different_kwonly and different_positional:
  294. if "Number " in different_positional[0] and "Number " in different_kwonly[0]:
  295. output_messages.append("Number of parameters ")
  296. output_messages += different_positional[1:]
  297. output_messages += different_kwonly[1:]
  298. else:
  299. output_messages += different_positional
  300. output_messages += different_kwonly
  301. else:
  302. if different_positional:
  303. output_messages += different_positional
  304. if different_kwonly:
  305. output_messages += different_kwonly
  306. if original.name in PYMETHODS:
  307. # Ignore the difference for special methods. If the parameter
  308. # numbers are different, then that is going to be caught by
  309. # unexpected-special-method-signature.
  310. # If the names are different, it doesn't matter, since they can't
  311. # be used as keyword arguments anyway.
  312. output_messages.clear()
  313. # Arguments will only violate LSP if there are variadics in the original
  314. # that are then removed from the overridden
  315. kwarg_lost = original.args.kwarg and not overridden.args.kwarg
  316. vararg_lost = original.args.vararg and not overridden.args.vararg
  317. if kwarg_lost or vararg_lost:
  318. output_messages += ["Variadics removed in"]
  319. return output_messages
  320. def _is_invalid_base_class(cls: nodes.ClassDef) -> bool:
  321. return cls.name in INVALID_BASE_CLASSES and is_builtin_object(cls)
  322. def _has_data_descriptor(cls: nodes.ClassDef, attr: str) -> bool:
  323. attributes = cls.getattr(attr)
  324. for attribute in attributes:
  325. try:
  326. for inferred in attribute.infer():
  327. if isinstance(inferred, astroid.Instance):
  328. try:
  329. inferred.getattr("__get__")
  330. inferred.getattr("__set__")
  331. except astroid.NotFoundError:
  332. continue
  333. else:
  334. return True
  335. except astroid.InferenceError:
  336. # Can't infer, avoid emitting a false positive in this case.
  337. return True
  338. return False
  339. def _called_in_methods(
  340. func: LocalsDictNodeNG,
  341. klass: nodes.ClassDef,
  342. methods: Sequence[str],
  343. ) -> bool:
  344. """Check if the func was called in any of the given methods,
  345. belonging to the *klass*.
  346. Returns True if so, False otherwise.
  347. """
  348. if not isinstance(func, nodes.FunctionDef):
  349. return False
  350. for method in methods:
  351. try:
  352. inferred = klass.getattr(method)
  353. except astroid.NotFoundError:
  354. continue
  355. for infer_method in inferred:
  356. for call in infer_method.nodes_of_class(nodes.Call):
  357. try:
  358. bound = next(call.func.infer())
  359. except (astroid.InferenceError, StopIteration):
  360. continue
  361. if not isinstance(bound, astroid.BoundMethod):
  362. continue
  363. func_obj = bound._proxied
  364. if isinstance(func_obj, astroid.UnboundMethod):
  365. func_obj = func_obj._proxied
  366. if func_obj.name == func.name:
  367. return True
  368. return False
  369. def _is_attribute_property(name: str, klass: nodes.ClassDef) -> bool:
  370. """Check if the given attribute *name* is a property in the given *klass*.
  371. It will look for `property` calls or for functions
  372. with the given name, decorated by `property` or `property`
  373. subclasses.
  374. Returns ``True`` if the name is a property in the given klass,
  375. ``False`` otherwise.
  376. """
  377. try:
  378. attributes = klass.getattr(name)
  379. except astroid.NotFoundError:
  380. return False
  381. property_name = "builtins.property"
  382. for attr in attributes:
  383. if isinstance(attr, util.UninferableBase):
  384. continue
  385. try:
  386. inferred = next(attr.infer())
  387. except astroid.InferenceError:
  388. continue
  389. if isinstance(inferred, nodes.FunctionDef) and decorated_with_property(
  390. inferred
  391. ):
  392. return True
  393. if inferred.pytype() != property_name:
  394. continue
  395. cls = node_frame_class(inferred)
  396. if cls == klass.declared_metaclass():
  397. continue
  398. return True
  399. return False
  400. def _has_same_layout_slots(
  401. slots: list[nodes.Const | None], assigned_value: nodes.Name
  402. ) -> bool:
  403. inferred = next(assigned_value.infer())
  404. if isinstance(inferred, nodes.ClassDef):
  405. other_slots = inferred.slots()
  406. if all(
  407. first_slot and second_slot and first_slot.value == second_slot.value
  408. for (first_slot, second_slot) in zip_longest(slots, other_slots)
  409. ):
  410. return True
  411. return False
  412. MSGS: dict[str, MessageDefinitionTuple] = {
  413. "F0202": (
  414. "Unable to check methods signature (%s / %s)",
  415. "method-check-failed",
  416. "Used when Pylint has been unable to check methods signature "
  417. "compatibility for an unexpected reason. Please report this kind "
  418. "if you don't make sense of it.",
  419. ),
  420. "E0202": (
  421. "An attribute defined in %s line %s hides this method",
  422. "method-hidden",
  423. "Used when a class defines a method which is hidden by an "
  424. "instance attribute from an ancestor class or set by some "
  425. "client code.",
  426. ),
  427. "E0203": (
  428. "Access to member %r before its definition line %s",
  429. "access-member-before-definition",
  430. "Used when an instance member is accessed before it's actually assigned.",
  431. ),
  432. "W0201": (
  433. "Attribute %r defined outside __init__",
  434. "attribute-defined-outside-init",
  435. "Used when an instance attribute is defined outside the __init__ method.",
  436. ),
  437. "W0212": (
  438. "Access to a protected member %s of a client class", # E0214
  439. "protected-access",
  440. "Used when a protected member (i.e. class member with a name "
  441. "beginning with an underscore) is access outside the class or a "
  442. "descendant of the class where it's defined.",
  443. ),
  444. "W0213": (
  445. "Flag member %(overlap)s shares bit positions with %(sources)s",
  446. "implicit-flag-alias",
  447. "Used when multiple integer values declared within an enum.IntFlag "
  448. "class share a common bit position.",
  449. ),
  450. "E0211": (
  451. "Method %r has no argument",
  452. "no-method-argument",
  453. "Used when a method which should have the bound instance as "
  454. "first argument has no argument defined.",
  455. ),
  456. "E0213": (
  457. 'Method %r should have "self" as first argument',
  458. "no-self-argument",
  459. 'Used when a method has an attribute different the "self" as '
  460. "first argument. This is considered as an error since this is "
  461. "a so common convention that you shouldn't break it!",
  462. ),
  463. "C0202": (
  464. "Class method %s should have %s as first argument",
  465. "bad-classmethod-argument",
  466. "Used when a class method has a first argument named differently "
  467. "than the value specified in valid-classmethod-first-arg option "
  468. '(default to "cls"), recommended to easily differentiate them '
  469. "from regular instance methods.",
  470. ),
  471. "C0203": (
  472. "Metaclass method %s should have %s as first argument",
  473. "bad-mcs-method-argument",
  474. "Used when a metaclass method has a first argument named "
  475. "differently than the value specified in valid-classmethod-first"
  476. '-arg option (default to "cls"), recommended to easily '
  477. "differentiate them from regular instance methods.",
  478. ),
  479. "C0204": (
  480. "Metaclass class method %s should have %s as first argument",
  481. "bad-mcs-classmethod-argument",
  482. "Used when a metaclass class method has a first argument named "
  483. "differently than the value specified in valid-metaclass-"
  484. 'classmethod-first-arg option (default to "mcs"), recommended to '
  485. "easily differentiate them from regular instance methods.",
  486. ),
  487. "W0211": (
  488. "Static method with %r as first argument",
  489. "bad-staticmethod-argument",
  490. 'Used when a static method has "self" or a value specified in '
  491. "valid-classmethod-first-arg option or "
  492. "valid-metaclass-classmethod-first-arg option as first argument.",
  493. ),
  494. "W0221": (
  495. "%s %s %r method",
  496. "arguments-differ",
  497. "Used when a method has a different number of arguments than in "
  498. "the implemented interface or in an overridden method. Extra arguments "
  499. "with default values are ignored.",
  500. ),
  501. "W0222": (
  502. "Signature differs from %s %r method",
  503. "signature-differs",
  504. "Used when a method signature is different than in the "
  505. "implemented interface or in an overridden method.",
  506. ),
  507. "W0223": (
  508. "Method %r is abstract in class %r but is not overridden in child class %r",
  509. "abstract-method",
  510. "Used when an abstract method (i.e. raise NotImplementedError) is "
  511. "not overridden in concrete class.",
  512. ),
  513. "W0231": (
  514. "__init__ method from base class %r is not called",
  515. "super-init-not-called",
  516. "Used when an ancestor class method has an __init__ method "
  517. "which is not called by a derived class.",
  518. ),
  519. "W0233": (
  520. "__init__ method from a non direct base class %r is called",
  521. "non-parent-init-called",
  522. "Used when an __init__ method is called on a class which is not "
  523. "in the direct ancestors for the analysed class.",
  524. ),
  525. "W0246": (
  526. "Useless parent or super() delegation in method %r",
  527. "useless-parent-delegation",
  528. "Used whenever we can detect that an overridden method is useless, "
  529. "relying on parent or super() delegation to do the same thing as another method "
  530. "from the MRO.",
  531. {"old_names": [("W0235", "useless-super-delegation")]},
  532. ),
  533. "W0236": (
  534. "Method %r was expected to be %r, found it instead as %r",
  535. "invalid-overridden-method",
  536. "Used when we detect that a method was overridden in a way "
  537. "that does not match its base class "
  538. "which could result in potential bugs at runtime.",
  539. ),
  540. "W0237": (
  541. "%s %s %r method",
  542. "arguments-renamed",
  543. "Used when a method parameter has a different name than in "
  544. "the implemented interface or in an overridden method.",
  545. ),
  546. "W0238": (
  547. "Unused private member `%s.%s`",
  548. "unused-private-member",
  549. "Emitted when a private member of a class is defined but not used.",
  550. ),
  551. "W0239": (
  552. "Method %r overrides a method decorated with typing.final which is defined in class %r",
  553. "overridden-final-method",
  554. "Used when a method decorated with typing.final has been overridden.",
  555. ),
  556. "W0240": (
  557. "Class %r is a subclass of a class decorated with typing.final: %r",
  558. "subclassed-final-class",
  559. "Used when a class decorated with typing.final has been subclassed.",
  560. ),
  561. "W0244": (
  562. "Redefined slots %r in subclass",
  563. "redefined-slots-in-subclass",
  564. "Used when a slot is re-defined in a subclass.",
  565. ),
  566. "W0245": (
  567. "Super call without brackets",
  568. "super-without-brackets",
  569. "Used when a call to super does not have brackets and thus is not an actual "
  570. "call and does not work as expected.",
  571. ),
  572. "E0236": (
  573. "Invalid object %r in __slots__, must contain only non empty strings",
  574. "invalid-slots-object",
  575. "Used when an invalid (non-string) object occurs in __slots__.",
  576. ),
  577. "E0237": (
  578. "Assigning to attribute %r not defined in class slots",
  579. "assigning-non-slot",
  580. "Used when assigning to an attribute not defined in the class slots.",
  581. ),
  582. "E0238": (
  583. "Invalid __slots__ object",
  584. "invalid-slots",
  585. "Used when an invalid __slots__ is found in class. "
  586. "Only a string, an iterable or a sequence is permitted.",
  587. ),
  588. "E0239": (
  589. "Inheriting %r, which is not a class.",
  590. "inherit-non-class",
  591. "Used when a class inherits from something which is not a class.",
  592. ),
  593. "E0240": (
  594. "Inconsistent method resolution order for class %r",
  595. "inconsistent-mro",
  596. "Used when a class has an inconsistent method resolution order.",
  597. ),
  598. "E0241": (
  599. "Duplicate bases for class %r",
  600. "duplicate-bases",
  601. "Duplicate use of base classes in derived classes raise TypeErrors.",
  602. ),
  603. "E0242": (
  604. "Value %r in slots conflicts with class variable",
  605. "class-variable-slots-conflict",
  606. "Used when a value in __slots__ conflicts with a class variable, property or method.",
  607. ),
  608. "E0243": (
  609. "Invalid assignment to '__class__'. Should be a class definition but got a '%s'",
  610. "invalid-class-object",
  611. "Used when an invalid object is assigned to a __class__ property. "
  612. "Only a class is permitted.",
  613. ),
  614. "E0244": (
  615. 'Extending inherited Enum class "%s"',
  616. "invalid-enum-extension",
  617. "Used when a class tries to extend an inherited Enum class. "
  618. "Doing so will raise a TypeError at runtime.",
  619. ),
  620. "R0202": (
  621. "Consider using a decorator instead of calling classmethod",
  622. "no-classmethod-decorator",
  623. "Used when a class method is defined without using the decorator syntax.",
  624. ),
  625. "R0203": (
  626. "Consider using a decorator instead of calling staticmethod",
  627. "no-staticmethod-decorator",
  628. "Used when a static method is defined without using the decorator syntax.",
  629. ),
  630. "C0205": (
  631. "Class __slots__ should be a non-string iterable",
  632. "single-string-used-for-slots",
  633. "Used when a class __slots__ is a simple string, rather than an iterable.",
  634. ),
  635. "R0205": (
  636. "Class %r inherits from object, can be safely removed from bases in python3",
  637. "useless-object-inheritance",
  638. "Used when a class inherit from object, which under python3 is implicit, "
  639. "hence can be safely removed from bases.",
  640. ),
  641. "R0206": (
  642. "Cannot have defined parameters for properties",
  643. "property-with-parameters",
  644. "Used when we detect that a property also has parameters, which are useless, "
  645. "given that properties cannot be called with additional arguments.",
  646. ),
  647. }
  648. def _scope_default() -> defaultdict[str, list[_AccessNodes]]:
  649. # It's impossible to nest defaultdicts so we must use a function
  650. return defaultdict(list)
  651. class ScopeAccessMap:
  652. """Store the accessed variables per scope."""
  653. def __init__(self) -> None:
  654. self._scopes: defaultdict[
  655. nodes.ClassDef, defaultdict[str, list[_AccessNodes]]
  656. ] = defaultdict(_scope_default)
  657. def set_accessed(self, node: _AccessNodes) -> None:
  658. """Set the given node as accessed."""
  659. frame = node_frame_class(node)
  660. if frame is None:
  661. # The node does not live in a class.
  662. return
  663. self._scopes[frame][node.attrname].append(node)
  664. def accessed(self, scope: nodes.ClassDef) -> dict[str, list[_AccessNodes]]:
  665. """Get the accessed variables for the given scope."""
  666. return self._scopes.get(scope, {})
  667. class ClassChecker(BaseChecker):
  668. """Checker for class nodes.
  669. Checks for :
  670. * methods without self as first argument
  671. * overridden methods signature
  672. * access only to existent members via self
  673. * attributes not defined in the __init__ method
  674. * unreachable code
  675. """
  676. # configuration section name
  677. name = "classes"
  678. # messages
  679. msgs = MSGS
  680. # configuration options
  681. options = (
  682. (
  683. "defining-attr-methods",
  684. {
  685. "default": (
  686. "__init__",
  687. "__new__",
  688. "setUp",
  689. "asyncSetUp",
  690. "__post_init__",
  691. ),
  692. "type": "csv",
  693. "metavar": "<method names>",
  694. "help": "List of method names used to declare (i.e. assign) \
  695. instance attributes.",
  696. },
  697. ),
  698. (
  699. "valid-classmethod-first-arg",
  700. {
  701. "default": ("cls",),
  702. "type": "csv",
  703. "metavar": "<argument names>",
  704. "help": "List of valid names for the first argument in \
  705. a class method.",
  706. },
  707. ),
  708. (
  709. "valid-metaclass-classmethod-first-arg",
  710. {
  711. "default": ("mcs",),
  712. "type": "csv",
  713. "metavar": "<argument names>",
  714. "help": "List of valid names for the first argument in \
  715. a metaclass class method.",
  716. },
  717. ),
  718. (
  719. "exclude-protected",
  720. {
  721. "default": (
  722. # namedtuple public API.
  723. "_asdict",
  724. "_fields",
  725. "_replace",
  726. "_source",
  727. "_make",
  728. "os._exit",
  729. ),
  730. "type": "csv",
  731. "metavar": "<protected access exclusions>",
  732. "help": (
  733. "List of member names, which should be excluded "
  734. "from the protected access warning."
  735. ),
  736. },
  737. ),
  738. (
  739. "check-protected-access-in-special-methods",
  740. {
  741. "default": False,
  742. "type": "yn",
  743. "metavar": "<y or n>",
  744. "help": "Warn about protected attribute access inside special methods",
  745. },
  746. ),
  747. )
  748. def __init__(self, linter: PyLinter) -> None:
  749. super().__init__(linter)
  750. self._accessed = ScopeAccessMap()
  751. self._first_attrs: list[str | None] = []
  752. def open(self) -> None:
  753. self._mixin_class_rgx = self.linter.config.mixin_class_rgx
  754. py_version = self.linter.config.py_version
  755. self._py38_plus = py_version >= (3, 8)
  756. @cached_property
  757. def _dummy_rgx(self) -> Pattern[str]:
  758. return self.linter.config.dummy_variables_rgx # type: ignore[no-any-return]
  759. @only_required_for_messages(
  760. "abstract-method",
  761. "invalid-slots",
  762. "single-string-used-for-slots",
  763. "invalid-slots-object",
  764. "class-variable-slots-conflict",
  765. "inherit-non-class",
  766. "useless-object-inheritance",
  767. "inconsistent-mro",
  768. "duplicate-bases",
  769. "redefined-slots-in-subclass",
  770. "invalid-enum-extension",
  771. "subclassed-final-class",
  772. "implicit-flag-alias",
  773. )
  774. def visit_classdef(self, node: nodes.ClassDef) -> None:
  775. """Init visit variable _accessed."""
  776. self._check_bases_classes(node)
  777. self._check_slots(node)
  778. self._check_proper_bases(node)
  779. self._check_typing_final(node)
  780. self._check_consistent_mro(node)
  781. def _check_consistent_mro(self, node: nodes.ClassDef) -> None:
  782. """Detect that a class has a consistent mro or duplicate bases."""
  783. try:
  784. node.mro()
  785. except astroid.InconsistentMroError:
  786. self.add_message("inconsistent-mro", args=node.name, node=node)
  787. except astroid.DuplicateBasesError:
  788. self.add_message("duplicate-bases", args=node.name, node=node)
  789. def _check_enum_base(self, node: nodes.ClassDef, ancestor: nodes.ClassDef) -> None:
  790. members = ancestor.getattr("__members__")
  791. if members and isinstance(members[0], nodes.Dict) and members[0].items:
  792. for _, name_node in members[0].items:
  793. # Exempt type annotations without value assignments
  794. if all(
  795. isinstance(item.parent, nodes.AnnAssign)
  796. and item.parent.value is None
  797. for item in ancestor.getattr(name_node.name)
  798. ):
  799. continue
  800. self.add_message(
  801. "invalid-enum-extension",
  802. args=ancestor.name,
  803. node=node,
  804. confidence=INFERENCE,
  805. )
  806. break
  807. if ancestor.is_subtype_of("enum.IntFlag"):
  808. # Collect integer flag assignments present on the class
  809. assignments = defaultdict(list)
  810. for assign_name in node.nodes_of_class(nodes.AssignName):
  811. if isinstance(assign_name.parent, nodes.Assign):
  812. value = getattr(assign_name.parent.value, "value", None)
  813. if isinstance(value, int):
  814. assignments[value].append(assign_name)
  815. # For each bit position, collect all the flags that set the bit
  816. bit_flags = defaultdict(set)
  817. for flag in assignments:
  818. flag_bits = (i for i, c in enumerate(reversed(bin(flag))) if c == "1")
  819. for bit in flag_bits:
  820. bit_flags[bit].add(flag)
  821. # Collect the minimum, unique values that each flag overlaps with
  822. overlaps = defaultdict(list)
  823. for flags in bit_flags.values():
  824. source, *conflicts = sorted(flags)
  825. for conflict in conflicts:
  826. overlaps[conflict].append(source)
  827. # Report the overlapping values
  828. for overlap in overlaps:
  829. for assignment_node in assignments[overlap]:
  830. self.add_message(
  831. "implicit-flag-alias",
  832. node=assignment_node,
  833. args={
  834. "overlap": f"<{node.name}.{assignment_node.name}: {overlap}>",
  835. "sources": ", ".join(
  836. f"<{node.name}.{assignments[source][0].name}: {source}> "
  837. f"({overlap} & {source} = {overlap & source})"
  838. for source in overlaps[overlap]
  839. ),
  840. },
  841. confidence=INFERENCE,
  842. )
  843. def _check_proper_bases(self, node: nodes.ClassDef) -> None:
  844. """Detect that a class inherits something which is not
  845. a class or a type.
  846. """
  847. for base in node.bases:
  848. ancestor = safe_infer(base)
  849. if not ancestor:
  850. continue
  851. if isinstance(ancestor, astroid.Instance) and ancestor.is_subtype_of(
  852. "builtins.type"
  853. ):
  854. continue
  855. if not isinstance(ancestor, nodes.ClassDef) or _is_invalid_base_class(
  856. ancestor
  857. ):
  858. self.add_message("inherit-non-class", args=base.as_string(), node=node)
  859. if isinstance(ancestor, nodes.ClassDef) and ancestor.is_subtype_of(
  860. "enum.Enum"
  861. ):
  862. self._check_enum_base(node, ancestor)
  863. if ancestor.name == object.__name__:
  864. self.add_message(
  865. "useless-object-inheritance", args=node.name, node=node
  866. )
  867. def _check_typing_final(self, node: nodes.ClassDef) -> None:
  868. """Detect that a class does not subclass a class decorated with
  869. `typing.final`.
  870. """
  871. if not self._py38_plus:
  872. return
  873. for base in node.bases:
  874. ancestor = safe_infer(base)
  875. if not ancestor:
  876. continue
  877. if isinstance(ancestor, nodes.ClassDef) and (
  878. decorated_with(ancestor, ["typing.final"])
  879. or uninferable_final_decorators(ancestor.decorators)
  880. ):
  881. self.add_message(
  882. "subclassed-final-class",
  883. args=(node.name, ancestor.name),
  884. node=node,
  885. )
  886. @only_required_for_messages(
  887. "unused-private-member",
  888. "attribute-defined-outside-init",
  889. "access-member-before-definition",
  890. )
  891. def leave_classdef(self, node: nodes.ClassDef) -> None:
  892. """Checker for Class nodes.
  893. check that instance attributes are defined in __init__ and check
  894. access to existent members
  895. """
  896. self._check_unused_private_functions(node)
  897. self._check_unused_private_variables(node)
  898. self._check_unused_private_attributes(node)
  899. self._check_attribute_defined_outside_init(node)
  900. def _check_unused_private_functions(self, node: nodes.ClassDef) -> None:
  901. for function_def in node.nodes_of_class(nodes.FunctionDef):
  902. if not is_attr_private(function_def.name):
  903. continue
  904. parent_scope = function_def.parent.scope()
  905. if isinstance(parent_scope, nodes.FunctionDef):
  906. # Handle nested functions
  907. if function_def.name in (
  908. n.name for n in parent_scope.nodes_of_class(nodes.Name)
  909. ):
  910. continue
  911. for child in node.nodes_of_class((nodes.Name, nodes.Attribute)):
  912. # Check for cases where the functions are used as a variable instead of as a
  913. # method call
  914. if isinstance(child, nodes.Name) and child.name == function_def.name:
  915. break
  916. if isinstance(child, nodes.Attribute):
  917. # Ignore recursive calls
  918. if (
  919. child.attrname != function_def.name
  920. or child.scope() == function_def
  921. ):
  922. continue
  923. # Check self.__attrname, cls.__attrname, node_name.__attrname
  924. if isinstance(child.expr, nodes.Name) and child.expr.name in {
  925. "self",
  926. "cls",
  927. node.name,
  928. }:
  929. break
  930. # Check type(self).__attrname
  931. if isinstance(child.expr, nodes.Call):
  932. inferred = safe_infer(child.expr)
  933. if (
  934. isinstance(inferred, nodes.ClassDef)
  935. and inferred.name == node.name
  936. ):
  937. break
  938. else:
  939. name_stack = []
  940. curr = parent_scope
  941. # Generate proper names for nested functions
  942. while curr != node:
  943. name_stack.append(curr.name)
  944. curr = curr.parent.scope()
  945. outer_level_names = f"{'.'.join(reversed(name_stack))}"
  946. function_repr = f"{outer_level_names}.{function_def.name}({function_def.args.as_string()})"
  947. self.add_message(
  948. "unused-private-member",
  949. node=function_def,
  950. args=(node.name, function_repr.lstrip(".")),
  951. )
  952. def _check_unused_private_variables(self, node: nodes.ClassDef) -> None:
  953. """Check if private variables are never used within a class."""
  954. for assign_name in node.nodes_of_class(nodes.AssignName):
  955. if isinstance(assign_name.parent, nodes.Arguments):
  956. continue # Ignore function arguments
  957. if not is_attr_private(assign_name.name):
  958. continue
  959. for child in node.nodes_of_class((nodes.Name, nodes.Attribute)):
  960. if isinstance(child, nodes.Name) and child.name == assign_name.name:
  961. break
  962. if isinstance(child, nodes.Attribute):
  963. if not isinstance(child.expr, nodes.Name):
  964. break
  965. if child.attrname == assign_name.name and child.expr.name in (
  966. "self",
  967. "cls",
  968. node.name,
  969. ):
  970. break
  971. else:
  972. args = (node.name, assign_name.name)
  973. self.add_message("unused-private-member", node=assign_name, args=args)
  974. def _check_unused_private_attributes(self, node: nodes.ClassDef) -> None:
  975. for assign_attr in node.nodes_of_class(nodes.AssignAttr):
  976. if not is_attr_private(assign_attr.attrname) or not isinstance(
  977. assign_attr.expr, nodes.Name
  978. ):
  979. continue
  980. # Logic for checking false positive when using __new__,
  981. # Get the returned object names of the __new__ magic function
  982. # Then check if the attribute was consumed in other instance methods
  983. acceptable_obj_names: list[str] = ["self"]
  984. scope = assign_attr.scope()
  985. if isinstance(scope, nodes.FunctionDef) and scope.name == "__new__":
  986. acceptable_obj_names.extend(
  987. [
  988. return_node.value.name
  989. for return_node in scope.nodes_of_class(nodes.Return)
  990. if isinstance(return_node.value, nodes.Name)
  991. ]
  992. )
  993. for attribute in node.nodes_of_class(nodes.Attribute):
  994. if attribute.attrname != assign_attr.attrname:
  995. continue
  996. if not isinstance(attribute.expr, nodes.Name):
  997. continue
  998. if assign_attr.expr.name in {
  999. "cls",
  1000. node.name,
  1001. } and attribute.expr.name in {"cls", "self", node.name}:
  1002. # If assigned to cls or class name, can be accessed by cls/self/class name
  1003. break
  1004. if (
  1005. assign_attr.expr.name in acceptable_obj_names
  1006. and attribute.expr.name == "self"
  1007. ):
  1008. # If assigned to self.attrib, can only be accessed by self
  1009. # Or if __new__ was used, the returned object names are acceptable
  1010. break
  1011. if assign_attr.expr.name == attribute.expr.name == node.name:
  1012. # Recognise attributes which are accessed via the class name
  1013. break
  1014. else:
  1015. args = (node.name, assign_attr.attrname)
  1016. self.add_message("unused-private-member", node=assign_attr, args=args)
  1017. def _check_attribute_defined_outside_init(self, cnode: nodes.ClassDef) -> None:
  1018. # check access to existent members on non metaclass classes
  1019. if (
  1020. "attribute-defined-outside-init"
  1021. in self.linter.config.ignored_checks_for_mixins
  1022. and self._mixin_class_rgx.match(cnode.name)
  1023. ):
  1024. # We are in a mixin class. No need to try to figure out if
  1025. # something is missing, since it is most likely that it will
  1026. # miss.
  1027. return
  1028. accessed = self._accessed.accessed(cnode)
  1029. if cnode.type != "metaclass":
  1030. self._check_accessed_members(cnode, accessed)
  1031. # checks attributes are defined in an allowed method such as __init__
  1032. if not self.linter.is_message_enabled("attribute-defined-outside-init"):
  1033. return
  1034. defining_methods = self.linter.config.defining_attr_methods
  1035. current_module = cnode.root()
  1036. for attr, nodes_lst in cnode.instance_attrs.items():
  1037. # Exclude `__dict__` as it is already defined.
  1038. if attr == "__dict__":
  1039. continue
  1040. # Skip nodes which are not in the current module and it may screw up
  1041. # the output, while it's not worth it
  1042. nodes_lst = [
  1043. n
  1044. for n in nodes_lst
  1045. if not isinstance(
  1046. n.statement(future=True), (nodes.Delete, nodes.AugAssign)
  1047. )
  1048. and n.root() is current_module
  1049. ]
  1050. if not nodes_lst:
  1051. continue # error detected by typechecking
  1052. # Check if any method attr is defined in is a defining method
  1053. # or if we have the attribute defined in a setter.
  1054. frames = (node.frame(future=True) for node in nodes_lst)
  1055. if any(
  1056. frame.name in defining_methods or is_property_setter(frame)
  1057. for frame in frames
  1058. ):
  1059. continue
  1060. # check attribute is defined in a parent's __init__
  1061. for parent in cnode.instance_attr_ancestors(attr):
  1062. attr_defined = False
  1063. # check if any parent method attr is defined in is a defining method
  1064. for node in parent.instance_attrs[attr]:
  1065. if node.frame(future=True).name in defining_methods:
  1066. attr_defined = True
  1067. if attr_defined:
  1068. # we're done :)
  1069. break
  1070. else:
  1071. # check attribute is defined as a class attribute
  1072. try:
  1073. cnode.local_attr(attr)
  1074. except astroid.NotFoundError:
  1075. for node in nodes_lst:
  1076. if node.frame(future=True).name not in defining_methods:
  1077. # If the attribute was set by a call in any
  1078. # of the defining methods, then don't emit
  1079. # the warning.
  1080. if _called_in_methods(
  1081. node.frame(future=True), cnode, defining_methods
  1082. ):
  1083. continue
  1084. self.add_message(
  1085. "attribute-defined-outside-init", args=attr, node=node
  1086. )
  1087. # pylint: disable = too-many-branches
  1088. def visit_functiondef(self, node: nodes.FunctionDef) -> None:
  1089. """Check method arguments, overriding."""
  1090. # ignore actual functions
  1091. if not node.is_method():
  1092. return
  1093. self._check_useless_super_delegation(node)
  1094. self._check_property_with_parameters(node)
  1095. # 'is_method()' is called and makes sure that this is a 'nodes.ClassDef'
  1096. klass: nodes.ClassDef = node.parent.frame(future=True)
  1097. # check first argument is self if this is actually a method
  1098. self._check_first_arg_for_type(node, klass.type == "metaclass")
  1099. if node.name == "__init__":
  1100. self._check_init(node, klass)
  1101. return
  1102. # check signature if the method overloads inherited method
  1103. for overridden in klass.local_attr_ancestors(node.name):
  1104. # get astroid for the searched method
  1105. try:
  1106. parent_function = overridden[node.name]
  1107. except KeyError:
  1108. # we have found the method but it's not in the local
  1109. # dictionary.
  1110. # This may happen with astroid build from living objects
  1111. continue
  1112. if not isinstance(parent_function, nodes.FunctionDef):
  1113. continue
  1114. self._check_signature(node, parent_function, klass)
  1115. self._check_invalid_overridden_method(node, parent_function)
  1116. break
  1117. if node.decorators:
  1118. for decorator in node.decorators.nodes:
  1119. if isinstance(decorator, nodes.Attribute) and decorator.attrname in {
  1120. "getter",
  1121. "setter",
  1122. "deleter",
  1123. }:
  1124. # attribute affectation will call this method, not hiding it
  1125. return
  1126. if isinstance(decorator, nodes.Name):
  1127. if decorator.name == "property":
  1128. # attribute affectation will either call a setter or raise
  1129. # an attribute error, anyway not hiding the function
  1130. return
  1131. # Infer the decorator and see if it returns something useful
  1132. inferred = safe_infer(decorator)
  1133. if not inferred:
  1134. return
  1135. if isinstance(inferred, nodes.FunctionDef):
  1136. # Okay, it's a decorator, let's see what it can infer.
  1137. try:
  1138. inferred = next(inferred.infer_call_result(inferred))
  1139. except astroid.InferenceError:
  1140. return
  1141. try:
  1142. if (
  1143. isinstance(inferred, (astroid.Instance, nodes.ClassDef))
  1144. and inferred.getattr("__get__")
  1145. and inferred.getattr("__set__")
  1146. ):
  1147. return
  1148. except astroid.AttributeInferenceError:
  1149. pass
  1150. # check if the method is hidden by an attribute
  1151. # pylint: disable = too-many-try-statements
  1152. try:
  1153. overridden = klass.instance_attr(node.name)[0]
  1154. overridden_frame = overridden.frame(future=True)
  1155. if (
  1156. isinstance(overridden_frame, nodes.FunctionDef)
  1157. and overridden_frame.type == "method"
  1158. ):
  1159. overridden_frame = overridden_frame.parent.frame(future=True)
  1160. if not (
  1161. isinstance(overridden_frame, nodes.ClassDef)
  1162. and klass.is_subtype_of(overridden_frame.qname())
  1163. ):
  1164. return
  1165. # If a subclass defined the method then it's not our fault.
  1166. for ancestor in klass.ancestors():
  1167. if node.name in ancestor.instance_attrs and is_attr_private(node.name):
  1168. return
  1169. for obj in ancestor.lookup(node.name)[1]:
  1170. if isinstance(obj, nodes.FunctionDef):
  1171. return
  1172. args = (overridden.root().name, overridden.fromlineno)
  1173. self.add_message("method-hidden", args=args, node=node)
  1174. except astroid.NotFoundError:
  1175. pass
  1176. visit_asyncfunctiondef = visit_functiondef
  1177. def _check_useless_super_delegation(self, function: nodes.FunctionDef) -> None:
  1178. """Check if the given function node is an useless method override.
  1179. We consider it *useless* if it uses the super() builtin, but having
  1180. nothing additional whatsoever than not implementing the method at all.
  1181. If the method uses super() to delegate an operation to the rest of the MRO,
  1182. and if the method called is the same as the current one, the arguments
  1183. passed to super() are the same as the parameters that were passed to
  1184. this method, then the method could be removed altogether, by letting
  1185. other implementation to take precedence.
  1186. """
  1187. if not _is_trivial_super_delegation(function):
  1188. return
  1189. call: nodes.Call = function.body[0].value
  1190. # Classes that override __eq__ should also override
  1191. # __hash__, even a trivial override is meaningful
  1192. if function.name == "__hash__":
  1193. for other_method in function.parent.mymethods():
  1194. if other_method.name == "__eq__":
  1195. return
  1196. # Check values of default args
  1197. klass = function.parent.frame(future=True)
  1198. meth_node = None
  1199. for overridden in klass.local_attr_ancestors(function.name):
  1200. # get astroid for the searched method
  1201. try:
  1202. meth_node = overridden[function.name]
  1203. except KeyError:
  1204. # we have found the method but it's not in the local
  1205. # dictionary.
  1206. # This may happen with astroid build from living objects
  1207. continue
  1208. if (
  1209. not isinstance(meth_node, nodes.FunctionDef)
  1210. # If the method have an ancestor which is not a
  1211. # function then it is legitimate to redefine it
  1212. or _has_different_parameters_default_value(
  1213. meth_node.args, function.args
  1214. )
  1215. # arguments to builtins such as Exception.__init__() cannot be inspected
  1216. or (meth_node.args.args is None and function.argnames() != ["self"])
  1217. ):
  1218. return
  1219. break
  1220. # Detect if the parameters are the same as the call's arguments.
  1221. params = _signature_from_arguments(function.args)
  1222. args = _signature_from_call(call)
  1223. if meth_node is not None:
  1224. # Detect if the super method uses varargs and the function doesn't or makes some of those explicit
  1225. if meth_node.args.vararg and (
  1226. not function.args.vararg
  1227. or len(function.args.args) > len(meth_node.args.args)
  1228. ):
  1229. return
  1230. def form_annotations(arguments: nodes.Arguments) -> list[str]:
  1231. annotations = chain(
  1232. (arguments.posonlyargs_annotations or []), arguments.annotations
  1233. )
  1234. return [ann.as_string() for ann in annotations if ann is not None]
  1235. called_annotations = form_annotations(function.args)
  1236. overridden_annotations = form_annotations(meth_node.args)
  1237. if called_annotations and overridden_annotations:
  1238. if called_annotations != overridden_annotations:
  1239. return
  1240. if (
  1241. function.returns is not None
  1242. and meth_node.returns is not None
  1243. and meth_node.returns.as_string() != function.returns.as_string()
  1244. ):
  1245. # Override adds typing information to the return type
  1246. return
  1247. if _definition_equivalent_to_call(params, args):
  1248. self.add_message(
  1249. "useless-parent-delegation",
  1250. node=function,
  1251. args=(function.name,),
  1252. confidence=INFERENCE,
  1253. )
  1254. def _check_property_with_parameters(self, node: nodes.FunctionDef) -> None:
  1255. if (
  1256. node.args.args
  1257. and len(node.args.args) > 1
  1258. and decorated_with_property(node)
  1259. and not is_property_setter(node)
  1260. ):
  1261. self.add_message("property-with-parameters", node=node)
  1262. def _check_invalid_overridden_method(
  1263. self,
  1264. function_node: nodes.FunctionDef,
  1265. parent_function_node: nodes.FunctionDef,
  1266. ) -> None:
  1267. parent_is_property = decorated_with_property(
  1268. parent_function_node
  1269. ) or is_property_setter_or_deleter(parent_function_node)
  1270. current_is_property = decorated_with_property(
  1271. function_node
  1272. ) or is_property_setter_or_deleter(function_node)
  1273. if parent_is_property and not current_is_property:
  1274. self.add_message(
  1275. "invalid-overridden-method",
  1276. args=(function_node.name, "property", function_node.type),
  1277. node=function_node,
  1278. )
  1279. elif not parent_is_property and current_is_property:
  1280. self.add_message(
  1281. "invalid-overridden-method",
  1282. args=(function_node.name, "method", "property"),
  1283. node=function_node,
  1284. )
  1285. parent_is_async = isinstance(parent_function_node, nodes.AsyncFunctionDef)
  1286. current_is_async = isinstance(function_node, nodes.AsyncFunctionDef)
  1287. if parent_is_async and not current_is_async:
  1288. self.add_message(
  1289. "invalid-overridden-method",
  1290. args=(function_node.name, "async", "non-async"),
  1291. node=function_node,
  1292. )
  1293. elif not parent_is_async and current_is_async:
  1294. self.add_message(
  1295. "invalid-overridden-method",
  1296. args=(function_node.name, "non-async", "async"),
  1297. node=function_node,
  1298. )
  1299. if (
  1300. decorated_with(parent_function_node, ["typing.final"])
  1301. or uninferable_final_decorators(parent_function_node.decorators)
  1302. ) and self._py38_plus:
  1303. self.add_message(
  1304. "overridden-final-method",
  1305. args=(function_node.name, parent_function_node.parent.frame().name),
  1306. node=function_node,
  1307. )
  1308. def _check_slots(self, node: nodes.ClassDef) -> None:
  1309. if "__slots__" not in node.locals:
  1310. return
  1311. for slots in node.ilookup("__slots__"):
  1312. # check if __slots__ is a valid type
  1313. if isinstance(slots, util.UninferableBase):
  1314. continue
  1315. if not is_iterable(slots) and not is_comprehension(slots):
  1316. self.add_message("invalid-slots", node=node)
  1317. continue
  1318. if isinstance(slots, nodes.Const):
  1319. # a string, ignore the following checks
  1320. self.add_message("single-string-used-for-slots", node=node)
  1321. continue
  1322. if not hasattr(slots, "itered"):
  1323. # we can't obtain the values, maybe a .deque?
  1324. continue
  1325. if isinstance(slots, nodes.Dict):
  1326. values = [item[0] for item in slots.items]
  1327. else:
  1328. values = slots.itered()
  1329. if isinstance(values, util.UninferableBase):
  1330. continue
  1331. for elt in values:
  1332. try:
  1333. self._check_slots_elt(elt, node)
  1334. except astroid.InferenceError:
  1335. continue
  1336. self._check_redefined_slots(node, slots, values)
  1337. def _check_redefined_slots(
  1338. self,
  1339. node: nodes.ClassDef,
  1340. slots_node: nodes.NodeNG,
  1341. slots_list: list[nodes.NodeNG],
  1342. ) -> None:
  1343. """Check if `node` redefines a slot which is defined in an ancestor class."""
  1344. slots_names: list[str] = []
  1345. for slot in slots_list:
  1346. if isinstance(slot, nodes.Const):
  1347. slots_names.append(slot.value)
  1348. else:
  1349. inferred_slot = safe_infer(slot)
  1350. inferred_slot_value = getattr(inferred_slot, "value", None)
  1351. if isinstance(inferred_slot_value, str):
  1352. slots_names.append(inferred_slot_value)
  1353. # Slots of all parent classes
  1354. ancestors_slots_names = {
  1355. slot.value
  1356. for ancestor in node.local_attr_ancestors("__slots__")
  1357. for slot in ancestor.slots() or []
  1358. }
  1359. # Slots which are common to `node` and its parent classes
  1360. redefined_slots = ancestors_slots_names.intersection(slots_names)
  1361. if redefined_slots:
  1362. self.add_message(
  1363. "redefined-slots-in-subclass",
  1364. args=([name for name in slots_names if name in redefined_slots],),
  1365. node=slots_node,
  1366. )
  1367. def _check_slots_elt(
  1368. self, elt: SuccessfulInferenceResult, node: nodes.ClassDef
  1369. ) -> None:
  1370. for inferred in elt.infer():
  1371. if isinstance(inferred, util.UninferableBase):
  1372. continue
  1373. if not isinstance(inferred, nodes.Const) or not isinstance(
  1374. inferred.value, str
  1375. ):
  1376. self.add_message(
  1377. "invalid-slots-object",
  1378. args=elt.as_string(),
  1379. node=elt,
  1380. confidence=INFERENCE,
  1381. )
  1382. continue
  1383. if not inferred.value:
  1384. self.add_message(
  1385. "invalid-slots-object",
  1386. args=elt.as_string(),
  1387. node=elt,
  1388. confidence=INFERENCE,
  1389. )
  1390. # Check if we have a conflict with a class variable.
  1391. class_variable = node.locals.get(inferred.value)
  1392. if class_variable:
  1393. # Skip annotated assignments which don't conflict at all with slots.
  1394. if len(class_variable) == 1:
  1395. parent = class_variable[0].parent
  1396. if isinstance(parent, nodes.AnnAssign) and parent.value is None:
  1397. return
  1398. self.add_message(
  1399. "class-variable-slots-conflict", args=(inferred.value,), node=elt
  1400. )
  1401. def leave_functiondef(self, node: nodes.FunctionDef) -> None:
  1402. """On method node, check if this method couldn't be a function.
  1403. ignore class, static and abstract methods, initializer,
  1404. methods overridden from a parent class.
  1405. """
  1406. if node.is_method():
  1407. if node.args.args is not None:
  1408. self._first_attrs.pop()
  1409. leave_asyncfunctiondef = leave_functiondef
  1410. def visit_attribute(self, node: nodes.Attribute) -> None:
  1411. """Check if the getattr is an access to a class member
  1412. if so, register it.
  1413. Also check for access to protected
  1414. class member from outside its class (but ignore __special__
  1415. methods)
  1416. """
  1417. self._check_super_without_brackets(node)
  1418. # Check self
  1419. if self._uses_mandatory_method_param(node):
  1420. self._accessed.set_accessed(node)
  1421. return
  1422. if not self.linter.is_message_enabled("protected-access"):
  1423. return
  1424. self._check_protected_attribute_access(node)
  1425. def _check_super_without_brackets(self, node: nodes.Attribute) -> None:
  1426. """Check if there is a function call on a super call without brackets."""
  1427. # Check if attribute call is in frame definition in class definition
  1428. frame = node.frame()
  1429. if not isinstance(frame, nodes.FunctionDef):
  1430. return
  1431. if not isinstance(frame.parent.frame(), nodes.ClassDef):
  1432. return
  1433. if not isinstance(node.parent, nodes.Call):
  1434. return
  1435. if not isinstance(node.expr, nodes.Name):
  1436. return
  1437. if node.expr.name == "super":
  1438. self.add_message("super-without-brackets", node=node.expr, confidence=HIGH)
  1439. @only_required_for_messages(
  1440. "assigning-non-slot", "invalid-class-object", "access-member-before-definition"
  1441. )
  1442. def visit_assignattr(self, node: nodes.AssignAttr) -> None:
  1443. if isinstance(
  1444. node.assign_type(), nodes.AugAssign
  1445. ) and self._uses_mandatory_method_param(node):
  1446. self._accessed.set_accessed(node)
  1447. self._check_in_slots(node)
  1448. self._check_invalid_class_object(node)
  1449. def _check_invalid_class_object(self, node: nodes.AssignAttr) -> None:
  1450. if not node.attrname == "__class__":
  1451. return
  1452. if isinstance(node.parent, nodes.Tuple):
  1453. class_index = -1
  1454. for i, elt in enumerate(node.parent.elts):
  1455. if hasattr(elt, "attrname") and elt.attrname == "__class__":
  1456. class_index = i
  1457. if class_index == -1:
  1458. # This should not happen because we checked that the node name
  1459. # is '__class__' earlier, but let's not be too confident here
  1460. return # pragma: no cover
  1461. inferred = safe_infer(node.parent.parent.value.elts[class_index])
  1462. else:
  1463. inferred = safe_infer(node.parent.value)
  1464. if (
  1465. isinstance(inferred, (nodes.ClassDef, util.UninferableBase))
  1466. or inferred is None
  1467. ):
  1468. # If is uninferable, we allow it to prevent false positives
  1469. return
  1470. self.add_message(
  1471. "invalid-class-object",
  1472. node=node,
  1473. args=inferred.__class__.__name__,
  1474. confidence=INFERENCE,
  1475. )
  1476. def _check_in_slots(self, node: nodes.AssignAttr) -> None:
  1477. """Check that the given AssignAttr node
  1478. is defined in the class slots.
  1479. """
  1480. inferred = safe_infer(node.expr)
  1481. if not isinstance(inferred, astroid.Instance):
  1482. return
  1483. klass = inferred._proxied
  1484. if not has_known_bases(klass):
  1485. return
  1486. if "__slots__" not in klass.locals or not klass.newstyle:
  1487. return
  1488. # If `__setattr__` is defined on the class, then we can't reason about
  1489. # what will happen when assigning to an attribute.
  1490. if any(
  1491. base.locals.get("__setattr__")
  1492. for base in klass.mro()
  1493. if base.qname() != "builtins.object"
  1494. ):
  1495. return
  1496. # If 'typing.Generic' is a base of bases of klass, the cached version
  1497. # of 'slots()' might have been evaluated incorrectly, thus deleted cache entry.
  1498. if any(base.qname() == "typing.Generic" for base in klass.mro()):
  1499. cache = getattr(klass, "__cache", None)
  1500. if cache and cache.get(klass.slots) is not None:
  1501. del cache[klass.slots]
  1502. slots = klass.slots()
  1503. if slots is None:
  1504. return
  1505. # If any ancestor doesn't use slots, the slots
  1506. # defined for this class are superfluous.
  1507. if any(
  1508. "__slots__" not in ancestor.locals and ancestor.name != "object"
  1509. for ancestor in klass.ancestors()
  1510. ):
  1511. return
  1512. if not any(slot.value == node.attrname for slot in slots):
  1513. # If we have a '__dict__' in slots, then
  1514. # assigning any name is valid.
  1515. if not any(slot.value == "__dict__" for slot in slots):
  1516. if _is_attribute_property(node.attrname, klass):
  1517. # Properties circumvent the slots mechanism,
  1518. # so we should not emit a warning for them.
  1519. return
  1520. if node.attrname != "__class__" and utils.is_class_attr(
  1521. node.attrname, klass
  1522. ):
  1523. return
  1524. if node.attrname in klass.locals:
  1525. for local_name in klass.locals.get(node.attrname):
  1526. statement = local_name.statement(future=True)
  1527. if (
  1528. isinstance(statement, nodes.AnnAssign)
  1529. and not statement.value
  1530. ):
  1531. return
  1532. if _has_data_descriptor(klass, node.attrname):
  1533. # Descriptors circumvent the slots mechanism as well.
  1534. return
  1535. if node.attrname == "__class__" and _has_same_layout_slots(
  1536. slots, node.parent.value
  1537. ):
  1538. return
  1539. self.add_message(
  1540. "assigning-non-slot",
  1541. args=(node.attrname,),
  1542. node=node,
  1543. confidence=INFERENCE,
  1544. )
  1545. @only_required_for_messages(
  1546. "protected-access", "no-classmethod-decorator", "no-staticmethod-decorator"
  1547. )
  1548. def visit_assign(self, assign_node: nodes.Assign) -> None:
  1549. self._check_classmethod_declaration(assign_node)
  1550. node = assign_node.targets[0]
  1551. if not isinstance(node, nodes.AssignAttr):
  1552. return
  1553. if self._uses_mandatory_method_param(node):
  1554. return
  1555. self._check_protected_attribute_access(node)
  1556. def _check_classmethod_declaration(self, node: nodes.Assign) -> None:
  1557. """Checks for uses of classmethod() or staticmethod().
  1558. When a @classmethod or @staticmethod decorator should be used instead.
  1559. A message will be emitted only if the assignment is at a class scope
  1560. and only if the classmethod's argument belongs to the class where it
  1561. is defined.
  1562. `node` is an assign node.
  1563. """
  1564. if not isinstance(node.value, nodes.Call):
  1565. return
  1566. # check the function called is "classmethod" or "staticmethod"
  1567. func = node.value.func
  1568. if not isinstance(func, nodes.Name) or func.name not in (
  1569. "classmethod",
  1570. "staticmethod",
  1571. ):
  1572. return
  1573. msg = (
  1574. "no-classmethod-decorator"
  1575. if func.name == "classmethod"
  1576. else "no-staticmethod-decorator"
  1577. )
  1578. # assignment must be at a class scope
  1579. parent_class = node.scope()
  1580. if not isinstance(parent_class, nodes.ClassDef):
  1581. return
  1582. # Check if the arg passed to classmethod is a class member
  1583. classmeth_arg = node.value.args[0]
  1584. if not isinstance(classmeth_arg, nodes.Name):
  1585. return
  1586. method_name = classmeth_arg.name
  1587. if any(method_name == member.name for member in parent_class.mymethods()):
  1588. self.add_message(msg, node=node.targets[0])
  1589. def _check_protected_attribute_access(
  1590. self, node: nodes.Attribute | nodes.AssignAttr
  1591. ) -> None:
  1592. """Given an attribute access node (set or get), check if attribute
  1593. access is legitimate.
  1594. Call _check_first_attr with node before calling
  1595. this method. Valid cases are:
  1596. * self._attr in a method or cls._attr in a classmethod. Checked by
  1597. _check_first_attr.
  1598. * Klass._attr inside "Klass" class.
  1599. * Klass2._attr inside "Klass" class when Klass2 is a base class of
  1600. Klass.
  1601. """
  1602. attrname = node.attrname
  1603. if (
  1604. not is_attr_protected(attrname)
  1605. or attrname in self.linter.config.exclude_protected
  1606. ):
  1607. return
  1608. # Typing annotations in function definitions can include protected members
  1609. if utils.is_node_in_type_annotation_context(node):
  1610. return
  1611. # Return if `attrname` is defined at the module-level or as a class attribute
  1612. # and is listed in `exclude-protected`.
  1613. inferred = safe_infer(node.expr)
  1614. if (
  1615. inferred
  1616. and isinstance(inferred, (nodes.ClassDef, nodes.Module))
  1617. and f"{inferred.name}.{attrname}" in self.linter.config.exclude_protected
  1618. ):
  1619. return
  1620. klass = node_frame_class(node)
  1621. if klass is None:
  1622. # We are not in a class, no remaining valid case
  1623. self.add_message("protected-access", node=node, args=attrname)
  1624. return
  1625. # In classes, check we are not getting a parent method
  1626. # through the class object or through super
  1627. # If the expression begins with a call to super, that's ok.
  1628. if (
  1629. isinstance(node.expr, nodes.Call)
  1630. and isinstance(node.expr.func, nodes.Name)
  1631. and node.expr.func.name == "super"
  1632. ):
  1633. return
  1634. # If the expression begins with a call to type(self), that's ok.
  1635. if self._is_type_self_call(node.expr):
  1636. return
  1637. # Check if we are inside the scope of a class or nested inner class
  1638. inside_klass = True
  1639. outer_klass = klass
  1640. callee = node.expr.as_string()
  1641. parents_callee = callee.split(".")
  1642. parents_callee.reverse()
  1643. for callee in parents_callee:
  1644. if not outer_klass or callee != outer_klass.name:
  1645. inside_klass = False
  1646. break
  1647. # Move up one level within the nested classes
  1648. outer_klass = get_outer_class(outer_klass)
  1649. # We are in a class, one remaining valid cases, Klass._attr inside
  1650. # Klass
  1651. if not (inside_klass or callee in klass.basenames):
  1652. # Detect property assignments in the body of the class.
  1653. # This is acceptable:
  1654. #
  1655. # class A:
  1656. # b = property(lambda: self._b)
  1657. stmt = node.parent.statement(future=True)
  1658. if (
  1659. isinstance(stmt, nodes.Assign)
  1660. and len(stmt.targets) == 1
  1661. and isinstance(stmt.targets[0], nodes.AssignName)
  1662. ):
  1663. name = stmt.targets[0].name
  1664. if _is_attribute_property(name, klass):
  1665. return
  1666. if (
  1667. self._is_classmethod(node.frame(future=True))
  1668. and self._is_inferred_instance(node.expr, klass)
  1669. and self._is_class_or_instance_attribute(attrname, klass)
  1670. ):
  1671. return
  1672. licit_protected_member = not attrname.startswith("__")
  1673. if (
  1674. not self.linter.config.check_protected_access_in_special_methods
  1675. and licit_protected_member
  1676. and self._is_called_inside_special_method(node)
  1677. ):
  1678. return
  1679. self.add_message("protected-access", node=node, args=attrname)
  1680. @staticmethod
  1681. def _is_called_inside_special_method(node: nodes.NodeNG) -> bool:
  1682. """Returns true if the node is located inside a special (aka dunder) method."""
  1683. frame_name = node.frame(future=True).name
  1684. return frame_name and frame_name in PYMETHODS
  1685. def _is_type_self_call(self, expr: nodes.NodeNG) -> bool:
  1686. return (
  1687. isinstance(expr, nodes.Call)
  1688. and isinstance(expr.func, nodes.Name)
  1689. and expr.func.name == "type"
  1690. and len(expr.args) == 1
  1691. and self._is_mandatory_method_param(expr.args[0])
  1692. )
  1693. @staticmethod
  1694. def _is_classmethod(func: LocalsDictNodeNG) -> bool:
  1695. """Check if the given *func* node is a class method."""
  1696. return isinstance(func, nodes.FunctionDef) and (
  1697. func.type == "classmethod" or func.name == "__class_getitem__"
  1698. )
  1699. @staticmethod
  1700. def _is_inferred_instance(expr: nodes.NodeNG, klass: nodes.ClassDef) -> bool:
  1701. """Check if the inferred value of the given *expr* is an instance of
  1702. *klass*.
  1703. """
  1704. inferred = safe_infer(expr)
  1705. if not isinstance(inferred, astroid.Instance):
  1706. return False
  1707. return inferred._proxied is klass
  1708. @staticmethod
  1709. def _is_class_or_instance_attribute(name: str, klass: nodes.ClassDef) -> bool:
  1710. """Check if the given attribute *name* is a class or instance member of the
  1711. given *klass*.
  1712. Returns ``True`` if the name is a property in the given klass,
  1713. ``False`` otherwise.
  1714. """
  1715. if utils.is_class_attr(name, klass):
  1716. return True
  1717. try:
  1718. klass.instance_attr(name)
  1719. return True
  1720. except astroid.NotFoundError:
  1721. return False
  1722. def _check_accessed_members(
  1723. self, node: nodes.ClassDef, accessed: dict[str, list[_AccessNodes]]
  1724. ) -> None:
  1725. """Check that accessed members are defined."""
  1726. excs = ("AttributeError", "Exception", "BaseException")
  1727. for attr, nodes_lst in accessed.items():
  1728. try:
  1729. # is it a class attribute ?
  1730. node.local_attr(attr)
  1731. # yes, stop here
  1732. continue
  1733. except astroid.NotFoundError:
  1734. pass
  1735. # is it an instance attribute of a parent class ?
  1736. try:
  1737. next(node.instance_attr_ancestors(attr))
  1738. # yes, stop here
  1739. continue
  1740. except StopIteration:
  1741. pass
  1742. # is it an instance attribute ?
  1743. try:
  1744. defstmts = node.instance_attr(attr)
  1745. except astroid.NotFoundError:
  1746. pass
  1747. else:
  1748. # filter out augment assignment nodes
  1749. defstmts = [stmt for stmt in defstmts if stmt not in nodes_lst]
  1750. if not defstmts:
  1751. # only augment assignment for this node, no-member should be
  1752. # triggered by the typecheck checker
  1753. continue
  1754. # filter defstmts to only pick the first one when there are
  1755. # several assignments in the same scope
  1756. scope = defstmts[0].scope()
  1757. defstmts = [
  1758. stmt
  1759. for i, stmt in enumerate(defstmts)
  1760. if i == 0 or stmt.scope() is not scope
  1761. ]
  1762. # if there are still more than one, don't attempt to be smarter
  1763. # than we can be
  1764. if len(defstmts) == 1:
  1765. defstmt = defstmts[0]
  1766. # check that if the node is accessed in the same method as
  1767. # it's defined, it's accessed after the initial assignment
  1768. frame = defstmt.frame(future=True)
  1769. lno = defstmt.fromlineno
  1770. for _node in nodes_lst:
  1771. if (
  1772. _node.frame(future=True) is frame
  1773. and _node.fromlineno < lno
  1774. and not astroid.are_exclusive(
  1775. _node.statement(future=True), defstmt, excs
  1776. )
  1777. ):
  1778. self.add_message(
  1779. "access-member-before-definition",
  1780. node=_node,
  1781. args=(attr, lno),
  1782. )
  1783. def _check_first_arg_for_type(
  1784. self, node: nodes.FunctionDef, metaclass: bool
  1785. ) -> None:
  1786. """Check the name of first argument, expect:.
  1787. * 'self' for a regular method
  1788. * 'cls' for a class method or a metaclass regular method (actually
  1789. valid-classmethod-first-arg value)
  1790. * 'mcs' for a metaclass class method (actually
  1791. valid-metaclass-classmethod-first-arg)
  1792. * not one of the above for a static method
  1793. """
  1794. # don't care about functions with unknown argument (builtins)
  1795. if node.args.args is None:
  1796. return
  1797. if node.args.posonlyargs:
  1798. first_arg = node.args.posonlyargs[0].name
  1799. elif node.args.args:
  1800. first_arg = node.argnames()[0]
  1801. else:
  1802. first_arg = None
  1803. self._first_attrs.append(first_arg)
  1804. first = self._first_attrs[-1]
  1805. # static method
  1806. if node.type == "staticmethod":
  1807. if (
  1808. first_arg == "self"
  1809. or first_arg in self.linter.config.valid_classmethod_first_arg
  1810. or first_arg in self.linter.config.valid_metaclass_classmethod_first_arg
  1811. ):
  1812. self.add_message("bad-staticmethod-argument", args=first, node=node)
  1813. return
  1814. self._first_attrs[-1] = None
  1815. elif "builtins.staticmethod" in node.decoratornames():
  1816. # Check if there is a decorator which is not named `staticmethod`
  1817. # but is assigned to one.
  1818. return
  1819. # class / regular method with no args
  1820. elif not (
  1821. node.args.args
  1822. or node.args.posonlyargs
  1823. or node.args.vararg
  1824. or node.args.kwarg
  1825. ):
  1826. self.add_message("no-method-argument", node=node, args=node.name)
  1827. # metaclass
  1828. elif metaclass:
  1829. # metaclass __new__ or classmethod
  1830. if node.type == "classmethod":
  1831. self._check_first_arg_config(
  1832. first,
  1833. self.linter.config.valid_metaclass_classmethod_first_arg,
  1834. node,
  1835. "bad-mcs-classmethod-argument",
  1836. node.name,
  1837. )
  1838. # metaclass regular method
  1839. else:
  1840. self._check_first_arg_config(
  1841. first,
  1842. self.linter.config.valid_classmethod_first_arg,
  1843. node,
  1844. "bad-mcs-method-argument",
  1845. node.name,
  1846. )
  1847. # regular class with class method
  1848. elif node.type == "classmethod" or node.name == "__class_getitem__":
  1849. self._check_first_arg_config(
  1850. first,
  1851. self.linter.config.valid_classmethod_first_arg,
  1852. node,
  1853. "bad-classmethod-argument",
  1854. node.name,
  1855. )
  1856. # regular class with regular method without self as argument
  1857. elif first != "self":
  1858. self.add_message("no-self-argument", node=node, args=node.name)
  1859. def _check_first_arg_config(
  1860. self,
  1861. first: str | None,
  1862. config: Sequence[str],
  1863. node: nodes.FunctionDef,
  1864. message: str,
  1865. method_name: str,
  1866. ) -> None:
  1867. if first not in config:
  1868. if len(config) == 1:
  1869. valid = repr(config[0])
  1870. else:
  1871. valid = ", ".join(repr(v) for v in config[:-1])
  1872. valid = f"{valid} or {config[-1]!r}"
  1873. self.add_message(message, args=(method_name, valid), node=node)
  1874. def _check_bases_classes(self, node: nodes.ClassDef) -> None:
  1875. """Check that the given class node implements abstract methods from
  1876. base classes.
  1877. """
  1878. def is_abstract(method: nodes.FunctionDef) -> bool:
  1879. return method.is_abstract(pass_is_abstract=False) # type: ignore[no-any-return]
  1880. # check if this class abstract
  1881. if class_is_abstract(node):
  1882. return
  1883. methods = sorted(
  1884. unimplemented_abstract_methods(node, is_abstract).items(),
  1885. key=lambda item: item[0],
  1886. )
  1887. for name, method in methods:
  1888. owner = method.parent.frame(future=True)
  1889. if owner is node:
  1890. continue
  1891. # owner is not this class, it must be a parent class
  1892. # check that the ancestor's method is not abstract
  1893. if name in node.locals:
  1894. # it is redefined as an attribute or with a descriptor
  1895. continue
  1896. self.add_message(
  1897. "abstract-method",
  1898. node=node,
  1899. args=(name, owner.name, node.name),
  1900. confidence=INFERENCE,
  1901. )
  1902. def _check_init(self, node: nodes.FunctionDef, klass_node: nodes.ClassDef) -> None:
  1903. """Check that the __init__ method call super or ancestors'__init__
  1904. method (unless it is used for type hinting with `typing.overload`).
  1905. """
  1906. if not self.linter.is_message_enabled(
  1907. "super-init-not-called"
  1908. ) and not self.linter.is_message_enabled("non-parent-init-called"):
  1909. return
  1910. to_call = _ancestors_to_call(klass_node)
  1911. not_called_yet = dict(to_call)
  1912. parents_with_called_inits: set[bases.UnboundMethod] = set()
  1913. for stmt in node.nodes_of_class(nodes.Call):
  1914. expr = stmt.func
  1915. if not isinstance(expr, nodes.Attribute) or expr.attrname != "__init__":
  1916. continue
  1917. # skip the test if using super
  1918. if (
  1919. isinstance(expr.expr, nodes.Call)
  1920. and isinstance(expr.expr.func, nodes.Name)
  1921. and expr.expr.func.name == "super"
  1922. ):
  1923. return
  1924. # pylint: disable = too-many-try-statements
  1925. try:
  1926. for klass in expr.expr.infer():
  1927. if isinstance(klass, util.UninferableBase):
  1928. continue
  1929. # The inferred klass can be super(), which was
  1930. # assigned to a variable and the `__init__`
  1931. # was called later.
  1932. #
  1933. # base = super()
  1934. # base.__init__(...)
  1935. if (
  1936. isinstance(klass, astroid.Instance)
  1937. and isinstance(klass._proxied, nodes.ClassDef)
  1938. and is_builtin_object(klass._proxied)
  1939. and klass._proxied.name == "super"
  1940. ):
  1941. return
  1942. if isinstance(klass, astroid.objects.Super):
  1943. return
  1944. try:
  1945. method = not_called_yet.pop(klass)
  1946. # Record that the class' init has been called
  1947. parents_with_called_inits.add(node_frame_class(method))
  1948. except KeyError:
  1949. if klass not in klass_node.ancestors(recurs=False):
  1950. self.add_message(
  1951. "non-parent-init-called", node=expr, args=klass.name
  1952. )
  1953. except astroid.InferenceError:
  1954. continue
  1955. for klass, method in not_called_yet.items():
  1956. # Check if the init of the class that defines this init has already
  1957. # been called.
  1958. if node_frame_class(method) in parents_with_called_inits:
  1959. return
  1960. if utils.is_protocol_class(klass):
  1961. return
  1962. if decorated_with(node, ["typing.overload"]):
  1963. continue
  1964. self.add_message(
  1965. "super-init-not-called",
  1966. args=klass.name,
  1967. node=node,
  1968. confidence=INFERENCE,
  1969. )
  1970. def _check_signature(
  1971. self,
  1972. method1: nodes.FunctionDef,
  1973. refmethod: nodes.FunctionDef,
  1974. cls: nodes.ClassDef,
  1975. ) -> None:
  1976. """Check that the signature of the two given methods match."""
  1977. if not (
  1978. isinstance(method1, nodes.FunctionDef)
  1979. and isinstance(refmethod, nodes.FunctionDef)
  1980. ):
  1981. self.add_message(
  1982. "method-check-failed", args=(method1, refmethod), node=method1
  1983. )
  1984. return
  1985. instance = cls.instantiate_class()
  1986. method1 = astroid.scoped_nodes.function_to_method(method1, instance)
  1987. refmethod = astroid.scoped_nodes.function_to_method(refmethod, instance)
  1988. # Don't care about functions with unknown argument (builtins).
  1989. if method1.args.args is None or refmethod.args.args is None:
  1990. return
  1991. # Ignore private to class methods.
  1992. if is_attr_private(method1.name):
  1993. return
  1994. # Ignore setters, they have an implicit extra argument,
  1995. # which shouldn't be taken in consideration.
  1996. if is_property_setter(method1):
  1997. return
  1998. arg_differ_output = _different_parameters(
  1999. refmethod, method1, dummy_parameter_regex=self._dummy_rgx
  2000. )
  2001. class_type = "overriding"
  2002. if len(arg_differ_output) > 0:
  2003. for msg in arg_differ_output:
  2004. if "Number" in msg:
  2005. total_args_method1 = len(method1.args.args)
  2006. if method1.args.vararg:
  2007. total_args_method1 += 1
  2008. if method1.args.kwarg:
  2009. total_args_method1 += 1
  2010. if method1.args.kwonlyargs:
  2011. total_args_method1 += len(method1.args.kwonlyargs)
  2012. total_args_refmethod = len(refmethod.args.args)
  2013. if refmethod.args.vararg:
  2014. total_args_refmethod += 1
  2015. if refmethod.args.kwarg:
  2016. total_args_refmethod += 1
  2017. if refmethod.args.kwonlyargs:
  2018. total_args_refmethod += len(refmethod.args.kwonlyargs)
  2019. error_type = "arguments-differ"
  2020. msg_args = (
  2021. msg
  2022. + f"was {total_args_refmethod} in '{refmethod.parent.frame().name}.{refmethod.name}' and "
  2023. f"is now {total_args_method1} in",
  2024. class_type,
  2025. f"{method1.parent.frame().name}.{method1.name}",
  2026. )
  2027. elif "renamed" in msg:
  2028. error_type = "arguments-renamed"
  2029. msg_args = (
  2030. msg,
  2031. class_type,
  2032. f"{method1.parent.frame().name}.{method1.name}",
  2033. )
  2034. else:
  2035. error_type = "arguments-differ"
  2036. msg_args = (
  2037. msg,
  2038. class_type,
  2039. f"{method1.parent.frame().name}.{method1.name}",
  2040. )
  2041. self.add_message(error_type, args=msg_args, node=method1)
  2042. elif (
  2043. len(method1.args.defaults) < len(refmethod.args.defaults)
  2044. and not method1.args.vararg
  2045. ):
  2046. class_type = "overridden"
  2047. self.add_message(
  2048. "signature-differs", args=(class_type, method1.name), node=method1
  2049. )
  2050. def _uses_mandatory_method_param(
  2051. self, node: nodes.Attribute | nodes.Assign | nodes.AssignAttr
  2052. ) -> bool:
  2053. """Check that attribute lookup name use first attribute variable name.
  2054. Name is `self` for method, `cls` for classmethod and `mcs` for metaclass.
  2055. """
  2056. return self._is_mandatory_method_param(node.expr)
  2057. def _is_mandatory_method_param(self, node: nodes.NodeNG) -> bool:
  2058. """Check if nodes.Name corresponds to first attribute variable name.
  2059. Name is `self` for method, `cls` for classmethod and `mcs` for metaclass.
  2060. Static methods return False.
  2061. """
  2062. if self._first_attrs:
  2063. first_attr = self._first_attrs[-1]
  2064. else:
  2065. # It's possible the function was already unregistered.
  2066. closest_func = utils.get_node_first_ancestor_of_type(
  2067. node, nodes.FunctionDef
  2068. )
  2069. if closest_func is None:
  2070. return False
  2071. if not closest_func.is_bound():
  2072. return False
  2073. if not closest_func.args.args:
  2074. return False
  2075. first_attr = closest_func.args.args[0].name
  2076. return isinstance(node, nodes.Name) and node.name == first_attr
  2077. def _ancestors_to_call(
  2078. klass_node: nodes.ClassDef, method_name: str = "__init__"
  2079. ) -> dict[nodes.ClassDef, bases.UnboundMethod]:
  2080. """Return a dictionary where keys are the list of base classes providing
  2081. the queried method, and so that should/may be called from the method node.
  2082. """
  2083. to_call: dict[nodes.ClassDef, bases.UnboundMethod] = {}
  2084. for base_node in klass_node.ancestors(recurs=False):
  2085. try:
  2086. init_node = next(base_node.igetattr(method_name))
  2087. if not isinstance(init_node, astroid.UnboundMethod):
  2088. continue
  2089. if init_node.is_abstract():
  2090. continue
  2091. to_call[base_node] = init_node
  2092. except astroid.InferenceError:
  2093. continue
  2094. return to_call