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