def test_generic_function_type(self) -> None: c = CallableType([self.x, self.y], [ARG_POS, ARG_POS], [None, None], self.y, self.function, name=None, variables=[TypeVarType('X', 'X', -1, [], self.fx.o)]) assert_equal(str(c), 'def [X] (X?, Y?) -> Y?') v = [TypeVarType('Y', 'Y', -1, [], self.fx.o), TypeVarType('X', 'X', -2, [], self.fx.o)] c2 = CallableType([], [], [], NoneType(), self.function, name=None, variables=v) assert_equal(str(c2), 'def [Y, X] ()')
def typed_dict_get_signature_callback( object_type: Type, args: List[List[Expression]], signature: CallableType, named_generic_type: Callable[[str, List[Type]], Type]) -> CallableType: """Try to infer a better signature type for TypedDict.get. This is used to get better type context for the second argument that depends on a TypedDict value type. """ if (isinstance(object_type, TypedDictType) and len(args) == 2 and len(args[0]) == 1 and isinstance(args[0][0], StrExpr) and len(signature.arg_types) == 2 and len(signature.variables) == 1): key = args[0][0].value value_type = object_type.items.get(key) if value_type: # Tweak the signature to include the value type as context. It's # only needed for type inference since there's a union with a type # variable that accepts everything. tv = TypeVarType(signature.variables[0]) return signature.copy_modified(arg_types=[ signature.arg_types[0], UnionType.make_simplified_union([value_type, tv]) ]) return signature
def fill_typevars(typ: TypeInfo) -> Union[Instance, TupleType]: """For a non-generic type, return instance type representing the type. For a generic G type with parameters T1, .., Tn, return G[T1, ..., Tn]. """ tvs: List[Type] = [] # TODO: why do we need to keep both typ.type_vars and typ.defn.type_vars? for i in range(len(typ.defn.type_vars)): tv = typ.defn.type_vars[i] # Change the line number tv = TypeVarType( tv.name, tv.fullname, tv.id, tv.values, tv.upper_bound, tv.variance, line=-1, column=-1, ) tvs.append(tv) inst = Instance(typ, tvs) if typ.tuple_type is None: return inst return typ.tuple_type.copy_modified(fallback=inst)
def typed_dict_get_signature_callback(ctx: MethodSigContext) -> CallableType: """Try to infer a better signature type for TypedDict.get. This is used to get better type context for the second argument that depends on a TypedDict value type. """ signature = ctx.default_signature if (isinstance(ctx.type, TypedDictType) and len(ctx.args) == 2 and len(ctx.args[0]) == 1 and isinstance(ctx.args[0][0], StrExpr) and len(signature.arg_types) == 2 and len(signature.variables) == 1 and len(ctx.args[1]) == 1): key = ctx.args[0][0].value value_type = ctx.type.items.get(key) ret_type = signature.ret_type if value_type: default_arg = ctx.args[1][0] if (isinstance(value_type, TypedDictType) and isinstance(default_arg, DictExpr) and len(default_arg.items) == 0): # Caller has empty dict {} as default for typed dict. value_type = value_type.copy_modified(required_keys=set()) # Tweak the signature to include the value type as context. It's # only needed for type inference since there's a union with a type # variable that accepts everything. tv = TypeVarType(signature.variables[0]) return signature.copy_modified(arg_types=[ signature.arg_types[0], UnionType.make_simplified_union([value_type, tv]) ], ret_type=ret_type) return signature
def _add_cmp(ctx: 'mypy.plugin.ClassDefContext', adder: 'MethodAdder') -> None: """Generate all the cmp methods for this class.""" # For __ne__ and __eq__ the type is: # def __ne__(self, other: object) -> bool bool_type = ctx.api.named_type('__builtins__.bool') object_type = ctx.api.named_type('__builtins__.object') args = [Argument(Var('other', object_type), object_type, None, ARG_POS)] for method in ['__ne__', '__eq__']: adder.add_method(method, args, bool_type) # For the rest we use: # AT = TypeVar('AT') # def __lt__(self: AT, other: AT) -> bool # This way comparisons with subclasses will work correctly. tvd = TypeVarDef(SELF_TVAR_NAME, ctx.cls.info.fullname() + '.' + SELF_TVAR_NAME, -1, [], object_type) tvd_type = TypeVarType(tvd) self_tvar_expr = TypeVarExpr( SELF_TVAR_NAME, ctx.cls.info.fullname() + '.' + SELF_TVAR_NAME, [], object_type) ctx.cls.info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr) args = [Argument(Var('other', tvd_type), tvd_type, None, ARG_POS)] for method in ['__lt__', '__le__', '__gt__', '__ge__']: adder.add_method(method, args, bool_type, self_type=tvd_type, tvd=tvd)
def add_construct_method(self, fields: List["PydanticModelField"]) -> None: """ Adds a fully typed `construct` classmethod to the class. Similar to the fields-aware __init__ method, but always uses the field names (not aliases), and does not treat settings fields as optional. """ ctx = self._ctx set_str = ctx.api.named_type("__builtins__.set", [ctx.api.named_type("__builtins__.str")]) optional_set_str = UnionType([set_str, NoneType()]) fields_set_argument = Argument(Var("_fields_set", optional_set_str), optional_set_str, None, ARG_OPT) construct_arguments = self.get_field_arguments( fields, typed=True, force_all_optional=False, use_alias=False) construct_arguments = [fields_set_argument] + construct_arguments obj_type = ctx.api.named_type("__builtins__.object") self_tvar_name = "Model" tvar_fullname = ctx.cls.fullname + "." + self_tvar_name tvd = TypeVarDef(self_tvar_name, tvar_fullname, -1, [], obj_type) self_tvar_expr = TypeVarExpr(self_tvar_name, tvar_fullname, [], obj_type) ctx.cls.info.names[self_tvar_name] = SymbolTableNode( MDEF, self_tvar_expr) self_type = TypeVarType(tvd) add_method( ctx, "construct", construct_arguments, return_type=self_type, self_type=self_type, tvar_def=tvd, is_classmethod=True, )
def typed_dict_pop_signature_callback(ctx: MethodSigContext) -> CallableType: """Try to infer a better signature type for TypedDict.pop. This is used to get better type context for the second argument that depends on a TypedDict value type. """ signature = ctx.default_signature str_type = ctx.api.named_generic_type('builtins.str', []) if (isinstance(ctx.type, TypedDictType) and len(ctx.args) == 2 and len(ctx.args[0]) == 1 and isinstance(ctx.args[0][0], StrExpr) and len(signature.arg_types) == 2 and len(signature.variables) == 1 and len(ctx.args[1]) == 1): key = ctx.args[0][0].value value_type = ctx.type.items.get(key) if value_type: # Tweak the signature to include the value type as context. It's # only needed for type inference since there's a union with a type # variable that accepts everything. assert isinstance(signature.variables[0], TypeVarDef) tv = TypeVarType(signature.variables[0]) typ = make_simplified_union([value_type, tv]) return signature.copy_modified( arg_types=[str_type, typ], ret_type=typ) return signature.copy_modified(arg_types=[str_type, signature.arg_types[1]])
def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: if self.is_class_scope: self.class_id += 1 i = self.class_id else: self.func_id -= 1 i = self.func_id if isinstance(tvar_expr, TypeVarExpr): tvar_def: TypeVarLikeType = TypeVarType( name, tvar_expr.fullname, i, values=tvar_expr.values, upper_bound=tvar_expr.upper_bound, variance=tvar_expr.variance, line=tvar_expr.line, column=tvar_expr.column) elif isinstance(tvar_expr, ParamSpecExpr): tvar_def = ParamSpecType(name, tvar_expr.fullname, i, line=tvar_expr.line, column=tvar_expr.column) else: assert False self.scope[tvar_expr.fullname] = tvar_def return tvar_def
def snapshot_definition(node: Optional[SymbolNode], common: Tuple[object, ...]) -> Tuple[object, ...]: """Create a snapshot description of a symbol table node. The representation is nested tuples and dicts. Only externally visible attributes are included. """ if isinstance(node, (OverloadedFuncDef, FuncItem)): # TODO: info if node.type: signature = snapshot_type(node.type) else: signature = snapshot_untyped_signature(node) return ('Func', common, node.is_property, signature) elif isinstance(node, Var): return ('Var', common, snapshot_optional_type(node.type)) elif isinstance(node, Decorator): # Note that decorated methods are represented by Decorator instances in # a symbol table since we need to preserve information about the # decorated function (whether it's a class function, for # example). Top-level decorated functions, however, are represented by # the corresponding Var node, since that happens to provide enough # context. return ('Decorator', node.is_overload, snapshot_optional_type(node.var.type), snapshot_definition(node.func, common)) elif isinstance(node, TypeInfo): attrs = ( node.is_abstract, node.is_enum, node.fallback_to_any, node.is_named_tuple, node.is_newtype, # We need this to e.g. trigger metaclass calculation in subclasses. snapshot_optional_type(node.metaclass_type), snapshot_optional_type(node.tuple_type), snapshot_optional_type(node.typeddict_type), [base.fullname() for base in node.mro], # Note that the structure of type variables is a part of the external interface, # since creating instances might fail, for example: # T = TypeVar('T', bound=int) # class C(Generic[T]): # ... # x: C[str] <- this is invalid, and needs to be re-checked if `T` changes. # An alternative would be to create both deps: <...> -> C, and <...> -> <C>, # but this currently seems a bit ad hoc. tuple( snapshot_type(TypeVarType(tdef)) for tdef in node.defn.type_vars), [snapshot_type(base) for base in node.bases], snapshot_optional_type(node._promote)) prefix = node.fullname() symbol_table = snapshot_symbol_table(prefix, node.names) # Special dependency for abstract attribute handling. symbol_table['(abstract)'] = ('Abstract', tuple(sorted(node.abstract_attributes))) return ('TypeInfo', common, attrs, symbol_table) else: # Other node types are handled elsewhere. assert False, type(node)
def _get_compose_type(context: FunctionContext) -> t.Optional[CallableType]: # TODO, why are the arguments lists of lists, # and do I need to worry about it? n_args = len([at for ats in context.arg_types for at in ats]) arg_types = [] arg_kinds = [] arg_names = [] ret_type_def = _type_var_def('R1', 'pfun.compose', context.api.named_type('builtins.object')) ret_type = TypeVarType(ret_type_def) variables = [ret_type_def] for n in range(n_args): current_arg_type_def = _type_var_def( f'R{n + 2}', 'pfun.compose', context.api.named_type('builtins.object')) current_arg_type = TypeVarType(current_arg_type_def) arg_type = CallableType( arg_types=[current_arg_type], ret_type=ret_type, arg_kinds=[ARG_POS], arg_names=[None], variables=[current_arg_type_def, ret_type_def], fallback=context.api.named_type('builtins.function')) arg_types.append(arg_type) arg_kinds.append(ARG_POS) arg_names.append(None) variables.append(current_arg_type_def) ret_type_def = current_arg_type_def ret_type = current_arg_type first_arg_type, *_, last_arg_type = arg_types ret_type = CallableType( arg_types=last_arg_type.arg_types, arg_names=last_arg_type.arg_names, arg_kinds=last_arg_type.arg_kinds, ret_type=first_arg_type.ret_type, variables=[first_arg_type.variables[-1], last_arg_type.variables[0]], fallback=context.api.named_type('builtins.function')) return CallableType(arg_types=arg_types, arg_kinds=arg_kinds, arg_names=arg_names, ret_type=ret_type, variables=variables, fallback=context.api.named_type('builtins.function'), name='compose')
def _set_lens_method_types(lens: Instance) -> None: arg_type = lens.args[0] t_def = _type_var_def('A', 'pfun.lens', upper_bound=arg_type, variance=COVARIANT) t_var = TypeVarType(t_def) __call__ = lens.type.names['__call__'] _set_method_type_vars(__call__, t_var, t_def)
def analyze_class_attribute_access(itype: Instance, name: str, context: Context, is_lvalue: bool, builtin_type: Callable[[str], Instance], not_ready_callback: Callable[[str, Context], None], msg: MessageBuilder, original_type: Type) -> Type: """original_type is the type of E in the expression E.var""" node = itype.type.get(name) if not node: if itype.type.fallback_to_any: return AnyType() return None is_decorated = isinstance(node.node, Decorator) is_method = is_decorated or isinstance(node.node, FuncDef) if is_lvalue: if is_method: msg.cant_assign_to_method(context) if isinstance(node.node, TypeInfo): msg.fail(messages.CANNOT_ASSIGN_TO_TYPE, context) if itype.type.is_enum and not (is_lvalue or is_decorated or is_method): return itype t = node.type if t: if isinstance(t, PartialType): return handle_partial_attribute_type(t, is_lvalue, msg, node.node) if not is_method and (isinstance(t, TypeVarType) or get_type_vars(t)): msg.fail(messages.GENERIC_INSTANCE_VAR_CLASS_ACCESS, context) is_classmethod = is_decorated and cast(Decorator, node.node).func.is_class return add_class_tvars(t, itype, is_classmethod, builtin_type, original_type) elif isinstance(node.node, Var): not_ready_callback(name, context) return AnyType() if isinstance(node.node, TypeVarExpr): return TypeVarType(node.tvar_def, node.tvar_def.line, node.tvar_def.column) if isinstance(node.node, TypeInfo): return type_object_type(node.node, builtin_type) if isinstance(node.node, MypyFile): # Reference to a module object. return builtin_type('builtins.module') if is_decorated: # TODO: Return type of decorated function. This is quick hack to work around #998. return AnyType() else: return function_type(cast(FuncBase, node.node), builtin_type('builtins.function'))
def visit_type_var(self, t: TypeVarType) -> ProperType: dup = TypeVarType( t.name, t.fullname, t.id, values=t.values, upper_bound=t.upper_bound, variance=t.variance, ) return self.copy_common(t, dup)
def fill_typevars(typ: TypeInfo) -> Union[Instance, TupleType]: """For a non-generic type, return instance type representing the type. For a generic G type with parameters T1, .., Tn, return G[T1, ..., Tn]. """ tv = [] # type: List[Type] for i in range(len(typ.type_vars)): tv.append(TypeVarType(typ.defn.type_vars[i])) inst = Instance(typ, tv) if typ.tuple_type is None: return inst return typ.tuple_type.copy_modified(fallback=inst)
def add_primitive_method(self): ctx = self._ctx self_tvar_def = self._get_tvar_def(SELF_TVAR_NAME, ctx) bool_type = ctx.api.named_type("__builtins__.bool") arg = Argument(Var("lazy", bool_type), bool_type, None, ARG_NAMED_OPT) add_method( ctx, "primitive", args=[arg], return_type=AnyType(TypeOfAny.unannotated), self_type=TypeVarType(self_tvar_def), tvar_def=self_tvar_def, )
def add_transmute_method(self): ctx = self._ctx self_tvar_def = self._get_tvar_def(SELF_TVAR_NAME, ctx) arg_type = AnyType(TypeOfAny.explicit) arg = Argument(Var("obj", arg_type), arg_type, None, ARG_POS) add_method( ctx, "transmute", args=[arg], return_type=TypeVarType(self_tvar_def), tvar_def=self_tvar_def, is_staticmethod=True, )
def _combine_hook(context: FunctionContext): result_types = [] error_types = [] env_types = [] try: for effect_type in context.arg_types[0]: env_type, error_type, result_type = get_proper_type( effect_type).args env_types.append(env_type) error_types.append(error_type) result_types.append(result_type) map_return_type_def = _type_var_def( 'R1', 'pfun.effect', context.api.named_type('builtins.object')) map_return_type = TypeVarType(map_return_type_def) map_function_type = CallableType( arg_types=result_types, arg_kinds=[ARG_POS for _ in result_types], arg_names=[None for _ in result_types], ret_type=map_return_type, variables=[map_return_type_def], fallback=context.api.named_type('builtins.function')) ret_type = context.default_return_type.ret_type combined_error_type = UnionType.make_union( sorted(set(error_types), key=str)) ret_type_args = ret_type.args ret_type_args[1] = combined_error_type ret_type_args[2] = map_return_type env_types = [ env_type for env_type in env_types if not isinstance(env_type, AnyType) ] if len(set(env_types)) == 1: combined_env_type = env_types[0] elif env_types and all( hasattr(env_type, 'type') and env_type.type.is_protocol for env_type in env_types): combined_env_type = reduce(_combine_protocols, env_types) else: combined_env_type = ret_type_args[0] ret_type_args[0] = combined_env_type ret_type = ret_type.copy_modified(args=ret_type_args) return CallableType( arg_types=[map_function_type], arg_kinds=[ARG_POS], arg_names=[None], variables=[map_return_type_def], ret_type=ret_type, fallback=context.api.named_type('builtins.function')) except AttributeError: return context.default_return_type
def add_translate_method(self): ctx = self._ctx self_tvar_def = self._get_tvar_def(SELF_TVAR_NAME, ctx) r_type = AnyType(TypeOfAny.explicit) arg_type = TypeType(r_type) arg = Argument(Var("target", arg_type), arg_type, None, ARG_POS) add_method( ctx, "translate", args=[arg], return_type=r_type, self_type=TypeVarType(self_tvar_def), tvar_def=self_tvar_def, )
def add_iter_method(self): ctx = self._ctx self_tvar_def = self._get_tvar_def(SELF_TVAR_NAME, ctx) r_type = AnyType(TypeOfAny.explicit) bool_type = ctx.api.named_type("__builtins__.bool") arg = Argument(Var("values", bool_type), bool_type, None, ARG_NAMED_OPT) add_method( ctx, "__iter__", args=[arg], return_type=r_type, self_type=TypeVarType(self_tvar_def), tvar_def=self_tvar_def, )
def callable(self, vars: List[str], *a: Type) -> CallableType: """callable(args, a1, ..., an, r) constructs a callable with argument types a1, ... an and return type r and type arguments vars. """ tv: List[TypeVarType] = [] n = -1 for v in vars: tv.append(TypeVarType(v, v, n, [], self.fx.o)) n -= 1 return CallableType(list(a[:-1]), [ARG_POS] * (len(a) - 1), [None] * (len(a) - 1), a[-1], self.fx.function, name=None, variables=tv)
def _add_order(ctx: 'mypy.plugin.ClassDefContext', adder: 'MethodAdder') -> None: """Generate all the ordering methods for this class.""" bool_type = ctx.api.named_type('__builtins__.bool') object_type = ctx.api.named_type('__builtins__.object') # Make the types be: # AT = TypeVar('AT') # def __lt__(self: AT, other: AT) -> bool # This way comparisons with subclasses will work correctly. tvd = TypeVarType(SELF_TVAR_NAME, ctx.cls.info.fullname + '.' + SELF_TVAR_NAME, -1, [], object_type) self_tvar_expr = TypeVarExpr(SELF_TVAR_NAME, ctx.cls.info.fullname + '.' + SELF_TVAR_NAME, [], object_type) ctx.cls.info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr) args = [Argument(Var('other', tvd), tvd, None, ARG_POS)] for method in ['__lt__', '__le__', '__gt__', '__ge__']: adder.add_method(method, args, bool_type, self_type=tvd, tvd=tvd)
def _add_cmp(ctx: 'mypy.plugin.ClassDefContext', adder: 'MethodAdder') -> None: """Generate all the cmp methods for this class.""" # For __ne__ and __eq__ the type is: # def __ne__(self, other: object) -> bool bool_type = ctx.api.named_type('__builtins__.bool') object_type = ctx.api.named_type('__builtins__.object') args = [Argument(Var('other', object_type), object_type, None, ARG_POS)] for method in ['__ne__', '__eq__']: adder.add_method(method, args, bool_type) # For the rest we use: # AT = TypeVar('AT') # def __lt__(self: AT, other: AT) -> bool # This way comparisons with subclasses will work correctly. tvd = TypeVarDef('AT', 'AT', 1, [], object_type) tvd_type = TypeVarType(tvd) args = [Argument(Var('other', tvd_type), tvd_type, None, ARG_POS)] for method in ['__lt__', '__le__', '__gt__', '__ge__']: adder.add_method(method, args, bool_type, self_type=tvd_type, tvd=tvd)
def freshen_function_type_vars(callee: F) -> F: """Substitute fresh type variables for generic function type variables.""" if isinstance(callee, CallableType): if not callee.is_generic(): return cast(F, callee) tvdefs = [] tvmap = {} # type: Dict[TypeVarId, Type] for v in callee.variables: tvdef = TypeVarDef.new_unification_variable(v) tvdefs.append(tvdef) tvmap[v.id] = TypeVarType(tvdef) fresh = cast(CallableType, expand_type(callee, tvmap)).copy_modified(variables=tvdefs) return cast(F, fresh) else: assert isinstance(callee, Overloaded) fresh_overload = Overloaded([freshen_function_type_vars(item) for item in callee.items()]) return cast(F, fresh_overload)
def add_schema_method(self): ctx = self._ctx api: SemanticAnalyzer = ctx.api return_type_info: SymbolTableNode = api.lookup_fully_qualified( "typic.SchemaReturnT") self_tvar_def = self._get_tvar_def(SELF_TVAR_NAME, ctx) arg_type = api.named_type("__builtins__.bool") arg = Argument(Var("primitive", arg_type), arg_type, None, ARG_NAMED_OPT) add_method( ctx, "schema", args=[arg], return_type=return_type_info.node.target, self_type=TypeVarType(self_tvar_def), tvar_def=self_tvar_def, is_classmethod=True, )
def add_json_method(self): ctx = self._ctx self_tvar_def = self._get_tvar_def(SELF_TVAR_NAME, ctx) bool_type = ctx.api.named_type("__builtins__.bool") int_type = ctx.api.named_type("__builtins__.int") str_type = ctx.api.named_type("__builtins__.str") indent = Argument(Var("indent", int_type), int_type, None, ARG_NAMED_OPT) ensure = Argument(Var("ensure_ascii", bool_type), bool_type, None, ARG_NAMED_OPT) add_method( ctx, "tojson", args=[indent, ensure], return_type=str_type, self_type=TypeVarType(self_tvar_def), tvar_def=self_tvar_def, )
def make_type_info(self, name: str, module_name: Optional[str] = None, is_abstract: bool = False, mro: Optional[List[TypeInfo]] = None, bases: Optional[List[Instance]] = None, typevars: Optional[List[str]] = None, variances: Optional[List[int]] = None) -> TypeInfo: """Make a TypeInfo suitable for use in unit tests.""" class_def = ClassDef(name, Block([]), None, []) class_def.fullname = name if module_name is None: if '.' in name: module_name = name.rsplit('.', 1)[0] else: module_name = '__main__' if typevars: v: List[TypeVarLikeType] = [] for id, n in enumerate(typevars, 1): if variances: variance = variances[id - 1] else: variance = COVARIANT v.append(TypeVarType(n, n, id, [], self.o, variance=variance)) class_def.type_vars = v info = TypeInfo(SymbolTable(), class_def, module_name) if mro is None: mro = [] if name != 'builtins.object': mro.append(self.oi) info.mro = [info] + mro if bases is None: if mro: # By default, assume that there is a single non-generic base. bases = [Instance(mro[0], [])] else: bases = [] info.bases = bases return info
def add_construct_method(self, fields: List['PydanticModelField']) -> None: """ Adds a fully typed `construct` classmethod to the class. Similar to the fields-aware __init__ method, but always uses the field names (not aliases), and does not treat settings fields as optional. """ ctx = self._ctx set_str = ctx.api.named_type( f'{BUILTINS_NAME}.set', [ctx.api.named_type(f'{BUILTINS_NAME}.str')]) optional_set_str = UnionType([set_str, NoneType()]) fields_set_argument = Argument(Var('_fields_set', optional_set_str), optional_set_str, None, ARG_OPT) construct_arguments = self.get_field_arguments( fields, typed=True, force_all_optional=False, use_alias=False) construct_arguments = [fields_set_argument] + construct_arguments obj_type = ctx.api.named_type(f'{BUILTINS_NAME}.object') self_tvar_name = '_PydanticBaseModel' # Make sure it does not conflict with other names in the class tvar_fullname = ctx.cls.fullname + '.' + self_tvar_name tvd = TypeVarDef(self_tvar_name, tvar_fullname, -1, [], obj_type) self_tvar_expr = TypeVarExpr(self_tvar_name, tvar_fullname, [], obj_type) ctx.cls.info.names[self_tvar_name] = SymbolTableNode( MDEF, self_tvar_expr) # Backward-compatible with TypeVarDef from Mypy 0.910. if isinstance(tvd, TypeVarType): self_type = tvd else: self_type = TypeVarType(tvd) # type: ignore[call-arg] add_method( ctx, 'construct', construct_arguments, return_type=self_type, self_type=self_type, tvar_def=tvd, is_classmethod=True, )
def bind_new(self, name: str, tvar_expr: TypeVarLikeExpr) -> TypeVarLikeType: if self.is_class_scope: self.class_id += 1 i = self.class_id namespace = self.namespace else: self.func_id -= 1 i = self.func_id # TODO: Consider also using namespaces for functions namespace = '' if isinstance(tvar_expr, TypeVarExpr): tvar_def: TypeVarLikeType = TypeVarType( name, tvar_expr.fullname, TypeVarId(i, namespace=namespace), values=tvar_expr.values, upper_bound=tvar_expr.upper_bound, variance=tvar_expr.variance, line=tvar_expr.line, column=tvar_expr.column) elif isinstance(tvar_expr, ParamSpecExpr): tvar_def = ParamSpecType(name, tvar_expr.fullname, i, flavor=ParamSpecFlavor.BARE, upper_bound=tvar_expr.upper_bound, line=tvar_expr.line, column=tvar_expr.column) elif isinstance(tvar_expr, TypeVarTupleExpr): tvar_def = TypeVarTupleType(name, tvar_expr.fullname, i, upper_bound=tvar_expr.upper_bound, line=tvar_expr.line, column=tvar_expr.column) else: assert False self.scope[tvar_expr.fullname] = tvar_def return tvar_def
def add_supports_metadata(self) -> None: """Injects ``Supports`` metadata into instance types' mro.""" if not isinstance(self._associated_type, Instance): return for instance_type in self._instance_types: assert isinstance(instance_type, Instance) supports_spec = self._associated_type.copy_modified(args=[ TypeVarType(var_def) for var_def in instance_type.type.defn.type_vars ]) supports_spec = type_loader.load_supports_type( supports_spec, self._ctx, ) if supports_spec not in instance_type.type.bases: instance_type.type.bases.append(supports_spec) if supports_spec.type not in instance_type.type.mro: instance_type.type.mro.insert(0, supports_spec.type) self._added_types.append(supports_spec)
def transform(self) -> None: """Apply all the necessary transformations to the underlying dataclass so as to ensure it is fully type checked according to the rules in PEP 557. """ ctx = self._ctx info = self._ctx.cls.info attributes = self.collect_attributes() if attributes is None: # Some definitions are not ready, defer() should be already called. return for attr in attributes: if attr.type is None: ctx.api.defer() return decorator_arguments = { 'init': _get_decorator_bool_argument(self._ctx, 'init', True), 'eq': _get_decorator_bool_argument(self._ctx, 'eq', True), 'order': _get_decorator_bool_argument(self._ctx, 'order', False), 'frozen': _get_decorator_bool_argument(self._ctx, 'frozen', False), } if info.get('replace') is None: obj_type = ctx.api.named_type('__builtins__.object') self_tvar_expr = TypeVarExpr(SELF_UVAR_NAME, info.fullname + '.' + SELF_UVAR_NAME, [], obj_type) info.names[SELF_UVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr) replace_tvar_def = TypeVarDef(SELF_UVAR_NAME, info.fullname + '.' + SELF_UVAR_NAME, -1, [], fill_typevars(info)) replace_other_type = TypeVarType(replace_tvar_def) add_method(ctx, 'replace', args=[ Argument( Var('changes', AnyType(TypeOfAny.explicit)), AnyType(TypeOfAny.explicit), None, ARG_STAR2) ], return_type=replace_other_type, self_type=replace_other_type, tvar_def=replace_tvar_def) # If there are no attributes, it may be that the semantic analyzer has not # processed them yet. In order to work around this, we can simply skip generating # __init__ if there are no attributes, because if the user truly did not define any, # then the object default __init__ with an empty signature will be present anyway. if (decorator_arguments['init'] and ('__init__' not in info.names or info.names['__init__'].plugin_generated) and attributes): add_method( ctx, '__init__', args=[ attr.to_argument() for attr in attributes if attr.is_in_init ], return_type=NoneType(), ) if (decorator_arguments['eq'] and info.get('__eq__') is None or decorator_arguments['order']): # Type variable for self types in generated methods. obj_type = ctx.api.named_type('__builtins__.object') self_tvar_expr = TypeVarExpr(SELF_TVAR_NAME, info.fullname + '.' + SELF_TVAR_NAME, [], obj_type) info.names[SELF_TVAR_NAME] = SymbolTableNode(MDEF, self_tvar_expr) # Add <, >, <=, >=, but only if the class has an eq method. if decorator_arguments['order']: if not decorator_arguments['eq']: ctx.api.fail('eq must be True if order is True', ctx.cls) for method_name in ['__lt__', '__gt__', '__le__', '__ge__']: # Like for __eq__ and __ne__, we want "other" to match # the self type. obj_type = ctx.api.named_type('__builtins__.object') order_tvar_def = TypeVarDef( SELF_TVAR_NAME, info.fullname + '.' + SELF_TVAR_NAME, -1, [], obj_type) order_other_type = TypeVarType(order_tvar_def) order_return_type = ctx.api.named_type('__builtins__.bool') order_args = [ Argument(Var('other', order_other_type), order_other_type, None, ARG_POS) ] existing_method = info.get(method_name) if existing_method is not None and not existing_method.plugin_generated: assert existing_method.node ctx.api.fail( 'You may not have a custom %s method when order=True' % method_name, existing_method.node, ) add_method( ctx, method_name, args=order_args, return_type=order_return_type, self_type=order_other_type, tvar_def=order_tvar_def, ) if decorator_arguments['frozen']: self._freeze(attributes) self.reset_init_only_vars(info, attributes) info.metadata['dataclass'] = { 'attributes': [attr.serialize() for attr in attributes], 'frozen': decorator_arguments['frozen'], }