def _getargs(self, node, args): self.match_args(node, args) sig, = self.signatures callargs = { name: var for name, var, _ in sig.signature.iter_args(args) } # typing.NamedTuple doesn't support rename or verbose name_var = callargs["typename"] fields_var = callargs["fields"] fields = abstract_utils.get_atomic_python_constant(fields_var) # The fields is a list of tuples, so we need to deeply unwrap them. fields = [abstract_utils.get_atomic_python_constant(t) for t in fields] # We need the actual string for the field names and the AtomicAbstractValue # for the field types. names = [] types = [] for field in fields: if (len(field) != 2 or any(not self._is_str_instance(v) for v in field[0].data)): # Note that we don't need to check field[1] because both 'str' # (forward reference) and 'type' are valid for it. sig, = self.signatures bad_param = function.BadParam(name="fields", expected=self._fields_type) raise function.WrongArgTypes(sig.signature, args, self.vm, bad_param) name, typ = field names.append(abstract_utils.get_atomic_python_constant(name)) types.append(abstract_utils.get_atomic_value(typ)) return name_var, names, types
def compute_subst(self, node, formal_args, arg_dict, view, alias_map=None): """Compute information about type parameters using one-way unification. Given the arguments of a function call, try to find a substitution that matches them against the specified formal parameters. Args: node: The current CFG node. formal_args: An iterable of (name, value) pairs of formal arguments. arg_dict: A map of strings to pytd.Bindings instances. view: A mapping of Variable to Value. alias_map: Optionally, a datatypes.UnionFind, which stores all the type renaming information, mapping of type parameter name to its representative. Returns: A tuple (subst, name), with "subst" the datatypes.HashableDict if we found a working substition, None otherwise, and "name" the bad parameter in case subst=None. """ if not arg_dict: # A call with no arguments always succeeds. assert not formal_args return datatypes.AliasingDict(), None subst = datatypes.AliasingDict() if alias_map: subst.uf = alias_map self._set_error_subst(None) for name, formal in formal_args: actual = arg_dict[name] subst = self._match_value_against_type(actual, formal, subst, node, view) if subst is None: formal = self.vm.annotations_util.sub_one_annotation( node, formal, [self._error_subst or {}]) return None, function.BadParam(name=name, expected=formal) return datatypes.HashableDict(subst), None
def call(self, node, _, args): result = self.vm.program.NewVariable() num_args = len(args.posargs) if num_args == 0 and self.vm.PY3: # The implicit type argument is available in a freevar named '__class__'. cls_var = None # If we are in a list comprehension we want the enclosing frame. index = -1 while self.vm.frames[index].f_code.co_name == "<listcomp>": index -= 1 frame = self.vm.frames[index] for i, free_var in enumerate(frame.f_code.co_freevars): if free_var == abstract.BuildClass.CLOSURE_NAME: cls_var = frame.cells[len(frame.f_code.co_cellvars) + i] break if not (cls_var and cls_var.bindings): self.vm.errorlog.invalid_super_call( self.vm.frames, message="Missing __class__ closure for super call.", details= "Is 'super' being called from a method defined in a class?" ) return node, self.vm.new_unsolvable(node) # The implicit super object argument is the first positional argument to # the function calling 'super'. self_arg = frame.first_posarg if not self_arg: self.vm.errorlog.invalid_super_call( self.vm.frames, message="Missing 'self' argument to 'super' call.") return node, self.vm.new_unsolvable(node) super_objects = self_arg.bindings elif 1 <= num_args <= 2: cls_var = args.posargs[0] super_objects = args.posargs[1].bindings if num_args == 2 else [ None ] else: raise function.WrongArgCount(self._SIGNATURE, args, self.vm) for cls in cls_var.bindings: if not isinstance( cls.data, (class_mixin.Class, abstract.AMBIGUOUS_OR_EMPTY)): bad = function.BadParam(name="cls", expected=self.vm.convert.type_type) raise function.WrongArgTypes(self._SIGNATURE, args, self.vm, bad_param=bad) for obj in super_objects: if obj: result.AddBinding( SuperInstance(cls.data, obj.data, self.vm), [cls, obj], node) else: result.AddBinding(SuperInstance(cls.data, None, self.vm), [cls], node) return node, result
def compute_subst(self, node, formal_args, arg_dict, view, alias_map=None): """Compute information about type parameters using one-way unification. Given the arguments of a function call, try to find a substitution that matches them against the specified formal parameters. Args: node: The current CFG node. formal_args: An iterable of (name, value) pairs of formal arguments. arg_dict: A map of strings to pytd.Bindings instances. view: A mapping of Variable to Value. alias_map: Optionally, a datatypes.UnionFind, which stores all the type renaming information, mapping of type parameter name to its representative. Returns: A tuple (subst, name), with "subst" the datatypes.HashableDict if we found a working substition, None otherwise, and "name" the bad parameter in case subst=None. """ if not arg_dict: # A call with no arguments always succeeds. assert not formal_args return datatypes.AliasingDict(), None subst = datatypes.AliasingDict() if alias_map: subst.uf = alias_map self._set_error_subst(None) self_subst = None for name, formal in formal_args: actual = arg_dict[name] subst = self._match_value_against_type(actual, formal, subst, node, view) if subst is None: formal = self.vm.annotations_util.sub_one_annotation( node, formal, [self._error_subst or {}]) return None, function.BadParam(name=name, expected=formal) if name == "self": self_subst = subst if self_subst: # Type parameters matched from a 'self' arg are class parameters whose # values have been declared by the user, e.g.: # x = Container[int](__any_object__) # We should keep the 'int' value rather than using Union[int, Unknown]. for name, value in self_subst.items(): if any(not isinstance(v, abstract.Empty) for v in value.data): subst[name] = value return datatypes.HashableDict(subst), None
def make(cls, vm): typing_ast = vm.loader.import_name("typing") # Because NamedTuple is a special case for the pyi parser, typing.pytd has # "_NamedTuple" instead. Replace the name of the returned function so that # error messages will correctly display "typing.NamedTuple". pyval = typing_ast.Lookup("typing._NamedTuple") pyval = pyval.Replace(name="typing.NamedTuple") self = super().make("NamedTuple", vm, pyval) # NamedTuple's fields arg has type Sequence[Sequence[Union[str, type]]], # which doesn't provide precise enough type-checking, so we have to do # some of our own in _getargs. _NamedTupleFields is an alias to # List[Tuple[str, type]], which gives a more understandable error message. fields_pyval = typing_ast.Lookup("typing._NamedTupleFields").type fields_type = vm.convert.constant_to_value(fields_pyval, {}, vm.root_node) # pylint: disable=protected-access self._fields_param = function.BadParam(name="fields", expected=fields_type) return self