def _get_elts(arg, context): def is_iterable(n): return isinstance(n, (nodes.List, nodes.Tuple, nodes.Set)) try: inferred = next(arg.infer(context)) except (InferenceError, StopIteration) as exc: raise UseInferenceDefault from exc if isinstance(inferred, nodes.Dict): items = inferred.items elif is_iterable(inferred): items = [] for elt in inferred.elts: # If an item is not a pair of two items, # then fallback to the default inference. # Also, take in consideration only hashable items, # tuples and consts. We are choosing Names as well. if not is_iterable(elt): raise UseInferenceDefault() if len(elt.elts) != 2: raise UseInferenceDefault() if not isinstance(elt.elts[0], (nodes.Tuple, nodes.Const, nodes.Name)): raise UseInferenceDefault() items.append(tuple(elt.elts)) else: raise UseInferenceDefault() return items
def infer_int(node, context=None): """Infer int() calls :param nodes.Call node: int() call to infer :param context.InferenceContext: node context :rtype nodes.Const: a Const containing the integer value of the int() call """ call = arguments.CallSite.from_call(node, context=context) if call.keyword_arguments: raise UseInferenceDefault( "TypeError: int() must take no keyword arguments") if call.positional_arguments: try: first_value = next( call.positional_arguments[0].infer(context=context)) except (InferenceError, StopIteration) as exc: raise UseInferenceDefault(str(exc)) from exc if first_value is util.Uninferable: raise UseInferenceDefault if isinstance(first_value, nodes.Const) and isinstance( first_value.value, (int, str)): try: actual_value = int(first_value.value) except ValueError: return nodes.Const(0) return nodes.Const(actual_value) return nodes.Const(0)
def infer_named_tuple(node, context=None): """Specific inference function for namedtuple Call node""" tuple_base_name = nodes.Name(name="tuple", parent=node.root()) class_node, name, attributes = infer_func_form( node, tuple_base_name, context=context ) call_site = arguments.CallSite.from_call(node, context=context) node = extract_node("import collections; collections.namedtuple") try: func = next(node.infer()) except StopIteration as e: raise InferenceError(node=node) from e try: rename = next(call_site.infer_argument(func, "rename", context)).bool_value() except (InferenceError, StopIteration): rename = False try: attributes = _check_namedtuple_attributes(name, attributes, rename) except AstroidTypeError as exc: raise UseInferenceDefault("TypeError: " + str(exc)) from exc except AstroidValueError as exc: raise UseInferenceDefault("ValueError: " + str(exc)) from exc replace_args = ", ".join(f"{arg}=None" for arg in attributes) field_def = ( " {name} = property(lambda self: self[{index:d}], " "doc='Alias for field number {index:d}')" ) field_defs = "\n".join( field_def.format(name=name, index=index) for index, name in enumerate(attributes) ) fake = AstroidBuilder(AstroidManager()).string_build( f""" class {name}(tuple): __slots__ = () _fields = {attributes!r} def _asdict(self): return self.__dict__ @classmethod def _make(cls, iterable, new=tuple.__new__, len=len): return new(cls, iterable) def _replace(self, {replace_args}): return self def __getnewargs__(self): return tuple(self) {field_defs} """ ) class_node.locals["_asdict"] = fake.body[0].locals["_asdict"] class_node.locals["_make"] = fake.body[0].locals["_make"] class_node.locals["_replace"] = fake.body[0].locals["_replace"] class_node.locals["_fields"] = fake.body[0].locals["_fields"] for attr in attributes: class_node.locals[attr] = fake.body[0].locals[attr] # we use UseInferenceDefault, we can't be a generator so return an iterator return iter([class_node])
def _functools_partial_inference( node: nodes.Call, context: InferenceContext | None = None ) -> Iterator[objects.PartialFunction]: call = arguments.CallSite.from_call(node, context=context) number_of_positional = len(call.positional_arguments) if number_of_positional < 1: raise UseInferenceDefault( "functools.partial takes at least one argument") if number_of_positional == 1 and not call.keyword_arguments: raise UseInferenceDefault( "functools.partial needs at least to have some filled arguments") partial_function = call.positional_arguments[0] try: inferred_wrapped_function = next( partial_function.infer(context=context)) except (InferenceError, StopIteration) as exc: raise UseInferenceDefault from exc if inferred_wrapped_function is Uninferable: raise UseInferenceDefault("Cannot infer the wrapped function") if not isinstance(inferred_wrapped_function, FunctionDef): raise UseInferenceDefault("The wrapped function is not a function") # Determine if the passed keywords into the callsite are supported # by the wrapped function. if not inferred_wrapped_function.args: function_parameters = [] else: function_parameters = chain( inferred_wrapped_function.args.args or (), inferred_wrapped_function.args.posonlyargs or (), inferred_wrapped_function.args.kwonlyargs or (), ) parameter_names = { param.name for param in function_parameters if isinstance(param, AssignName) } if set(call.keyword_arguments) - parameter_names: raise UseInferenceDefault( "wrapped function received unknown parameters") partial_function = objects.PartialFunction( call, name=inferred_wrapped_function.name, lineno=inferred_wrapped_function.lineno, col_offset=inferred_wrapped_function.col_offset, parent=node.parent, ) partial_function.postinit( args=inferred_wrapped_function.args, body=inferred_wrapped_function.body, decorators=inferred_wrapped_function.decorators, returns=inferred_wrapped_function.returns, type_comment_returns=inferred_wrapped_function.type_comment_returns, type_comment_args=inferred_wrapped_function.type_comment_args, doc_node=inferred_wrapped_function.doc_node, ) return iter((partial_function, ))
def infer_str(node, context=None): """Infer str() calls :param nodes.Call node: str() call to infer :param context.InferenceContext: node context :rtype nodes.Const: a Const containing an empty string """ call = arguments.CallSite.from_call(node, context=context) if call.keyword_arguments: raise UseInferenceDefault("TypeError: str() must take no keyword arguments") try: return nodes.Const("") except (AstroidTypeError, InferenceError) as exc: raise UseInferenceDefault(str(exc)) from exc
def _container_generic_transform( # pylint: disable=inconsistent-return-statements arg, context, klass, iterables, build_elts): if isinstance(arg, klass): return arg if isinstance(arg, iterables): if all(isinstance(elt, nodes.Const) for elt in arg.elts): elts = [elt.value for elt in arg.elts] else: # TODO: Does not handle deduplication for sets. elts = [] for element in arg.elts: if not element: continue inferred = helpers.safe_infer(element, context=context) if inferred: evaluated_object = nodes.EvaluatedObject(original=element, value=inferred) elts.append(evaluated_object) elif isinstance(arg, nodes.Dict): # Dicts need to have consts as strings already. if not all(isinstance(elt[0], nodes.Const) for elt in arg.items): raise UseInferenceDefault() elts = [item[0].value for item in arg.items] elif isinstance(arg, nodes.Const) and isinstance(arg.value, (str, bytes)): elts = arg.value else: return return klass.from_elements(elts=build_elts(elts))
def _infer_first(node, context): if node is util.Uninferable: raise UseInferenceDefault try: value = next(node.infer(context=context)) except StopIteration as exc: raise InferenceError from exc if value is util.Uninferable: raise UseInferenceDefault() return value
def infer_issubclass(callnode, context=None): """Infer issubclass() calls :param nodes.Call callnode: an `issubclass` call :param InferenceContext context: the context for the inference :rtype nodes.Const: Boolean Const value of the `issubclass` call :raises UseInferenceDefault: If the node cannot be inferred """ call = arguments.CallSite.from_call(callnode, context=context) if call.keyword_arguments: # issubclass doesn't support keyword arguments raise UseInferenceDefault("TypeError: issubclass() takes no keyword arguments") if len(call.positional_arguments) != 2: raise UseInferenceDefault( "Expected two arguments, got {count}".format( count=len(call.positional_arguments) ) ) # The left hand argument is the obj to be checked obj_node, class_or_tuple_node = call.positional_arguments try: obj_type = next(obj_node.infer(context=context)) except (InferenceError, StopIteration) as exc: raise UseInferenceDefault from exc if not isinstance(obj_type, nodes.ClassDef): raise UseInferenceDefault("TypeError: arg 1 must be class") # The right hand argument is the class(es) that the given # object is to be checked against. try: class_container = _class_or_tuple_to_container( class_or_tuple_node, context=context ) except InferenceError as exc: raise UseInferenceDefault from exc try: issubclass_bool = helpers.object_issubclass(obj_type, class_container, context) except AstroidTypeError as exc: raise UseInferenceDefault("TypeError: " + str(exc)) from exc except MroError as exc: raise UseInferenceDefault from exc return nodes.Const(issubclass_bool)
def infer_len(node, context=None): """Infer length calls :param nodes.Call node: len call to infer :param context.InferenceContext: node context :rtype nodes.Const: a Const node with the inferred length, if possible """ call = arguments.CallSite.from_call(node, context=context) if call.keyword_arguments: raise UseInferenceDefault( "TypeError: len() must take no keyword arguments") if len(call.positional_arguments) != 1: raise UseInferenceDefault( "TypeError: len() must take exactly one argument " "({len}) given".format(len=len(call.positional_arguments))) [argument_node] = call.positional_arguments try: return nodes.Const(helpers.object_len(argument_node, context=context)) except (AstroidTypeError, InferenceError) as exc: raise UseInferenceDefault(str(exc)) from exc
def infer_isinstance(callnode, context=None): """Infer isinstance calls :param nodes.Call callnode: an isinstance call :param InferenceContext context: context for call (currently unused but is a common interface for inference) :rtype nodes.Const: Boolean Const value of isinstance call :raises UseInferenceDefault: If the node cannot be inferred """ call = arguments.CallSite.from_call(callnode, context=context) if call.keyword_arguments: # isinstance doesn't support keyword arguments raise UseInferenceDefault("TypeError: isinstance() takes no keyword arguments") if len(call.positional_arguments) != 2: raise UseInferenceDefault( "Expected two arguments, got {count}".format( count=len(call.positional_arguments) ) ) # The left hand argument is the obj to be checked obj_node, class_or_tuple_node = call.positional_arguments # The right hand argument is the class(es) that the given # obj is to be check is an instance of try: class_container = _class_or_tuple_to_container( class_or_tuple_node, context=context ) except InferenceError as exc: raise UseInferenceDefault from exc try: isinstance_bool = helpers.object_isinstance(obj_node, class_container, context) except AstroidTypeError as exc: raise UseInferenceDefault("TypeError: " + str(exc)) from exc except MroError as exc: raise UseInferenceDefault from exc if isinstance_bool is util.Uninferable: raise UseInferenceDefault return nodes.Const(isinstance_bool)
def _infer_copy_method( node: nodes.Call, context: InferenceContext | None = None) -> Iterator[nodes.NodeNG]: assert isinstance(node.func, nodes.Attribute) inferred_orig, inferred_copy = itertools.tee( node.func.expr.infer(context=context)) if all( isinstance(inferred_node, (nodes.Dict, nodes.List, nodes.Set, objects.FrozenSet)) for inferred_node in inferred_orig): return inferred_copy raise UseInferenceDefault()
def infer_namespace(node, context=None): callsite = arguments.CallSite.from_call(node, context=context) if not callsite.keyword_arguments: # Cannot make sense of it. raise UseInferenceDefault() class_node = nodes.ClassDef("Namespace", "docstring") class_node.parent = node.parent for attr in set(callsite.keyword_arguments): fake_node = nodes.EmptyNode() fake_node.parent = class_node fake_node.attrname = attr class_node.instance_attrs[attr] = [fake_node] return iter((class_node.instantiate_class(),))
def _inference_tip_cached(func: InferFn, instance: None, args: typing.Any, kwargs: typing.Any) -> Iterator[InferOptions]: """Cache decorator used for inference tips""" node = args[0] try: result = _cache[func, node] # If through recursion we end up trying to infer the same # func + node we raise here. if result is None: raise UseInferenceDefault() except KeyError: _cache[func, node] = None result = _cache[func, node] = list(func(*args, **kwargs)) assert result return iter(result)
def _container_generic_inference(node, context, node_type, transform): args = node.args if not args: return node_type() if len(node.args) > 1: raise UseInferenceDefault() (arg, ) = args transformed = transform(arg) if not transformed: try: inferred = next(arg.infer(context=context)) except (InferenceError, StopIteration) as exc: raise UseInferenceDefault from exc if inferred is util.Uninferable: raise UseInferenceDefault transformed = transform(inferred) if not transformed or transformed is util.Uninferable: raise UseInferenceDefault return transformed
def infer_dict(node, context=None): """Try to infer a dict call to a Dict node. The function treats the following cases: * dict() * dict(mapping) * dict(iterable) * dict(iterable, **kwargs) * dict(mapping, **kwargs) * dict(**kwargs) If a case can't be inferred, we'll fallback to default inference. """ call = arguments.CallSite.from_call(node, context=context) if call.has_invalid_arguments() or call.has_invalid_keywords(): raise UseInferenceDefault args = call.positional_arguments kwargs = list(call.keyword_arguments.items()) if not args and not kwargs: # dict() return nodes.Dict() if kwargs and not args: # dict(a=1, b=2, c=4) items = [(nodes.Const(key), value) for key, value in kwargs] elif len(args) == 1 and kwargs: # dict(some_iterable, b=2, c=4) elts = _get_elts(args[0], context) keys = [(nodes.Const(key), value) for key, value in kwargs] items = elts + keys elif len(args) == 1: items = _get_elts(args[0], context) else: raise UseInferenceDefault() value = nodes.Dict(col_offset=node.col_offset, lineno=node.lineno, parent=node.parent) value.postinit(items) return value
def infer_type_sub(node, context=None): """ Infer a type[...] subscript :param node: node to infer :type node: astroid.nodes.node_classes.NodeNG :param context: inference context :type context: astroid.context.InferenceContext :return: the inferred node :rtype: nodes.NodeNG """ node_scope, _ = node.scope().lookup("type") if node_scope.qname() != "builtins": raise UseInferenceDefault() class_src = """ class type: def __class_getitem__(cls, key): return cls """ node = extract_node(class_src) return node.infer(context=context)
def _find_func_form_arguments(node, context): def _extract_namedtuple_arg_or_keyword( # pylint: disable=inconsistent-return-statements position, key_name=None ): if len(args) > position: return _infer_first(args[position], context) if key_name and key_name in found_keywords: return _infer_first(found_keywords[key_name], context) args = node.args keywords = node.keywords found_keywords = ( {keyword.arg: keyword.value for keyword in keywords} if keywords else {} ) name = _extract_namedtuple_arg_or_keyword(position=0, key_name="typename") names = _extract_namedtuple_arg_or_keyword(position=1, key_name="field_names") if name and names: return name.value, names raise UseInferenceDefault()
def infer_dict_fromkeys(node, context=None): """Infer dict.fromkeys :param nodes.Call node: dict.fromkeys() call to infer :param context.InferenceContext context: node context :rtype nodes.Dict: a Dictionary containing the values that astroid was able to infer. In case the inference failed for any reason, an empty dictionary will be inferred instead. """ def _build_dict_with_elements(elements): new_node = nodes.Dict(col_offset=node.col_offset, lineno=node.lineno, parent=node.parent) new_node.postinit(elements) return new_node call = arguments.CallSite.from_call(node, context=context) if call.keyword_arguments: raise UseInferenceDefault( "TypeError: int() must take no keyword arguments") if len(call.positional_arguments) not in {1, 2}: raise UseInferenceDefault( "TypeError: Needs between 1 and 2 positional arguments") default = nodes.Const(None) values = call.positional_arguments[0] try: inferred_values = next(values.infer(context=context)) except (InferenceError, StopIteration): return _build_dict_with_elements([]) if inferred_values is util.Uninferable: return _build_dict_with_elements([]) # Limit to a couple of potential values, as this can become pretty complicated accepted_iterable_elements = (nodes.Const, ) if isinstance(inferred_values, (nodes.List, nodes.Set, nodes.Tuple)): elements = inferred_values.elts for element in elements: if not isinstance(element, accepted_iterable_elements): # Fallback to an empty dict return _build_dict_with_elements([]) elements_with_value = [(element, default) for element in elements] return _build_dict_with_elements(elements_with_value) if isinstance(inferred_values, nodes.Const) and isinstance( inferred_values.value, (str, bytes)): elements = [(nodes.Const(element), default) for element in inferred_values.value] return _build_dict_with_elements(elements) if isinstance(inferred_values, nodes.Dict): keys = inferred_values.itered() for key in keys: if not isinstance(key, accepted_iterable_elements): # Fallback to an empty dict return _build_dict_with_elements([]) elements_with_value = [(element, default) for element in keys] return _build_dict_with_elements(elements_with_value) # Fallback to an empty dictionary return _build_dict_with_elements([])