brain_builtin_inference.py 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106
  1. # Licensed under the LGPL: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html
  2. # For details: https://github.com/pylint-dev/astroid/blob/main/LICENSE
  3. # Copyright (c) https://github.com/pylint-dev/astroid/blob/main/CONTRIBUTORS.txt
  4. """Astroid hooks for various builtins."""
  5. from __future__ import annotations
  6. import itertools
  7. from collections.abc import Callable, Iterable, Iterator
  8. from functools import partial
  9. from typing import TYPE_CHECKING, Any, NoReturn, cast
  10. from astroid import arguments, helpers, nodes, objects, util
  11. from astroid.builder import AstroidBuilder
  12. from astroid.context import InferenceContext
  13. from astroid.exceptions import (
  14. AstroidTypeError,
  15. AttributeInferenceError,
  16. InferenceError,
  17. MroError,
  18. UseInferenceDefault,
  19. )
  20. from astroid.inference_tip import inference_tip
  21. from astroid.manager import AstroidManager
  22. from astroid.nodes import scoped_nodes
  23. from astroid.typing import (
  24. ConstFactoryResult,
  25. InferenceResult,
  26. SuccessfulInferenceResult,
  27. )
  28. if TYPE_CHECKING:
  29. from astroid.bases import Instance
  30. ContainerObjects = (
  31. objects.FrozenSet | objects.DictItems | objects.DictKeys | objects.DictValues
  32. )
  33. BuiltContainers = type[tuple] | type[list] | type[set] | type[frozenset]
  34. CopyResult = nodes.Dict | nodes.List | nodes.Set | objects.FrozenSet
  35. OBJECT_DUNDER_NEW = "object.__new__"
  36. STR_CLASS = """
  37. class whatever(object):
  38. def join(self, iterable):
  39. return {rvalue}
  40. def replace(self, old, new, count=None):
  41. return {rvalue}
  42. def format(self, *args, **kwargs):
  43. return {rvalue}
  44. def encode(self, encoding='ascii', errors=None):
  45. return b''
  46. def decode(self, encoding='ascii', errors=None):
  47. return u''
  48. def capitalize(self):
  49. return {rvalue}
  50. def title(self):
  51. return {rvalue}
  52. def lower(self):
  53. return {rvalue}
  54. def upper(self):
  55. return {rvalue}
  56. def swapcase(self):
  57. return {rvalue}
  58. def index(self, sub, start=None, end=None):
  59. return 0
  60. def find(self, sub, start=None, end=None):
  61. return 0
  62. def count(self, sub, start=None, end=None):
  63. return 0
  64. def strip(self, chars=None):
  65. return {rvalue}
  66. def lstrip(self, chars=None):
  67. return {rvalue}
  68. def rstrip(self, chars=None):
  69. return {rvalue}
  70. def rjust(self, width, fillchar=None):
  71. return {rvalue}
  72. def center(self, width, fillchar=None):
  73. return {rvalue}
  74. def ljust(self, width, fillchar=None):
  75. return {rvalue}
  76. """
  77. BYTES_CLASS = """
  78. class whatever(object):
  79. def join(self, iterable):
  80. return {rvalue}
  81. def replace(self, old, new, count=None):
  82. return {rvalue}
  83. def decode(self, encoding='ascii', errors=None):
  84. return u''
  85. def capitalize(self):
  86. return {rvalue}
  87. def title(self):
  88. return {rvalue}
  89. def lower(self):
  90. return {rvalue}
  91. def upper(self):
  92. return {rvalue}
  93. def swapcase(self):
  94. return {rvalue}
  95. def index(self, sub, start=None, end=None):
  96. return 0
  97. def find(self, sub, start=None, end=None):
  98. return 0
  99. def count(self, sub, start=None, end=None):
  100. return 0
  101. def strip(self, chars=None):
  102. return {rvalue}
  103. def lstrip(self, chars=None):
  104. return {rvalue}
  105. def rstrip(self, chars=None):
  106. return {rvalue}
  107. def rjust(self, width, fillchar=None):
  108. return {rvalue}
  109. def center(self, width, fillchar=None):
  110. return {rvalue}
  111. def ljust(self, width, fillchar=None):
  112. return {rvalue}
  113. """
  114. def _use_default() -> NoReturn: # pragma: no cover
  115. raise UseInferenceDefault()
  116. def _extend_string_class(class_node, code, rvalue):
  117. """Function to extend builtin str/unicode class."""
  118. code = code.format(rvalue=rvalue)
  119. fake = AstroidBuilder(AstroidManager()).string_build(code)["whatever"]
  120. for method in fake.mymethods():
  121. method.parent = class_node
  122. method.lineno = None
  123. method.col_offset = None
  124. if "__class__" in method.locals:
  125. method.locals["__class__"] = [class_node]
  126. class_node.locals[method.name] = [method]
  127. method.parent = class_node
  128. def _extend_builtins(class_transforms):
  129. builtin_ast = AstroidManager().builtins_module
  130. for class_name, transform in class_transforms.items():
  131. transform(builtin_ast[class_name])
  132. def on_bootstrap():
  133. """Called by astroid_bootstrapping()."""
  134. _extend_builtins(
  135. {
  136. "bytes": partial(_extend_string_class, code=BYTES_CLASS, rvalue="b''"),
  137. "str": partial(_extend_string_class, code=STR_CLASS, rvalue="''"),
  138. }
  139. )
  140. def _builtin_filter_predicate(node, builtin_name) -> bool:
  141. # pylint: disable = too-many-boolean-expressions
  142. if (
  143. builtin_name == "type"
  144. and node.root().name == "re"
  145. and isinstance(node.func, nodes.Name)
  146. and node.func.name == "type"
  147. and isinstance(node.parent, nodes.Assign)
  148. and len(node.parent.targets) == 1
  149. and isinstance(node.parent.targets[0], nodes.AssignName)
  150. and node.parent.targets[0].name in {"Pattern", "Match"}
  151. ):
  152. # Handle re.Pattern and re.Match in brain_re
  153. # Match these patterns from stdlib/re.py
  154. # ```py
  155. # Pattern = type(...)
  156. # Match = type(...)
  157. # ```
  158. return False
  159. if isinstance(node.func, nodes.Name):
  160. return node.func.name == builtin_name
  161. if isinstance(node.func, nodes.Attribute):
  162. return (
  163. node.func.attrname == "fromkeys"
  164. and isinstance(node.func.expr, nodes.Name)
  165. and node.func.expr.name == "dict"
  166. )
  167. return False
  168. def register_builtin_transform(
  169. manager: AstroidManager, transform, builtin_name
  170. ) -> None:
  171. """Register a new transform function for the given *builtin_name*.
  172. The transform function must accept two parameters, a node and
  173. an optional context.
  174. """
  175. def _transform_wrapper(
  176. node: nodes.Call, context: InferenceContext | None = None, **kwargs: Any
  177. ) -> Iterator:
  178. result = transform(node, context=context)
  179. if result:
  180. if not result.parent:
  181. # Let the transformation function determine
  182. # the parent for its result. Otherwise,
  183. # we set it to be the node we transformed from.
  184. result.parent = node
  185. if result.lineno is None:
  186. result.lineno = node.lineno
  187. # Can be a 'Module' see https://github.com/pylint-dev/pylint/issues/4671
  188. # We don't have a regression test on this one: tread carefully
  189. if hasattr(result, "col_offset") and result.col_offset is None:
  190. result.col_offset = node.col_offset
  191. return iter([result])
  192. manager.register_transform(
  193. nodes.Call,
  194. inference_tip(_transform_wrapper),
  195. partial(_builtin_filter_predicate, builtin_name=builtin_name),
  196. )
  197. def _container_generic_inference(
  198. node: nodes.Call,
  199. context: InferenceContext | None,
  200. node_type: type[nodes.BaseContainer],
  201. transform: Callable[[SuccessfulInferenceResult], nodes.BaseContainer | None],
  202. ) -> nodes.BaseContainer:
  203. args = node.args
  204. if not args:
  205. return node_type(
  206. lineno=node.lineno,
  207. col_offset=node.col_offset,
  208. parent=node.parent,
  209. end_lineno=node.end_lineno,
  210. end_col_offset=node.end_col_offset,
  211. )
  212. if len(node.args) > 1:
  213. raise UseInferenceDefault()
  214. (arg,) = args
  215. transformed = transform(arg)
  216. if not transformed:
  217. try:
  218. inferred = next(arg.infer(context=context))
  219. except (InferenceError, StopIteration) as exc:
  220. raise UseInferenceDefault from exc
  221. if isinstance(inferred, util.UninferableBase):
  222. raise UseInferenceDefault
  223. transformed = transform(inferred)
  224. if not transformed or isinstance(transformed, util.UninferableBase):
  225. raise UseInferenceDefault
  226. return transformed
  227. def _container_generic_transform(
  228. arg: SuccessfulInferenceResult,
  229. context: InferenceContext | None,
  230. klass: type[nodes.BaseContainer],
  231. iterables: tuple[type[nodes.BaseContainer] | type[ContainerObjects], ...],
  232. build_elts: BuiltContainers,
  233. ) -> nodes.BaseContainer | None:
  234. elts: Iterable | str | bytes
  235. if isinstance(arg, klass):
  236. return arg
  237. if isinstance(arg, iterables):
  238. arg = cast((nodes.BaseContainer | ContainerObjects), arg)
  239. if all(isinstance(elt, nodes.Const) for elt in arg.elts):
  240. elts = [cast(nodes.Const, elt).value for elt in arg.elts]
  241. else:
  242. # TODO: Does not handle deduplication for sets.
  243. elts = []
  244. for element in arg.elts:
  245. if not element:
  246. continue
  247. inferred = util.safe_infer(element, context=context)
  248. if inferred:
  249. evaluated_object = nodes.EvaluatedObject(
  250. original=element, value=inferred
  251. )
  252. elts.append(evaluated_object)
  253. elif isinstance(arg, nodes.Dict):
  254. # Dicts need to have consts as strings already.
  255. elts = [
  256. item[0].value if isinstance(item[0], nodes.Const) else _use_default()
  257. for item in arg.items
  258. ]
  259. elif isinstance(arg, nodes.Const) and isinstance(arg.value, (str, bytes)):
  260. elts = arg.value
  261. else:
  262. return None
  263. return klass.from_elements(elts=build_elts(elts))
  264. def _infer_builtin_container(
  265. node: nodes.Call,
  266. context: InferenceContext | None,
  267. klass: type[nodes.BaseContainer],
  268. iterables: tuple[type[nodes.NodeNG] | type[ContainerObjects], ...],
  269. build_elts: BuiltContainers,
  270. ) -> nodes.BaseContainer:
  271. transform_func = partial(
  272. _container_generic_transform,
  273. context=context,
  274. klass=klass,
  275. iterables=iterables,
  276. build_elts=build_elts,
  277. )
  278. return _container_generic_inference(node, context, klass, transform_func)
  279. # pylint: disable=invalid-name
  280. infer_tuple = partial(
  281. _infer_builtin_container,
  282. klass=nodes.Tuple,
  283. iterables=(
  284. nodes.List,
  285. nodes.Set,
  286. objects.FrozenSet,
  287. objects.DictItems,
  288. objects.DictKeys,
  289. objects.DictValues,
  290. ),
  291. build_elts=tuple,
  292. )
  293. infer_list = partial(
  294. _infer_builtin_container,
  295. klass=nodes.List,
  296. iterables=(
  297. nodes.Tuple,
  298. nodes.Set,
  299. objects.FrozenSet,
  300. objects.DictItems,
  301. objects.DictKeys,
  302. objects.DictValues,
  303. ),
  304. build_elts=list,
  305. )
  306. infer_set = partial(
  307. _infer_builtin_container,
  308. klass=nodes.Set,
  309. iterables=(nodes.List, nodes.Tuple, objects.FrozenSet, objects.DictKeys),
  310. build_elts=set,
  311. )
  312. infer_frozenset = partial(
  313. _infer_builtin_container,
  314. klass=objects.FrozenSet,
  315. iterables=(nodes.List, nodes.Tuple, nodes.Set, objects.FrozenSet, objects.DictKeys),
  316. build_elts=frozenset,
  317. )
  318. def _get_elts(arg, context):
  319. def is_iterable(n) -> bool:
  320. return isinstance(n, (nodes.List, nodes.Tuple, nodes.Set))
  321. try:
  322. inferred = next(arg.infer(context))
  323. except (InferenceError, StopIteration) as exc:
  324. raise UseInferenceDefault from exc
  325. if isinstance(inferred, nodes.Dict):
  326. items = inferred.items
  327. elif is_iterable(inferred):
  328. items = []
  329. for elt in inferred.elts:
  330. # If an item is not a pair of two items,
  331. # then fallback to the default inference.
  332. # Also, take in consideration only hashable items,
  333. # tuples and consts. We are choosing Names as well.
  334. if not is_iterable(elt):
  335. raise UseInferenceDefault()
  336. if len(elt.elts) != 2:
  337. raise UseInferenceDefault()
  338. if not isinstance(elt.elts[0], (nodes.Tuple, nodes.Const, nodes.Name)):
  339. raise UseInferenceDefault()
  340. items.append(tuple(elt.elts))
  341. else:
  342. raise UseInferenceDefault()
  343. return items
  344. def infer_dict(node: nodes.Call, context: InferenceContext | None = None) -> nodes.Dict:
  345. """Try to infer a dict call to a Dict node.
  346. The function treats the following cases:
  347. * dict()
  348. * dict(mapping)
  349. * dict(iterable)
  350. * dict(iterable, **kwargs)
  351. * dict(mapping, **kwargs)
  352. * dict(**kwargs)
  353. If a case can't be inferred, we'll fallback to default inference.
  354. """
  355. call = arguments.CallSite.from_call(node, context=context)
  356. if call.has_invalid_arguments() or call.has_invalid_keywords():
  357. raise UseInferenceDefault
  358. args = call.positional_arguments
  359. kwargs = list(call.keyword_arguments.items())
  360. items: list[tuple[InferenceResult, InferenceResult]]
  361. if not args and not kwargs:
  362. # dict()
  363. return nodes.Dict(
  364. lineno=node.lineno,
  365. col_offset=node.col_offset,
  366. parent=node.parent,
  367. end_lineno=node.end_lineno,
  368. end_col_offset=node.end_col_offset,
  369. )
  370. if kwargs and not args:
  371. # dict(a=1, b=2, c=4)
  372. items = [(nodes.Const(key), value) for key, value in kwargs]
  373. elif len(args) == 1 and kwargs:
  374. # dict(some_iterable, b=2, c=4)
  375. elts = _get_elts(args[0], context)
  376. keys = [(nodes.Const(key), value) for key, value in kwargs]
  377. items = elts + keys
  378. elif len(args) == 1:
  379. items = _get_elts(args[0], context)
  380. else:
  381. raise UseInferenceDefault()
  382. value = nodes.Dict(
  383. col_offset=node.col_offset,
  384. lineno=node.lineno,
  385. parent=node.parent,
  386. end_lineno=node.end_lineno,
  387. end_col_offset=node.end_col_offset,
  388. )
  389. value.postinit(items)
  390. return value
  391. def infer_super(
  392. node: nodes.Call, context: InferenceContext | None = None
  393. ) -> objects.Super:
  394. """Understand super calls.
  395. There are some restrictions for what can be understood:
  396. * unbounded super (one argument form) is not understood.
  397. * if the super call is not inside a function (classmethod or method),
  398. then the default inference will be used.
  399. * if the super arguments can't be inferred, the default inference
  400. will be used.
  401. """
  402. if len(node.args) == 1:
  403. # Ignore unbounded super.
  404. raise UseInferenceDefault
  405. scope = node.scope()
  406. if not isinstance(scope, nodes.FunctionDef):
  407. # Ignore non-method uses of super.
  408. raise UseInferenceDefault
  409. if scope.type not in ("classmethod", "method"):
  410. # Not interested in staticmethods.
  411. raise UseInferenceDefault
  412. cls = scoped_nodes.get_wrapping_class(scope)
  413. assert cls is not None
  414. if not node.args:
  415. mro_pointer = cls
  416. # In we are in a classmethod, the interpreter will fill
  417. # automatically the class as the second argument, not an instance.
  418. if scope.type == "classmethod":
  419. mro_type = cls
  420. else:
  421. mro_type = cls.instantiate_class()
  422. else:
  423. try:
  424. mro_pointer = next(node.args[0].infer(context=context))
  425. except (InferenceError, StopIteration) as exc:
  426. raise UseInferenceDefault from exc
  427. try:
  428. mro_type = next(node.args[1].infer(context=context))
  429. except (InferenceError, StopIteration) as exc:
  430. raise UseInferenceDefault from exc
  431. if isinstance(mro_pointer, util.UninferableBase) or isinstance(
  432. mro_type, util.UninferableBase
  433. ):
  434. # No way we could understand this.
  435. raise UseInferenceDefault
  436. super_obj = objects.Super(
  437. mro_pointer=mro_pointer,
  438. mro_type=mro_type,
  439. self_class=cls,
  440. scope=scope,
  441. call=node,
  442. )
  443. super_obj.parent = node
  444. return super_obj
  445. def _infer_getattr_args(node, context):
  446. if len(node.args) not in (2, 3):
  447. # Not a valid getattr call.
  448. raise UseInferenceDefault
  449. try:
  450. obj = next(node.args[0].infer(context=context))
  451. attr = next(node.args[1].infer(context=context))
  452. except (InferenceError, StopIteration) as exc:
  453. raise UseInferenceDefault from exc
  454. if isinstance(obj, util.UninferableBase) or isinstance(attr, util.UninferableBase):
  455. # If one of the arguments is something we can't infer,
  456. # then also make the result of the getattr call something
  457. # which is unknown.
  458. return util.Uninferable, util.Uninferable
  459. is_string = isinstance(attr, nodes.Const) and isinstance(attr.value, str)
  460. if not is_string:
  461. raise UseInferenceDefault
  462. return obj, attr.value
  463. def infer_getattr(node, context: InferenceContext | None = None):
  464. """Understand getattr calls.
  465. If one of the arguments is an Uninferable object, then the
  466. result will be an Uninferable object. Otherwise, the normal attribute
  467. lookup will be done.
  468. """
  469. obj, attr = _infer_getattr_args(node, context)
  470. if (
  471. isinstance(obj, util.UninferableBase)
  472. or isinstance(attr, util.UninferableBase)
  473. or not hasattr(obj, "igetattr")
  474. ):
  475. return util.Uninferable
  476. try:
  477. return next(obj.igetattr(attr, context=context))
  478. except (StopIteration, InferenceError, AttributeInferenceError):
  479. if len(node.args) == 3:
  480. # Try to infer the default and return it instead.
  481. try:
  482. return next(node.args[2].infer(context=context))
  483. except (StopIteration, InferenceError) as exc:
  484. raise UseInferenceDefault from exc
  485. raise UseInferenceDefault
  486. def infer_hasattr(node, context: InferenceContext | None = None):
  487. """Understand hasattr calls.
  488. This always guarantees three possible outcomes for calling
  489. hasattr: Const(False) when we are sure that the object
  490. doesn't have the intended attribute, Const(True) when
  491. we know that the object has the attribute and Uninferable
  492. when we are unsure of the outcome of the function call.
  493. """
  494. try:
  495. obj, attr = _infer_getattr_args(node, context)
  496. if (
  497. isinstance(obj, util.UninferableBase)
  498. or isinstance(attr, util.UninferableBase)
  499. or not hasattr(obj, "getattr")
  500. ):
  501. return util.Uninferable
  502. obj.getattr(attr, context=context)
  503. except UseInferenceDefault:
  504. # Can't infer something from this function call.
  505. return util.Uninferable
  506. except AttributeInferenceError:
  507. # Doesn't have it.
  508. return nodes.Const(False)
  509. return nodes.Const(True)
  510. def infer_callable(node, context: InferenceContext | None = None):
  511. """Understand callable calls.
  512. This follows Python's semantics, where an object
  513. is callable if it provides an attribute __call__,
  514. even though that attribute is something which can't be
  515. called.
  516. """
  517. if len(node.args) != 1:
  518. # Invalid callable call.
  519. raise UseInferenceDefault
  520. argument = node.args[0]
  521. try:
  522. inferred = next(argument.infer(context=context))
  523. except (InferenceError, StopIteration):
  524. return util.Uninferable
  525. if isinstance(inferred, util.UninferableBase):
  526. return util.Uninferable
  527. return nodes.Const(inferred.callable())
  528. def infer_property(
  529. node: nodes.Call, context: InferenceContext | None = None
  530. ) -> objects.Property:
  531. """Understand `property` class.
  532. This only infers the output of `property`
  533. call, not the arguments themselves.
  534. """
  535. if len(node.args) < 1:
  536. # Invalid property call.
  537. raise UseInferenceDefault
  538. getter = node.args[0]
  539. try:
  540. inferred = next(getter.infer(context=context))
  541. except (InferenceError, StopIteration) as exc:
  542. raise UseInferenceDefault from exc
  543. if not isinstance(inferred, (nodes.FunctionDef, nodes.Lambda)):
  544. raise UseInferenceDefault
  545. prop_func = objects.Property(
  546. function=inferred,
  547. name="<property>",
  548. lineno=node.lineno,
  549. col_offset=node.col_offset,
  550. # ↓ semantically, the definition of the class of property isn't within
  551. # node.frame. It's somewhere in the builtins module, but we are special
  552. # casing it for each "property()" call, so we are making up the
  553. # definition on the spot, ad-hoc.
  554. parent=scoped_nodes.SYNTHETIC_ROOT,
  555. )
  556. prop_func.postinit(
  557. body=[],
  558. args=inferred.args,
  559. doc_node=getattr(inferred, "doc_node", None),
  560. )
  561. return prop_func
  562. def infer_bool(node, context: InferenceContext | None = None):
  563. """Understand bool calls."""
  564. if len(node.args) > 1:
  565. # Invalid bool call.
  566. raise UseInferenceDefault
  567. if not node.args:
  568. return nodes.Const(False)
  569. argument = node.args[0]
  570. try:
  571. inferred = next(argument.infer(context=context))
  572. except (InferenceError, StopIteration):
  573. return util.Uninferable
  574. if isinstance(inferred, util.UninferableBase):
  575. return util.Uninferable
  576. bool_value = inferred.bool_value(context=context)
  577. if isinstance(bool_value, util.UninferableBase):
  578. return util.Uninferable
  579. return nodes.Const(bool_value)
  580. def infer_type(node, context: InferenceContext | None = None):
  581. """Understand the one-argument form of *type*."""
  582. if len(node.args) != 1:
  583. raise UseInferenceDefault
  584. return helpers.object_type(node.args[0], context)
  585. def infer_slice(node, context: InferenceContext | None = None):
  586. """Understand `slice` calls."""
  587. args = node.args
  588. if not 0 < len(args) <= 3:
  589. raise UseInferenceDefault
  590. infer_func = partial(util.safe_infer, context=context)
  591. args = [infer_func(arg) for arg in args]
  592. for arg in args:
  593. if not arg or isinstance(arg, util.UninferableBase):
  594. raise UseInferenceDefault
  595. if not isinstance(arg, nodes.Const):
  596. raise UseInferenceDefault
  597. if not isinstance(arg.value, (type(None), int)):
  598. raise UseInferenceDefault
  599. if len(args) < 3:
  600. # Make sure we have 3 arguments.
  601. args.extend([None] * (3 - len(args)))
  602. slice_node = nodes.Slice(
  603. lineno=node.lineno,
  604. col_offset=node.col_offset,
  605. parent=node.parent,
  606. end_lineno=node.end_lineno,
  607. end_col_offset=node.end_col_offset,
  608. )
  609. slice_node.postinit(*args)
  610. return slice_node
  611. def _infer_object__new__decorator(
  612. node: nodes.ClassDef, context: InferenceContext | None = None, **kwargs: Any
  613. ) -> Iterator[Instance]:
  614. # Instantiate class immediately
  615. # since that's what @object.__new__ does
  616. return iter((node.instantiate_class(),))
  617. def _infer_object__new__decorator_check(node) -> bool:
  618. """Predicate before inference_tip.
  619. Check if the given ClassDef has an @object.__new__ decorator
  620. """
  621. if not node.decorators:
  622. return False
  623. for decorator in node.decorators.nodes:
  624. if isinstance(decorator, nodes.Attribute):
  625. if decorator.as_string() == OBJECT_DUNDER_NEW:
  626. return True
  627. return False
  628. def infer_issubclass(callnode, context: InferenceContext | None = None):
  629. """Infer issubclass() calls.
  630. :param nodes.Call callnode: an `issubclass` call
  631. :param InferenceContext context: the context for the inference
  632. :rtype nodes.Const: Boolean Const value of the `issubclass` call
  633. :raises UseInferenceDefault: If the node cannot be inferred
  634. """
  635. call = arguments.CallSite.from_call(callnode, context=context)
  636. if call.keyword_arguments:
  637. # issubclass doesn't support keyword arguments
  638. raise UseInferenceDefault("TypeError: issubclass() takes no keyword arguments")
  639. if len(call.positional_arguments) != 2:
  640. raise UseInferenceDefault(
  641. f"Expected two arguments, got {len(call.positional_arguments)}"
  642. )
  643. # The left hand argument is the obj to be checked
  644. obj_node, class_or_tuple_node = call.positional_arguments
  645. try:
  646. obj_type = next(obj_node.infer(context=context))
  647. except (InferenceError, StopIteration) as exc:
  648. raise UseInferenceDefault from exc
  649. if not isinstance(obj_type, nodes.ClassDef):
  650. raise UseInferenceDefault(
  651. f"TypeError: arg 1 must be class, not {type(obj_type)!r}"
  652. )
  653. # The right hand argument is the class(es) that the given
  654. # object is to be checked against.
  655. try:
  656. class_container = _class_or_tuple_to_container(
  657. class_or_tuple_node, context=context
  658. )
  659. except InferenceError as exc:
  660. raise UseInferenceDefault from exc
  661. try:
  662. issubclass_bool = helpers.object_issubclass(obj_type, class_container, context)
  663. except AstroidTypeError as exc:
  664. raise UseInferenceDefault("TypeError: " + str(exc)) from exc
  665. except MroError as exc:
  666. raise UseInferenceDefault from exc
  667. return nodes.Const(issubclass_bool)
  668. def infer_isinstance(
  669. callnode: nodes.Call, context: InferenceContext | None = None
  670. ) -> nodes.Const:
  671. """Infer isinstance calls.
  672. :param nodes.Call callnode: an isinstance call
  673. :raises UseInferenceDefault: If the node cannot be inferred
  674. """
  675. call = arguments.CallSite.from_call(callnode, context=context)
  676. if call.keyword_arguments:
  677. # isinstance doesn't support keyword arguments
  678. raise UseInferenceDefault("TypeError: isinstance() takes no keyword arguments")
  679. if len(call.positional_arguments) != 2:
  680. raise UseInferenceDefault(
  681. f"Expected two arguments, got {len(call.positional_arguments)}"
  682. )
  683. # The left hand argument is the obj to be checked
  684. obj_node, class_or_tuple_node = call.positional_arguments
  685. # The right hand argument is the class(es) that the given
  686. # obj is to be check is an instance of
  687. try:
  688. class_container = _class_or_tuple_to_container(
  689. class_or_tuple_node, context=context
  690. )
  691. except InferenceError as exc:
  692. raise UseInferenceDefault from exc
  693. try:
  694. isinstance_bool = helpers.object_isinstance(obj_node, class_container, context)
  695. except AstroidTypeError as exc:
  696. raise UseInferenceDefault("TypeError: " + str(exc)) from exc
  697. except MroError as exc:
  698. raise UseInferenceDefault from exc
  699. if isinstance(isinstance_bool, util.UninferableBase):
  700. raise UseInferenceDefault
  701. return nodes.Const(isinstance_bool)
  702. def _class_or_tuple_to_container(
  703. node: InferenceResult, context: InferenceContext | None = None
  704. ) -> list[InferenceResult]:
  705. # Move inferences results into container
  706. # to simplify later logic
  707. # raises InferenceError if any of the inferences fall through
  708. try:
  709. node_infer = next(node.infer(context=context))
  710. except StopIteration as e:
  711. raise InferenceError(node=node, context=context) from e
  712. # arg2 MUST be a type or a TUPLE of types
  713. # for isinstance
  714. if isinstance(node_infer, nodes.Tuple):
  715. try:
  716. class_container = [
  717. next(node.infer(context=context)) for node in node_infer.elts
  718. ]
  719. except StopIteration as e:
  720. raise InferenceError(node=node, context=context) from e
  721. else:
  722. class_container = [node_infer]
  723. return class_container
  724. def infer_len(node, context: InferenceContext | None = None) -> nodes.Const:
  725. """Infer length calls.
  726. :param nodes.Call node: len call to infer
  727. :param context.InferenceContext: node context
  728. :rtype nodes.Const: a Const node with the inferred length, if possible
  729. """
  730. call = arguments.CallSite.from_call(node, context=context)
  731. if call.keyword_arguments:
  732. raise UseInferenceDefault("TypeError: len() must take no keyword arguments")
  733. if len(call.positional_arguments) != 1:
  734. raise UseInferenceDefault(
  735. "TypeError: len() must take exactly one argument "
  736. "({len}) given".format(len=len(call.positional_arguments))
  737. )
  738. [argument_node] = call.positional_arguments
  739. try:
  740. return nodes.Const(helpers.object_len(argument_node, context=context))
  741. except (AstroidTypeError, InferenceError) as exc:
  742. raise UseInferenceDefault(str(exc)) from exc
  743. def infer_str(node, context: InferenceContext | None = None) -> nodes.Const:
  744. """Infer str() calls.
  745. :param nodes.Call node: str() call to infer
  746. :param context.InferenceContext: node context
  747. :rtype nodes.Const: a Const containing an empty string
  748. """
  749. call = arguments.CallSite.from_call(node, context=context)
  750. if call.keyword_arguments:
  751. raise UseInferenceDefault("TypeError: str() must take no keyword arguments")
  752. try:
  753. return nodes.Const("")
  754. except (AstroidTypeError, InferenceError) as exc:
  755. raise UseInferenceDefault(str(exc)) from exc
  756. def infer_int(node, context: InferenceContext | None = None):
  757. """Infer int() calls.
  758. :param nodes.Call node: int() call to infer
  759. :param context.InferenceContext: node context
  760. :rtype nodes.Const: a Const containing the integer value of the int() call
  761. """
  762. call = arguments.CallSite.from_call(node, context=context)
  763. if call.keyword_arguments:
  764. raise UseInferenceDefault("TypeError: int() must take no keyword arguments")
  765. if call.positional_arguments:
  766. try:
  767. first_value = next(call.positional_arguments[0].infer(context=context))
  768. except (InferenceError, StopIteration) as exc:
  769. raise UseInferenceDefault(str(exc)) from exc
  770. if isinstance(first_value, util.UninferableBase):
  771. raise UseInferenceDefault
  772. if isinstance(first_value, nodes.Const) and isinstance(
  773. first_value.value, (int, str)
  774. ):
  775. try:
  776. actual_value = int(first_value.value)
  777. except ValueError:
  778. return nodes.Const(0)
  779. return nodes.Const(actual_value)
  780. return nodes.Const(0)
  781. def infer_dict_fromkeys(node, context: InferenceContext | None = None):
  782. """Infer dict.fromkeys.
  783. :param nodes.Call node: dict.fromkeys() call to infer
  784. :param context.InferenceContext context: node context
  785. :rtype nodes.Dict:
  786. a Dictionary containing the values that astroid was able to infer.
  787. In case the inference failed for any reason, an empty dictionary
  788. will be inferred instead.
  789. """
  790. def _build_dict_with_elements(elements: list) -> nodes.Dict:
  791. new_node = nodes.Dict(
  792. col_offset=node.col_offset,
  793. lineno=node.lineno,
  794. parent=node.parent,
  795. end_lineno=node.end_lineno,
  796. end_col_offset=node.end_col_offset,
  797. )
  798. new_node.postinit(elements)
  799. return new_node
  800. call = arguments.CallSite.from_call(node, context=context)
  801. if call.keyword_arguments:
  802. raise UseInferenceDefault("TypeError: int() must take no keyword arguments")
  803. if len(call.positional_arguments) not in {1, 2}:
  804. raise UseInferenceDefault(
  805. "TypeError: Needs between 1 and 2 positional arguments"
  806. )
  807. default = nodes.Const(None)
  808. values = call.positional_arguments[0]
  809. try:
  810. inferred_values = next(values.infer(context=context))
  811. except (InferenceError, StopIteration):
  812. return _build_dict_with_elements([])
  813. if inferred_values is util.Uninferable:
  814. return _build_dict_with_elements([])
  815. # Limit to a couple of potential values, as this can become pretty complicated
  816. accepted_iterable_elements = (nodes.Const,)
  817. if isinstance(inferred_values, (nodes.List, nodes.Set, nodes.Tuple)):
  818. elements = inferred_values.elts
  819. for element in elements:
  820. if not isinstance(element, accepted_iterable_elements):
  821. # Fallback to an empty dict
  822. return _build_dict_with_elements([])
  823. elements_with_value = [(element, default) for element in elements]
  824. return _build_dict_with_elements(elements_with_value)
  825. if isinstance(inferred_values, nodes.Const) and isinstance(
  826. inferred_values.value, (str, bytes)
  827. ):
  828. elements_with_value = [
  829. (nodes.Const(element), default) for element in inferred_values.value
  830. ]
  831. return _build_dict_with_elements(elements_with_value)
  832. if isinstance(inferred_values, nodes.Dict):
  833. keys = inferred_values.itered()
  834. for key in keys:
  835. if not isinstance(key, accepted_iterable_elements):
  836. # Fallback to an empty dict
  837. return _build_dict_with_elements([])
  838. elements_with_value = [(element, default) for element in keys]
  839. return _build_dict_with_elements(elements_with_value)
  840. # Fallback to an empty dictionary
  841. return _build_dict_with_elements([])
  842. def _infer_copy_method(
  843. node: nodes.Call, context: InferenceContext | None = None, **kwargs: Any
  844. ) -> Iterator[CopyResult]:
  845. assert isinstance(node.func, nodes.Attribute)
  846. inferred_orig, inferred_copy = itertools.tee(node.func.expr.infer(context=context))
  847. if all(
  848. isinstance(
  849. inferred_node, (nodes.Dict, nodes.List, nodes.Set, objects.FrozenSet)
  850. )
  851. for inferred_node in inferred_orig
  852. ):
  853. return cast(Iterator[CopyResult], inferred_copy)
  854. raise UseInferenceDefault
  855. def _is_str_format_call(node: nodes.Call) -> bool:
  856. """Catch calls to str.format()."""
  857. if not (isinstance(node.func, nodes.Attribute) and node.func.attrname == "format"):
  858. return False
  859. if isinstance(node.func.expr, nodes.Name):
  860. value = util.safe_infer(node.func.expr)
  861. else:
  862. value = node.func.expr
  863. return isinstance(value, nodes.Const) and isinstance(value.value, str)
  864. def _infer_str_format_call(
  865. node: nodes.Call, context: InferenceContext | None = None, **kwargs: Any
  866. ) -> Iterator[ConstFactoryResult | util.UninferableBase]:
  867. """Return a Const node based on the template and passed arguments."""
  868. call = arguments.CallSite.from_call(node, context=context)
  869. assert isinstance(node.func, (nodes.Attribute, nodes.AssignAttr, nodes.DelAttr))
  870. value: nodes.Const
  871. if isinstance(node.func.expr, nodes.Name):
  872. if not (
  873. (inferred := util.safe_infer(node.func.expr))
  874. and isinstance(inferred, nodes.Const)
  875. ):
  876. return iter([util.Uninferable])
  877. value = inferred
  878. elif isinstance(node.func.expr, nodes.Const):
  879. value = node.func.expr
  880. else: # pragma: no cover
  881. return iter([util.Uninferable])
  882. format_template = value.value
  883. # Get the positional arguments passed
  884. inferred_positional: list[nodes.Const] = []
  885. for i in call.positional_arguments:
  886. one_inferred = util.safe_infer(i, context)
  887. if not isinstance(one_inferred, nodes.Const):
  888. return iter([util.Uninferable])
  889. inferred_positional.append(one_inferred)
  890. pos_values: list[str] = [i.value for i in inferred_positional]
  891. # Get the keyword arguments passed
  892. inferred_keyword: dict[str, nodes.Const] = {}
  893. for k, v in call.keyword_arguments.items():
  894. one_inferred = util.safe_infer(v, context)
  895. if not isinstance(one_inferred, nodes.Const):
  896. return iter([util.Uninferable])
  897. inferred_keyword[k] = one_inferred
  898. keyword_values: dict[str, str] = {k: v.value for k, v in inferred_keyword.items()}
  899. try:
  900. formatted_string = format_template.format(*pos_values, **keyword_values)
  901. except (AttributeError, IndexError, KeyError, TypeError, ValueError):
  902. # AttributeError: named field in format string was not found in the arguments
  903. # IndexError: there are too few arguments to interpolate
  904. # TypeError: Unsupported format string
  905. # ValueError: Unknown format code
  906. return iter([util.Uninferable])
  907. return iter([nodes.const_factory(formatted_string)])
  908. def register(manager: AstroidManager) -> None:
  909. # Builtins inference
  910. register_builtin_transform(manager, infer_bool, "bool")
  911. register_builtin_transform(manager, infer_super, "super")
  912. register_builtin_transform(manager, infer_callable, "callable")
  913. register_builtin_transform(manager, infer_property, "property")
  914. register_builtin_transform(manager, infer_getattr, "getattr")
  915. register_builtin_transform(manager, infer_hasattr, "hasattr")
  916. register_builtin_transform(manager, infer_tuple, "tuple")
  917. register_builtin_transform(manager, infer_set, "set")
  918. register_builtin_transform(manager, infer_list, "list")
  919. register_builtin_transform(manager, infer_dict, "dict")
  920. register_builtin_transform(manager, infer_frozenset, "frozenset")
  921. register_builtin_transform(manager, infer_type, "type")
  922. register_builtin_transform(manager, infer_slice, "slice")
  923. register_builtin_transform(manager, infer_isinstance, "isinstance")
  924. register_builtin_transform(manager, infer_issubclass, "issubclass")
  925. register_builtin_transform(manager, infer_len, "len")
  926. register_builtin_transform(manager, infer_str, "str")
  927. register_builtin_transform(manager, infer_int, "int")
  928. register_builtin_transform(manager, infer_dict_fromkeys, "dict.fromkeys")
  929. # Infer object.__new__ calls
  930. manager.register_transform(
  931. nodes.ClassDef,
  932. inference_tip(_infer_object__new__decorator),
  933. _infer_object__new__decorator_check,
  934. )
  935. manager.register_transform(
  936. nodes.Call,
  937. inference_tip(_infer_copy_method),
  938. lambda node: isinstance(node.func, nodes.Attribute)
  939. and node.func.attrname == "copy",
  940. )
  941. manager.register_transform(
  942. nodes.Call,
  943. inference_tip(_infer_str_format_call),
  944. _is_str_format_call,
  945. )