classes.py 91 KB

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