def infer_isinstance(callnode, context=None): """Infer isinstance calls :param nodes.Call callnode: an isinstance call :param InferenceContext: 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) 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: raise UseInferenceDefault try: isinstance_bool = helpers.object_isinstance(obj_node, class_container, context) except AstroidTypeError as exc: raise UseInferenceDefault("TypeError: " + str(exc)) except MroError as exc: raise UseInferenceDefault from exc if isinstance_bool is util.Uninferable: raise UseInferenceDefault return nodes.Const(isinstance_bool)
def infer_objects_get_as_a(call_node, context=None): if getattr(call_node.func, "attrname") != 'get': raise UseInferenceDefault() code = """\ class A(object): class objects(object): @staticmethod def get(): pass def save(): pass""" m = astroid.parse(code, 'infer.this') class_node = m.body[0] return iter((class_node.instantiate_class(),))
def _find_func_form_arguments(node, context): args = node.args keywords = node.keywords if args and len(args) == 2: name = _infer_first(node.args[0], context).value names = _infer_first(node.args[1], context) return name, names elif keywords: found_keywords = {keyword.arg: keyword.value for keyword in keywords} if {'field_names', 'typename'}.issubset(found_keywords.keys()): name = _infer_first(found_keywords['typename'], context).value names = _infer_first(found_keywords['field_names'], context) return name, names raise UseInferenceDefault()
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() elif 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 _find_func_form_arguments(node, context): def _extract_namedtuple_arg_or_keyword(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 _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_type_sub(node, context=None): """ Infer a type[...] subscript :param node: node to infer :type node: astroid.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 _container_generic_transform(arg, klass, iterables, build_elts): if isinstance(arg, klass): return arg elif 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 = filter(None, map(helpers.safe_infer, arg.elts)) 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, (six.string_types, six.binary_type) ): elts = arg.value else: return return klass.from_elements(elts=build_elts(elts))
def infer_func_form(node, base_type, context=None, enum=False): """Specific inference function for namedtuple or Python 3 enum. """ # node is a Call node, class name as first argument and generated class # attributes as second argument # namedtuple or enums list of attributes can be a list of strings or a # whitespace-separate string try: name, names = _find_func_form_arguments(node, context) try: attributes = names.value.replace(",", " ").split() except AttributeError: if not enum: attributes = [ _infer_first(const, context).value for const in names.elts ] else: # Enums supports either iterator of (name, value) pairs # or mappings. if hasattr(names, "items") and isinstance(names.items, list): attributes = [ _infer_first(const[0], context).value for const in names.items if isinstance(const[0], nodes.Const) ] elif hasattr(names, "elts"): # Enums can support either ["a", "b", "c"] # or [("a", 1), ("b", 2), ...], but they can't # be mixed. if all( isinstance(const, nodes.Tuple) for const in names.elts): attributes = [ _infer_first(const.elts[0], context).value for const in names.elts if isinstance(const, nodes.Tuple) ] else: attributes = [ _infer_first(const, context).value for const in names.elts ] else: raise AttributeError if not attributes: raise AttributeError except (AttributeError, exceptions.InferenceError): raise UseInferenceDefault() attributes = [attr for attr in attributes if " " not in attr] # If we can't infer the name of the class, don't crash, up to this point # we know it is a namedtuple anyway. name = name or "Uninferable" # we want to return a Class node instance with proper attributes set class_node = nodes.ClassDef(name, "docstring") class_node.parent = node.parent # set base class=tuple class_node.bases.append(base_type) # XXX add __init__(*attributes) method for attr in attributes: fake_node = nodes.EmptyNode() fake_node.parent = class_node fake_node.attrname = attr class_node.instance_attrs[attr] = [fake_node] return class_node, name, attributes
def infer_dict_fromkeys(node, context=None): """Infer dict.fromkeys :param nodes.Call node: dict.fromkeys() call to infer :param context.InferenceContext: 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: 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) elif 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) elif 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([])
def infer_func_form(node, base_type, context=None, enum=False): """Specific inference function for namedtuple or Python 3 enum. """ def infer_first(node): if node is util.Uninferable: raise UseInferenceDefault try: value = next(node.infer(context=context)) if value is util.Uninferable: raise UseInferenceDefault() else: return value except StopIteration: raise InferenceError() # node is a Call node, class name as first argument and generated class # attributes as second argument if len(node.args) != 2: # something weird here, go back to class implementation raise UseInferenceDefault() # namedtuple or enums list of attributes can be a list of strings or a # whitespace-separate string try: name = infer_first(node.args[0]).value names = infer_first(node.args[1]) try: attributes = names.value.replace(',', ' ').split() except AttributeError: if not enum: attributes = [infer_first(const).value for const in names.elts] else: # Enums supports either iterator of (name, value) pairs # or mappings. # TODO: support only list, tuples and mappings. if hasattr(names, 'items') and isinstance(names.items, list): attributes = [ infer_first(const[0]).value for const in names.items if isinstance(const[0], nodes.Const) ] elif hasattr(names, 'elts'): # Enums can support either ["a", "b", "c"] # or [("a", 1), ("b", 2), ...], but they can't # be mixed. if all( isinstance(const, nodes.Tuple) for const in names.elts): attributes = [ infer_first(const.elts[0]).value for const in names.elts if isinstance(const, nodes.Tuple) ] else: attributes = [ infer_first(const).value for const in names.elts ] else: raise AttributeError if not attributes: raise AttributeError except (AttributeError, exceptions.InferenceError): raise UseInferenceDefault() # we want to return a Class node instance with proper attributes set class_node = nodes.ClassDef(name, 'docstring') class_node.parent = node.parent # set base class=tuple class_node.bases.append(base_type) # XXX add __init__(*attributes) method for attr in attributes: fake_node = nodes.EmptyNode() fake_node.parent = class_node fake_node.attrname = attr class_node.instance_attrs[attr] = [fake_node] return class_node, name, attributes