def _make_func(self, name="_", param_names=None, varargs_name=None, kwonly_params=(), kwargs_name=None, defaults=(), annotations=None, late_annotations=None): return abstract.SimpleFunction(name, param_names or (), varargs_name, kwonly_params, kwargs_name, defaults, annotations or {}, late_annotations or {}, self._vm)
def call(self, node, func, args): args = args.simplify(node) self._match_args(node, args, match_all_views=True) # As long as the types match we do not really care about the actual # class name. But, if we have a string literal value as the name arg, # we will use it. name_arg = args.namedargs.get(self._name_arg_name) or args.posargs[0] try: _ = abstract.get_atomic_python_constant(name_arg, str) except abstract.ConversionError: name_arg = self.vm.convert.constant_to_var( "_NewType_Internal_Class_Name_%d_" % self.internal_name_counter) type_arg = args.namedargs.get(self._type_arg_name) or args.posargs[1] try: type_value = abstract.get_atomic_value(type_arg) except abstract.ConversionError: # We need the type arg to be an atomic value. If not, we just # silently return unsolvable. return node, self.vm.convert.create_new_unsolvable(node) value_arg_name = "val" constructor = abstract.SimpleFunction( name="__init__", param_names=("self", value_arg_name), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={}, annotations={value_arg_name: type_value}, late_annotations={}, vm=self.vm, ).to_variable(node) members = abstract.Dict(self.vm) members.set_str_item(node, "__init__", constructor) return node, self.vm.make_class(node, name_arg, (type_arg,), members.to_variable(node), None)
def _build_namedtuple(self, name, field_names, field_types, node): # Build an InterpreterClass representing the namedtuple. if field_types: field_types_union = abstract.Union(field_types, self.vm) else: field_types_union = self.vm.convert.none_type members = { n: t.instantiate(node) for n, t in moves.zip(field_names, field_types) } # collections.namedtuple has: __dict__, __slots__ and _fields. # typing.NamedTuple adds: _field_types, __annotations__ and _field_defaults. # __slots__ and _fields are tuples containing the names of the fields. slots = tuple( self.vm.convert.build_string(node, f) for f in field_names) members["__slots__"] = abstract.Tuple(slots, self.vm).to_variable(node) members["_fields"] = abstract.Tuple(slots, self.vm).to_variable(node) # __dict__ and _field_defaults are both collections.OrderedDicts that map # field names (strings) to objects of the field types. ordered_dict_cls = self.vm.convert.name_to_value( "collections.OrderedDict", ast=self.collections_ast) # In Python 2, keys can be `str` or `unicode`; support both. # In Python 3, `str_type` and `unicode_type` are the same. field_keys_union = abstract.Union( [self.vm.convert.str_type, self.vm.convert.unicode_type], self.vm) # Normally, we would use abstract_utils.K and abstract_utils.V, but # collections.pyi doesn't conform to that standard. field_dict_cls = abstract.ParameterizedClass(ordered_dict_cls, { "K": field_keys_union, "V": field_types_union }, self.vm) members["__dict__"] = field_dict_cls.instantiate(node) members["_field_defaults"] = field_dict_cls.instantiate(node) # _field_types and __annotations__ are both collections.OrderedDicts # that map field names (strings) to the types of the fields. field_types_cls = abstract.ParameterizedClass( ordered_dict_cls, { "K": field_keys_union, "V": self.vm.convert.type_type }, self.vm) members["_field_types"] = field_types_cls.instantiate(node) members["__annotations__"] = field_types_cls.instantiate(node) # __new__ new_annots = {} new_lates = {} for (n, t) in moves.zip(field_names, field_types): # We don't support late annotations yet, but once we do, they'll show up # as LateAnnotation objects to be stored in new_lates. new_annots[n] = t # We set the bound on this TypeParameter later. This gives __new__ the # signature: def __new__(cls: Type[_Tname], ...) -> _Tname, i.e. the same # signature that visitor.CreateTypeParametersForSignatures would create. # This allows subclasses of the NamedTuple to get the correct type from # their constructors. cls_type_param = abstract.TypeParameter( visitors.CreateTypeParametersForSignatures.PREFIX + name, self.vm, bound=None) new_annots["cls"] = abstract.ParameterizedClass( self.vm.convert.type_type, {abstract_utils.T: cls_type_param}, self.vm) new_annots["return"] = cls_type_param members["__new__"] = abstract.SimpleFunction( name="__new__", param_names=("cls", ) + tuple(field_names), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={}, annotations=new_annots, late_annotations=new_lates, vm=self.vm).to_variable(node) # __init__ members["__init__"] = abstract.SimpleFunction( name="__init__", param_names=("self", ), varargs_name="args", kwonly_params=(), kwargs_name="kwargs", defaults={}, annotations={}, late_annotations={}, vm=self.vm).to_variable(node) # _make # _make is a classmethod, so it needs to be wrapped by # specialibuiltins.ClassMethodInstance. # Like __new__, it uses the _Tname TypeVar. sized_cls = self.vm.convert.name_to_value("typing.Sized") iterable_type = abstract.ParameterizedClass( self.vm.convert.name_to_value("typing.Iterable"), {abstract_utils.T: field_types_union}, self.vm) make = abstract.SimpleFunction( name="_make", param_names=("cls", "iterable", "new", "len"), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={ "new": self.vm.convert.unsolvable.to_variable(node), "len": self.vm.convert.unsolvable.to_variable(node) }, annotations={ "cls": abstract.ParameterizedClass(self.vm.convert.type_type, {abstract_utils.T: cls_type_param}, self.vm), "iterable": iterable_type, "new": self.vm.convert.unsolvable, "len": abstract.Callable( self.vm.convert.name_to_value("typing.Callable"), { 0: sized_cls, abstract_utils.ARGS: sized_cls, abstract_utils.RET: self.vm.convert.int_type }, self.vm), "return": cls_type_param }, late_annotations={}, vm=self.vm).to_variable(node) make_args = function.Args(posargs=(make, )) _, members["_make"] = self.vm.special_builtins["classmethod"].call( node, None, make_args) # _replace # Like __new__, it uses the _Tname TypeVar. We have to annotate the `self` # param to make sure the TypeVar is substituted correctly. members["_replace"] = abstract.SimpleFunction( name="_replace", param_names=("self", ), varargs_name=None, kwonly_params=(), kwargs_name="kwds", defaults={}, annotations={ "self": cls_type_param, "kwds": field_types_union, "return": cls_type_param }, late_annotations={}, vm=self.vm).to_variable(node) # __getnewargs__ getnewargs_tuple_params = dict( tuple(enumerate(field_types)) + ((abstract_utils.T, field_types_union), )) getnewargs_tuple = abstract.TupleClass(self.vm.convert.tuple_type, getnewargs_tuple_params, self.vm) members["__getnewargs__"] = abstract.SimpleFunction( name="__getnewargs__", param_names=("self", ), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={}, annotations={ "return": getnewargs_tuple }, late_annotations={}, vm=self.vm).to_variable(node) # __getstate__ members["__getstate__"] = abstract.SimpleFunction( name="__getstate__", param_names=("self", ), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={}, annotations={}, late_annotations={}, vm=self.vm).to_variable(node) # _asdict members["_asdict"] = abstract.SimpleFunction( name="_asdict", param_names=("self", ), varargs_name=None, kwonly_params=(), kwargs_name=None, defaults={}, annotations={ "return": field_dict_cls }, late_annotations={}, vm=self.vm).to_variable(node) # Finally, make the class. abs_membs = abstract.Dict(self.vm) abs_membs.update(node, members) cls_var = self.vm.make_class( node=node, name_var=self.vm.convert.build_string(node, name), bases=[self.vm.convert.tuple_type.to_variable(node)], class_dict_var=abs_membs.to_variable(node), cls_var=None) # Now that the class has been made, we can complete the TypeParameter used # by __new__, _make and _replace. cls_type_param.bound = cls_var.data[0] return cls_var
def make_method(vm, node, name, params=None, kwonly_params=None, return_type=None, self_param=None, varargs=None, kwargs=None): """Make a method from params. Args: vm: vm node: Node to create the method variable at name: The method name params: Positional params [type: [Param]] kwonly_params: Keyword only params [type: [Param]] return_type: Return type [type: PARAM_TYPES] self_param: Self param [type: Param, defaults to self: Any] varargs: Varargs param [type: Param, allows *args to be named and typed] kwargs: Kwargs param [type: Param, allows **kwargs to be named and typed] Returns: A new method wrapped in a variable. """ def _process_annotation(param): """Process a single param into either annotations or late_annotations.""" if not param.typ: return elif isinstance(param.typ, cfg.Variable): if all(t.cls for t in param.typ.data): types = param.typ.data if len(types) == 1: annotations[param.name] = types[0].cls else: t = abstract.Union([t.cls for t in types], vm) annotations[param.name] = t elif isinstance(param.typ, abstract.LateAnnotation): late_annotations[param.name] = param.typ else: annotations[param.name] = param.typ # Set default values params = params or [] kwonly_params = kwonly_params or [] self_param = self_param or Param("self", None, None) annotations = {} late_annotations = {} params = [self_param] + params return_param = Param("return", return_type, None) if return_type else None special_params = [x for x in (return_param, varargs, kwargs) if x] for param in special_params + params + kwonly_params: _process_annotation(param) if vm.PY2: assert not kwonly_params, "kwonly_params is unsupported in python2" names = lambda xs: tuple(x.name for x in xs) param_names = names(params) kwonly_names = names(kwonly_params) defaults = {x.name: x.default for x in params if x.default} varargs_name = varargs.name if varargs else None kwargs_name = kwargs.name if kwargs else None ret = abstract.SimpleFunction(name=name, param_names=param_names, varargs_name=varargs_name, kwonly_params=kwonly_names, kwargs_name=kwargs_name, defaults=defaults, annotations=annotations, late_annotations=late_annotations, vm=vm) if late_annotations: vm.functions_with_late_annotations.append(ret) return ret.to_variable(node)
def make_method(vm, node, name, params=None, kwonly_params=None, return_type=None, self_param=None, varargs=None, kwargs=None, kind=pytd.MethodTypes.METHOD): """Make a method from params. Args: vm: vm node: Node to create the method variable at name: The method name params: Positional params [type: [Param]] kwonly_params: Keyword only params [type: [Param]] return_type: Return type [type: PARAM_TYPES] self_param: Self param [type: Param, defaults to self: Any] varargs: Varargs param [type: Param, allows *args to be named and typed] kwargs: Kwargs param [type: Param, allows **kwargs to be named and typed] kind: The method kind Returns: A new method wrapped in a variable. """ def _process_annotation(param): """Process a single param into annotations.""" if not param.typ: return elif isinstance(param.typ, cfg.Variable): if all(t.cls for t in param.typ.data): types = param.typ.data if len(types) == 1: annotations[param.name] = types[0].cls else: t = abstract.Union([t.cls for t in types], vm) annotations[param.name] = t else: annotations[param.name] = param.typ # Set default values params = params or [] kwonly_params = kwonly_params or [] if kind in (pytd.MethodTypes.METHOD, pytd.MethodTypes.PROPERTY): self_param = [self_param or Param("self", None, None)] elif kind == pytd.MethodTypes.CLASSMETHOD: self_param = [Param("cls", None, None)] else: assert kind == pytd.MethodTypes.STATICMETHOD self_param = [] annotations = {} params = self_param + params return_param = Param("return", return_type, None) if return_type else None special_params = [x for x in (return_param, varargs, kwargs) if x] for param in special_params + params + kwonly_params: _process_annotation(param) if vm.PY2: assert not kwonly_params, "kwonly_params is unsupported in python2" names = lambda xs: tuple(x.name for x in xs) param_names = names(params) kwonly_names = names(kwonly_params) defaults = {x.name: x.default for x in params + kwonly_params if x.default} varargs_name = varargs.name if varargs else None kwargs_name = kwargs.name if kwargs else None ret = abstract.SimpleFunction(name=name, param_names=param_names, varargs_name=varargs_name, kwonly_params=kwonly_names, kwargs_name=kwargs_name, defaults=defaults, annotations=annotations, vm=vm) # Check that the constructed function has a valid signature bad_param = ret.signature.check_defaults() if bad_param: msg = "In method %s, non-default argument %s follows default argument" % ( name, bad_param) vm.errorlog.invalid_function_definition(vm.frames, msg) retvar = ret.to_variable(node) if kind in (pytd.MethodTypes.METHOD, pytd.MethodTypes.PROPERTY): return retvar if kind == pytd.MethodTypes.CLASSMETHOD: decorator = vm.load_special_builtin("classmethod") else: assert kind == pytd.MethodTypes.STATICMETHOD decorator = vm.load_special_builtin("staticmethod") args = function.Args(posargs=(retvar, )) return decorator.call(node, funcv=None, args=args)[1]