def cached_function_method_signature(ctx: MethodSigContext) -> CallableType: """Fixes the `_CachedFunction.__call__` signature to be correct. It already has *almost* the correct signature, except: 1. the `self` argument needs to be marked as "bound"; 2. any `cache_context` argument should be removed; 3. an optional keyword argument `on_invalidated` should be added. """ # First we mark this as a bound function signature. signature = bind_self(ctx.default_signature) # Secondly, we remove any "cache_context" args. # # Note: We should be only doing this if `cache_context=True` is set, but if # it isn't then the code will raise an exception when its called anyway, so # its not the end of the world. context_arg_index = None for idx, name in enumerate(signature.arg_names): if name == "cache_context": context_arg_index = idx break arg_types = list(signature.arg_types) arg_names = list(signature.arg_names) arg_kinds = list(signature.arg_kinds) if context_arg_index: arg_types.pop(context_arg_index) arg_names.pop(context_arg_index) arg_kinds.pop(context_arg_index) # Third, we add an optional "on_invalidate" argument. # # This is a callable which accepts no input and returns nothing. calltyp = CallableType( arg_types=[], arg_kinds=[], arg_names=[], ret_type=NoneType(), fallback=ctx.api.named_generic_type("builtins.function", []), ) arg_types.append(calltyp) arg_names.append("on_invalidate") arg_kinds.append(ARG_NAMED_OPT) # Arg is an optional kwarg. signature = signature.copy_modified( arg_types=arg_types, arg_names=arg_names, arg_kinds=arg_kinds, ) return signature
def test_none(self) -> None: self.assert_meet(NoneType(), NoneType(), NoneType()) self.assert_meet(NoneType(), self.fx.anyt, NoneType()) # Any type t joined with None results in None, unless t is Any. for t in [self.fx.a, self.fx.o, UnboundType('x'), self.fx.t, self.tuple(), self.callable(self.fx.a, self.fx.b)]: self.assert_meet(t, NoneType(), NoneType())
def build_newtype_typeinfo(self, name: str, old_type: Type, base_type: Instance) -> TypeInfo: info = self.api.basic_new_typeinfo(name, base_type) info.is_newtype = True # Add __init__ method args = [Argument(Var('self'), NoneType(), None, ARG_POS), self.make_argument('item', old_type)] signature = CallableType( arg_types=[Instance(info, []), old_type], arg_kinds=[arg.kind for arg in args], arg_names=['self', 'item'], ret_type=NoneType(), fallback=self.api.named_type('__builtins__.function'), name=name) init_func = FuncDef('__init__', args, Block([]), typ=signature) init_func.info = info init_func._fullname = info.fullname + '.__init__' info.names['__init__'] = SymbolTableNode(MDEF, init_func) return info
def trivial_meet(s: Type, t: Type) -> ProperType: """Return one of types (expanded) if it is a subtype of other, otherwise bottom type.""" if is_subtype(s, t): return get_proper_type(s) elif is_subtype(t, s): return get_proper_type(t) else: if state.strict_optional: return UninhabitedType() else: return NoneType()
def dynamic_init_hook(ctx: ClassDefContext) -> None: if "__init__" in ctx.cls.info.names: return # transform from mypy.plugins.common import add_method_to_class from mypy.types import NoneType from mypy.nodes import ( AssignmentStmt, NameExpr, PlaceholderNode, Var, Argument, ARG_NAMED, ) # from: mypy.plugins.dataclasses args = [] cls = ctx.cls for stmt in cls.defs.body: # Any assignment that doesn't use the new type declaration # syntax can be ignored out of hand. if not (isinstance(stmt, AssignmentStmt) and stmt.new_syntax): continue # a: int, b: str = 1, 'foo' is not supported syntax so we # don't have to worry about it. lhs = stmt.lvalues[0] if not isinstance(lhs, NameExpr): continue sym = cls.info.names.get(lhs.name) if sym is None: # This name is likely blocked by a star import. We don't need to defer because # defer() is already called by mark_incomplete(). continue node = sym.node if isinstance(node, PlaceholderNode): # This node is not ready yet. return None assert isinstance(node, Var) # x: ClassVar[int] is ignored by dataclasses. if node.is_classvar: continue args.append(Argument(node, node.type, None, ARG_NAMED)) add_method_to_class(ctx.api, cls, "__init__", args=args, return_type=NoneType())
def test_create_mismatch(self) -> None: own_symbol = Var("x", NoneType()) other_symbol = Var("x", None) data = {"name_a": "package.x", "name_b": "package.x"} result = ComparisonResult.create_mismatch(own_symbol, other_symbol, data=data) assert result.match_result is MatchResult.MISMATCH assert result.symbol is own_symbol assert result.reference is other_symbol assert result.message is not None assert result.data is data
def narrow_declared_type(declared: Type, narrowed: Type) -> Type: """Return the declared type narrowed down to another type.""" # TODO: check infinite recursion for aliases here. if isinstance(narrowed, TypeGuardedType): # type: ignore[misc] # A type guard forces the new type even if it doesn't overlap the old. return narrowed.type_guard declared = get_proper_type(declared) narrowed = get_proper_type(narrowed) if declared == narrowed: return declared if isinstance(declared, UnionType): return make_simplified_union([ narrow_declared_type(x, narrowed) for x in declared.relevant_items() ]) if is_enum_overlapping_union(declared, narrowed): return narrowed elif not is_overlapping_types( declared, narrowed, prohibit_none_typevar_overlap=True): if state.strict_optional: return UninhabitedType() else: return NoneType() elif isinstance(narrowed, UnionType): return make_simplified_union([ narrow_declared_type(declared, x) for x in narrowed.relevant_items() ]) elif isinstance(narrowed, AnyType): return narrowed elif isinstance(narrowed, TypeVarType) and is_subtype( narrowed.upper_bound, declared): return narrowed elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType): return TypeType.make_normalized( narrow_declared_type(declared.item, narrowed.item)) elif (isinstance(declared, TypeType) and isinstance(narrowed, Instance) and narrowed.type.is_metaclass()): # We'd need intersection types, so give up. return declared elif isinstance(declared, (Instance, TupleType, TypeType, LiteralType)): return meet_types(declared, narrowed) elif isinstance(declared, TypedDictType) and isinstance( narrowed, Instance): # Special case useful for selecting TypedDicts from unions using isinstance(x, dict). if (narrowed.type.fullname == 'builtins.dict' and all( isinstance(t, AnyType) for t in get_proper_types(narrowed.args))): return declared return meet_types(declared, narrowed) return narrowed
def visit_instance(self, t: Instance) -> ProperType: if isinstance(self.s, Instance): si = self.s if t.type == si.type: if is_subtype(t, self.s) or is_subtype(self.s, t): # Combine type arguments. We could have used join below # equivalently. args = [] # type: List[Type] for i in range(len(t.args)): args.append(self.meet(t.args[i], si.args[i])) return Instance(t.type, args) else: if state.strict_optional: return UninhabitedType() else: return NoneType() else: if is_subtype(t, self.s): return t elif is_subtype(self.s, t): # See also above comment. return self.s else: if state.strict_optional: return UninhabitedType() else: return NoneType() elif isinstance(self.s, FunctionLike) and t.type.is_protocol: call = unpack_callback_protocol(t) if call: return meet_types(call, self.s) elif isinstance(self.s, TypeType): return meet_types(t, self.s) elif isinstance(self.s, TupleType): return meet_types(t, self.s) elif isinstance(self.s, LiteralType): return meet_types(t, self.s) elif isinstance(self.s, TypedDictType): return meet_types(t, self.s) return self.default(self.s)
def transform_oas_type( oas_type: oas_model.OASType[Any], handler_type: ProperType, ctx: FunctionContext, ) -> Type: m_type: Optional[Type] = None union_members: List[Type] = [] assert isinstance(ctx.api, TypeChecker) if isinstance(oas_type, oas_model.OASStringType): m_type = ctx.api.str_type() elif isinstance(oas_type, oas_model.OASBooleanType): m_type = ctx.api.named_type('bool') elif isinstance(oas_type, oas_model.OASNumberType): m_type = ctx.api.named_type( 'int' if issubclass(int, oas_type.number_cls) else 'float', ) elif isinstance(oas_type, oas_model.OASOneOfType): union_members = [ transform_oas_type( nested_type, handler_type, ctx, ) for include_type, nested_type in oas_type.schemas if include_type ] elif isinstance(oas_type, oas_model.OASArrayType): m_type = ctx.api.named_generic_type( name='set' if oas_type.unique_items else 'list', args=[ transform_oas_type( oas_type.items_type, handler_type, ctx, ) ], ) elif isinstance(oas_type, oas_model.OASObjectType): m_type = transform_oas_object_type(oas_type, handler_type, ctx) else: raise NotImplementedError(f'{oas_type} not yet implemented') if m_type is not None: m_type.set_line(handler_type) union_members.append(m_type) if oas_type.nullable: union_members.append(NoneType()) ut = simplify_union(UnionType.make_union(items=union_members)) ut.set_line(handler_type) return ut
def make_fake_register_class_instance(api: CheckerPluginInterface, type_args: Sequence[Type] ) -> Instance: defn = ClassDef(REGISTER_RETURN_CLASS, Block([])) defn.fullname = f'functools.{REGISTER_RETURN_CLASS}' info = TypeInfo(SymbolTable(), defn, "functools") obj_type = api.named_generic_type('builtins.object', []).type info.bases = [Instance(obj_type, [])] info.mro = [info, obj_type] defn.info = info func_arg = Argument(Var('name'), AnyType(TypeOfAny.implementation_artifact), None, ARG_POS) add_method_to_class(api, defn, '__call__', [func_arg], NoneType()) return Instance(info, type_args)
def visit_singleton_pattern(self, o: SingletonPattern) -> PatternType: current_type = self.type_context[-1] value: Union[bool, None] = o.value if isinstance(value, bool): typ = self.chk.expr_checker.infer_literal_expr_type( value, "builtins.bool") elif value is None: typ = NoneType() else: assert False narrowed_type, rest_type = self.chk.conditional_types_with_intersection( current_type, [get_type_range(typ)], o, default=current_type) return PatternType(narrowed_type, rest_type, {})
def get_suggestion(self, mod: str, node: FuncDef) -> PyAnnotateSignature: """Compute a suggestion for a function. Return the type and whether the first argument should be ignored. """ graph = self.graph callsites, orig_errors = self.get_callsites(node) uses = get_arg_uses(self.manager.all_types, node) if self.no_errors and orig_errors: raise SuggestionFailure("Function does not typecheck.") is_method = bool(node.info) and not node.is_static if len(node.arg_names) >= 10: raise SuggestionFailure("Too many arguments") with strict_optional_set(graph[mod].options.strict_optional): guesses = self.get_guesses( is_method, self.get_starting_type(node), self.get_default_arg_types(graph[mod], node), callsites, uses, ) guesses = self.filter_options(guesses, is_method) if len(guesses) > self.max_guesses: raise SuggestionFailure("Too many possibilities!") best, _ = self.find_best(node, guesses) # Now try to find the return type! self.try_type(node, best) returns = get_return_types(self.manager.all_types, node) with strict_optional_set(graph[mod].options.strict_optional): if returns: ret_types = generate_type_combinations(returns) else: ret_types = [NoneType()] guesses = [ best.copy_modified(ret_type=refine_type(best.ret_type, t)) for t in ret_types ] guesses = self.filter_options(guesses, is_method) best, errors = self.find_best(node, guesses) if self.no_errors and errors: raise SuggestionFailure("No annotation without errors") return self.pyannotate_signature(mod, is_method, best)
def strawberry_pydantic_class_callback(ctx: ClassDefContext) -> None: # in future we want to have a proper pydantic plugin, but for now # let's fallback to **kwargs for __init__, some resources are here: # https://github.com/samuelcolvin/pydantic/blob/master/pydantic/mypy.py # >>> model_index = ctx.cls.decorators[0].arg_names.index("model") # >>> model_name = ctx.cls.decorators[0].args[model_index].name # >>> model_type = ctx.api.named_type("UserModel") # >>> model_type = ctx.api.lookup(model_name, Context()) model_expression = _get_argument(call=ctx.reason, name="model") # type: ignore if model_expression is None: ctx.api.fail("model argument in decorator failed to be parsed", ctx.reason) else: # Add __init__ init_args = [ Argument(Var("kwargs"), AnyType(TypeOfAny.explicit), None, ARG_STAR2) ] add_method(ctx, "__init__", init_args, NoneType()) model_type = _get_type_for_expr(model_expression, ctx.api) # Add to_pydantic add_method( ctx, "to_pydantic", args=[], return_type=model_type, ) # Add from_pydantic model_argument = Argument( variable=Var(name="instance", type=model_type), type_annotation=model_type, initializer=None, kind=ARG_OPT, ) add_static_method_to_class( ctx.api, ctx.cls, name="from_pydantic", args=[model_argument], return_type=fill_typevars(ctx.cls.info), )
def _unbound_to_instance( api: SemanticAnalyzerPluginInterface, typ: Type ) -> Type: """Take the UnboundType that we seem to get as the ret_type from a FuncDef and convert it into an Instance/TypeInfo kind of structure that seems to work as the left-hand type of an AssignmentStatement. """ if not isinstance(typ, UnboundType): return typ # TODO: figure out a more robust way to check this. The node is some # kind of _SpecialForm, there's a typing.Optional that's _SpecialForm, # but I cant figure out how to get them to match up if typ.name == "Optional": # convert from "Optional?" to the more familiar # UnionType[..., NoneType()] return _unbound_to_instance( api, UnionType( [_unbound_to_instance(api, typ_arg) for typ_arg in typ.args] + [NoneType()] ), ) node = api.lookup_qualified(typ.name, typ) if ( node is not None and isinstance(node, SymbolTableNode) and isinstance(node.node, TypeInfo) ): bound_type = node.node return Instance( bound_type, [ _unbound_to_instance(api, arg) if isinstance(arg, UnboundType) else arg for arg in typ.args ], ) else: return typ
def get_suggestion(self, function: str) -> str: """Compute a suggestion for a function. Return the type and whether the first argument should be ignored. """ graph = self.graph mod, _, node = self.find_node(function) callsites, orig_errors = self.get_callsites(node) if self.no_errors and orig_errors: raise SuggestionFailure("Function does not typecheck.") # FIXME: what about static and class methods? is_method = bool(node.info) if len(node.arg_names) >= 10: raise SuggestionFailure("Too many arguments") with strict_optional_set(graph[mod].options.strict_optional): guesses = self.get_guesses( is_method, self.get_trivial_type(node), self.get_default_arg_types(graph[mod], node), callsites) guesses = self.filter_options(guesses) if len(guesses) > self.max_guesses: raise SuggestionFailure("Too many possibilities!") best, _ = self.find_best(node, guesses) # Now try to find the return type! self.try_type(node, best) returns = get_return_types(self.manager.all_types, node) with strict_optional_set(graph[mod].options.strict_optional): if returns: ret_types = generate_type_combinations(returns) else: ret_types = [NoneType()] guesses = [best.copy_modified(ret_type=t) for t in ret_types] guesses = self.filter_options(guesses) best, errors = self.find_best(node, guesses) if self.no_errors and errors: raise SuggestionFailure("No annotation without errors") return self.format_callable(mod, is_method, best)
def _change_decorator_function_type( decorated: CallableType, decorator: CallableType, provided_arguments: List[str], ) -> CallableType: decorator.arg_kinds = decorated.arg_kinds decorator.arg_names = decorated.arg_names # Mark provided arguments as optional decorator.arg_types = copy.copy(decorated.arg_types) for argument in provided_arguments: index = decorated.arg_names.index(argument) decorated_type = decorated.arg_types[index] decorator.arg_types[index] = UnionType.make_union( [decorated_type, NoneType()]) decorated.arg_kinds[index] = ARG_NAMED_OPT return decorator
def _add_init(ctx: 'mypy.plugin.ClassDefContext', attributes: List[Attribute], adder: 'MethodAdder') -> None: """Generate an __init__ method for the attributes and add it to the class.""" args = [attribute.argument(ctx) for attribute in attributes if attribute.init] if all( # We use getattr rather than instance checks because the variable.type # might be wrapped into a Union or some other type, but even non-Any # types reliably track the fact that the argument was not annotated. getattr(arg.variable.type, "type_of_any", None) == TypeOfAny.unannotated for arg in args ): # This workaround makes --disallow-incomplete-defs usable with attrs, # but is definitely suboptimal as a long-term solution. # See https://github.com/python/mypy/issues/5954 for discussion. for a in args: a.variable.type = AnyType(TypeOfAny.implementation_artifact) a.type_annotation = AnyType(TypeOfAny.implementation_artifact) adder.add_method('__init__', args, NoneType())
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=[TypeVarDef('X', 'X', -1, [], self.fx.o)]) assert_equal(str(c), 'def [X] (X?, Y?) -> Y?') v = [ TypeVarDef('Y', 'Y', -1, [], self.fx.o), TypeVarDef('X', 'X', -2, [], self.fx.o) ] c2 = CallableType([], [], [], NoneType(), self.function, name=None, variables=v) assert_equal(str(c2), 'def [Y, X] ()')
def add_initializer(self, fields: List['PydanticModelField'], config: 'ModelConfigData', is_settings: bool) -> None: """ Adds a fields-aware `__init__` method to the class. The added `__init__` will be annotated with types vs. all `Any` depending on the plugin settings. """ ctx = self._ctx typed = self.plugin_config.init_typed use_alias = config.allow_population_by_field_name is not True force_all_optional = is_settings or bool( config.has_alias_generator and not config.allow_population_by_field_name ) init_arguments = self.get_field_arguments( fields, typed=typed, force_all_optional=force_all_optional, use_alias=use_alias ) if not self.should_init_forbid_extra(fields, config): var = Var('kwargs') init_arguments.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2)) add_method(ctx, '__init__', init_arguments, NoneType())
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 transform_parameter_to_type( param: oas.OASParameter, handler_arg_type: ProperType, handler_arg_default_value: HandlerArgDefaultValue, ctx: FunctionContext, ) -> Type: oas_is_required = param.required handler_has_default = (handler_arg_default_value is not _ARG_NO_DEFAULT_VALUE_MARKER) if not oas_is_required and not handler_has_default: needs_optional = True elif isinstance(handler_arg_default_value, type(None)): needs_optional = True else: needs_optional = False if isinstance(param.schema, tuple): oas_type, _ = param.schema items = [transform_oas_type( oas_type, handler_arg_type, ctx, )] else: items = [ transform_oas_type( v.schema, handler_arg_type, ctx, ) for v in param.schema.values() ] if needs_optional: items.append(NoneType()) return simplify_union( UnionType.make_union( items=items, line=(handler_arg_type.line or handler_arg_type.end_line) or -1, column=handler_arg_type.column, ), )
def attr_hook(ctx): assert isinstance(ctx.default_return_type, Instance) if ctx.default_return_type.type.fullname() == 'mod.Attr': attr_base = ctx.default_return_type else: attr_base = None for base in ctx.default_return_type.type.bases: if base.type.fullname() == 'mod.Attr': attr_base = base break assert attr_base is not None last_arg_exprs = ctx.args[-1] if any( isinstance(expr, NameExpr) and expr.name == 'True' for expr in last_arg_exprs): return attr_base assert len(attr_base.args) == 1 arg_type = attr_base.args[0] return Instance(attr_base.type, [UnionType([arg_type, NoneType()])], line=ctx.default_return_type.line, column=ctx.default_return_type.column)
def narrow_declared_type(declared: Type, narrowed: Type) -> Type: """Return the declared type narrowed down to another type.""" # TODO: check infinite recursion for aliases here. declared = get_proper_type(declared) narrowed = get_proper_type(narrowed) if declared == narrowed: return declared if isinstance(declared, UnionType): return make_simplified_union([ narrow_declared_type(x, narrowed) for x in declared.relevant_items() ]) elif not is_overlapping_types( declared, narrowed, prohibit_none_typevar_overlap=True): if state.strict_optional: return UninhabitedType() else: return NoneType() elif isinstance(narrowed, UnionType): return make_simplified_union([ narrow_declared_type(declared, x) for x in narrowed.relevant_items() ]) elif isinstance(narrowed, AnyType): return narrowed elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType): return TypeType.make_normalized( narrow_declared_type(declared.item, narrowed.item)) elif isinstance(declared, (Instance, TupleType, TypeType, LiteralType)): return meet_types(declared, narrowed) elif isinstance(declared, TypedDictType) and isinstance( narrowed, Instance): # Special case useful for selecting TypedDicts from unions using isinstance(x, dict). if (narrowed.type.fullname == 'builtins.dict' and all( isinstance(t, AnyType) for t in get_proper_types(narrowed.args))): return declared return meet_types(declared, narrowed) return narrowed
def narrow_declared_type(declared: Type, narrowed: Type) -> Type: """Return the declared type narrowed down to another type.""" if declared == narrowed: return declared if isinstance(declared, UnionType): return UnionType.make_simplified_union([narrow_declared_type(x, narrowed) for x in declared.relevant_items()]) elif not is_overlapping_types(declared, narrowed, prohibit_none_typevar_overlap=True): if state.strict_optional: return UninhabitedType() else: return NoneType() elif isinstance(narrowed, UnionType): return UnionType.make_simplified_union([narrow_declared_type(declared, x) for x in narrowed.relevant_items()]) elif isinstance(narrowed, AnyType): return narrowed elif isinstance(declared, TypeType) and isinstance(narrowed, TypeType): return TypeType.make_normalized(narrow_declared_type(declared.item, narrowed.item)) elif isinstance(declared, (Instance, TupleType, TypeType, LiteralType)): return meet_types(declared, narrowed) return narrowed
def layout_class_callback(ctx: ClassDefContext) -> None: path_type = ctx.api.builtin_type("pathlib.Path") config_type = ctx.api.named_type_or_none( "tts.config.Config") # type: ignore # Change the types of class members when type checking the body to `pathlib.Path`. # `_unpacked_layout` expects the values to be paths. for stmt in ctx.cls.defs.body: assert isinstance(stmt, AssignmentStmt) assert len(stmt.lvalues) == 1 lvalue = stmt.lvalues[0] assert isinstance(lvalue, NameExpr) lvalue.node = _make_name_lvalue_var(lvalue, path_type, ctx) add_method( ctx, "__init__", [ Argument(Var("path"), path_type, None, ARG_POS), Argument(Var("config"), config_type, None, ARG_POS), ], NoneType(), )
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'], }
def __init__(self, variance: int = COVARIANT) -> None: # The 'object' class self.oi = self.make_type_info('builtins.object') # class object self.o = Instance(self.oi, []) # object # Type variables (these are effectively global) def make_type_var(name: str, id: int, values: List[Type], upper_bound: Type, variance: int) -> TypeVarType: return TypeVarType(name, name, id, values, upper_bound, variance) self.t = make_type_var('T', 1, [], self.o, variance) # T`1 (type variable) self.tf = make_type_var('T', -1, [], self.o, variance) # T`-1 (type variable) self.tf2 = make_type_var('T', -2, [], self.o, variance) # T`-2 (type variable) self.s = make_type_var('S', 2, [], self.o, variance) # S`2 (type variable) self.s1 = make_type_var('S', 1, [], self.o, variance) # S`1 (type variable) self.sf = make_type_var('S', -2, [], self.o, variance) # S`-2 (type variable) self.sf1 = make_type_var('S', -1, [], self.o, variance) # S`-1 (type variable) # Simple types self.anyt = AnyType(TypeOfAny.special_form) self.nonet = NoneType() self.uninhabited = UninhabitedType() # Abstract class TypeInfos # class F self.fi = self.make_type_info('F', is_abstract=True) # class F2 self.f2i = self.make_type_info('F2', is_abstract=True) # class F3(F) self.f3i = self.make_type_info('F3', is_abstract=True, mro=[self.fi]) # Class TypeInfos self.std_tuplei = self.make_type_info('builtins.tuple', mro=[self.oi], typevars=['T'], variances=[COVARIANT ]) # class tuple self.type_typei = self.make_type_info('builtins.type') # class type self.bool_type_info = self.make_type_info('builtins.bool') self.functioni = self.make_type_info( 'builtins.function') # function TODO self.ai = self.make_type_info('A', mro=[self.oi]) # class A self.bi = self.make_type_info('B', mro=[self.ai, self.oi]) # class B(A) self.ci = self.make_type_info('C', mro=[self.ai, self.oi]) # class C(A) self.di = self.make_type_info('D', mro=[self.oi]) # class D # class E(F) self.ei = self.make_type_info('E', mro=[self.fi, self.oi]) # class E2(F2, F) self.e2i = self.make_type_info('E2', mro=[self.f2i, self.fi, self.oi]) # class E3(F, F2) self.e3i = self.make_type_info('E3', mro=[self.fi, self.f2i, self.oi]) # Generic class TypeInfos # G[T] self.gi = self.make_type_info('G', mro=[self.oi], typevars=['T'], variances=[variance]) # G2[T] self.g2i = self.make_type_info('G2', mro=[self.oi], typevars=['T'], variances=[variance]) # H[S, T] self.hi = self.make_type_info('H', mro=[self.oi], typevars=['S', 'T'], variances=[variance, variance]) # GS[T, S] <: G[S] self.gsi = self.make_type_info('GS', mro=[self.gi, self.oi], typevars=['T', 'S'], variances=[variance, variance], bases=[Instance(self.gi, [self.s])]) # GS2[S] <: G[S] self.gs2i = self.make_type_info('GS2', mro=[self.gi, self.oi], typevars=['S'], variances=[variance], bases=[Instance(self.gi, [self.s1])]) # list[T] self.std_listi = self.make_type_info('builtins.list', mro=[self.oi], typevars=['T'], variances=[variance]) # Instance types self.std_tuple = Instance(self.std_tuplei, [self.anyt]) # tuple self.type_type = Instance(self.type_typei, []) # type self.function = Instance(self.functioni, []) # function TODO self.a = Instance(self.ai, []) # A self.b = Instance(self.bi, []) # B self.c = Instance(self.ci, []) # C self.d = Instance(self.di, []) # D self.e = Instance(self.ei, []) # E self.e2 = Instance(self.e2i, []) # E2 self.e3 = Instance(self.e3i, []) # E3 self.f = Instance(self.fi, []) # F self.f2 = Instance(self.f2i, []) # F2 self.f3 = Instance(self.f3i, []) # F3 # Generic instance types self.ga = Instance(self.gi, [self.a]) # G[A] self.gb = Instance(self.gi, [self.b]) # G[B] self.gd = Instance(self.gi, [self.d]) # G[D] self.go = Instance(self.gi, [self.o]) # G[object] self.gt = Instance(self.gi, [self.t]) # G[T`1] self.gtf = Instance(self.gi, [self.tf]) # G[T`-1] self.gtf2 = Instance(self.gi, [self.tf2]) # G[T`-2] self.gs = Instance(self.gi, [self.s]) # G[S] self.gdyn = Instance(self.gi, [self.anyt]) # G[Any] self.gn = Instance(self.gi, [NoneType()]) # G[None] self.g2a = Instance(self.g2i, [self.a]) # G2[A] self.gsaa = Instance(self.gsi, [self.a, self.a]) # GS[A, A] self.gsab = Instance(self.gsi, [self.a, self.b]) # GS[A, B] self.gsba = Instance(self.gsi, [self.b, self.a]) # GS[B, A] self.gs2a = Instance(self.gs2i, [self.a]) # GS2[A] self.gs2b = Instance(self.gs2i, [self.b]) # GS2[B] self.gs2d = Instance(self.gs2i, [self.d]) # GS2[D] self.hab = Instance(self.hi, [self.a, self.b]) # H[A, B] self.haa = Instance(self.hi, [self.a, self.a]) # H[A, A] self.hbb = Instance(self.hi, [self.b, self.b]) # H[B, B] self.hts = Instance(self.hi, [self.t, self.s]) # H[T, S] self.had = Instance(self.hi, [self.a, self.d]) # H[A, D] self.hao = Instance(self.hi, [self.a, self.o]) # H[A, object] self.lsta = Instance(self.std_listi, [self.a]) # List[A] self.lstb = Instance(self.std_listi, [self.b]) # List[B] self.lit1 = LiteralType(1, self.a) self.lit2 = LiteralType(2, self.a) self.lit3 = LiteralType("foo", self.d) self.lit4 = LiteralType(4, self.a) self.lit1_inst = Instance(self.ai, [], last_known_value=self.lit1) self.lit2_inst = Instance(self.ai, [], last_known_value=self.lit2) self.lit3_inst = Instance(self.di, [], last_known_value=self.lit3) self.lit4_inst = Instance(self.ai, [], last_known_value=self.lit4) self.type_a = TypeType.make_normalized(self.a) self.type_b = TypeType.make_normalized(self.b) self.type_c = TypeType.make_normalized(self.c) self.type_d = TypeType.make_normalized(self.d) self.type_t = TypeType.make_normalized(self.t) self.type_any = TypeType.make_normalized(self.anyt) self._add_bool_dunder(self.bool_type_info) self._add_bool_dunder(self.ai)
def analyze_descriptor_access(instance_type: Type, descriptor_type: Type, builtin_type: Callable[[str], Instance], msg: MessageBuilder, context: Context, *, chk: 'mypy.checker.TypeChecker') -> Type: """Type check descriptor access. Arguments: instance_type: The type of the instance on which the descriptor attribute is being accessed (the type of ``a`` in ``a.f`` when ``f`` is a descriptor). descriptor_type: The type of the descriptor attribute being accessed (the type of ``f`` in ``a.f`` when ``f`` is a descriptor). context: The node defining the context of this inference. Return: The return type of the appropriate ``__get__`` overload for the descriptor. """ instance_type = get_proper_type(instance_type) descriptor_type = get_proper_type(descriptor_type) if isinstance(descriptor_type, UnionType): # Map the access over union types return make_simplified_union([ analyze_descriptor_access(instance_type, typ, builtin_type, msg, context, chk=chk) for typ in descriptor_type.items ]) elif not isinstance(descriptor_type, Instance): return descriptor_type if not descriptor_type.type.has_readable_member('__get__'): return descriptor_type dunder_get = descriptor_type.type.get_method('__get__') if dunder_get is None: msg.fail( message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format( descriptor_type), context) return AnyType(TypeOfAny.from_error) function = function_type(dunder_get, builtin_type('builtins.function')) bound_method = bind_self(function, descriptor_type) typ = map_instance_to_supertype(descriptor_type, dunder_get.info) dunder_get_type = expand_type_by_instance(bound_method, typ) if isinstance(instance_type, FunctionLike) and instance_type.is_type_obj(): owner_type = instance_type.items()[0].ret_type instance_type = NoneType() elif isinstance(instance_type, TypeType): owner_type = instance_type.item instance_type = NoneType() else: owner_type = instance_type _, inferred_dunder_get_type = chk.expr_checker.check_call( dunder_get_type, [ TempNode(instance_type, context=context), TempNode(TypeType.make_normalized(owner_type), context=context) ], [ARG_POS, ARG_POS], context) inferred_dunder_get_type = get_proper_type(inferred_dunder_get_type) if isinstance(inferred_dunder_get_type, AnyType): # check_call failed, and will have reported an error return inferred_dunder_get_type if not isinstance(inferred_dunder_get_type, CallableType): msg.fail( message_registry.DESCRIPTOR_GET_NOT_CALLABLE.format( descriptor_type), context) return AnyType(TypeOfAny.from_error) return inferred_dunder_get_type.ret_type
def argument(self, ctx: 'mypy.plugin.ClassDefContext') -> Argument: """Return this attribute as an argument to __init__.""" assert self.init init_type = self.init_type or self.info[self.name].type if self.converter.name: # When a converter is set the init_type is overridden by the first argument # of the converter method. converter = lookup_qualified_stnode(ctx.api.modules, self.converter.name, True) if not converter: # The converter may be a local variable. Check there too. converter = ctx.api.lookup_qualified(self.converter.name, self.info, True) # Get the type of the converter. converter_type = None # type: Optional[Type] if converter and isinstance(converter.node, TypeInfo): from mypy.checkmember import type_object_type # To avoid import cycle. converter_type = type_object_type(converter.node, ctx.api.builtin_type) elif converter and isinstance(converter.node, OverloadedFuncDef): converter_type = converter.node.type elif converter and converter.type: converter_type = converter.type init_type = None converter_type = get_proper_type(converter_type) if isinstance(converter_type, CallableType) and converter_type.arg_types: init_type = ctx.api.anal_type(converter_type.arg_types[0]) elif isinstance(converter_type, Overloaded): types = [] # type: List[Type] for item in converter_type.items(): # Walk the overloads looking for methods that can accept one argument. num_arg_types = len(item.arg_types) if not num_arg_types: continue if num_arg_types > 1 and any(kind == ARG_POS for kind in item.arg_kinds[1:]): continue types.append(item.arg_types[0]) # Make a union of all the valid types. if types: args = make_simplified_union(types) init_type = ctx.api.anal_type(args) if self.converter.is_attr_converters_optional and init_type: # If the converter was attr.converter.optional(type) then add None to # the allowed init_type. init_type = UnionType.make_union([init_type, NoneType()]) if not init_type: ctx.api.fail("Cannot determine __init__ type from converter", self.context) init_type = AnyType(TypeOfAny.from_error) elif self.converter.name == '': # This means we had a converter but it's not of a type we can infer. # Error was shown in _get_converter_name init_type = AnyType(TypeOfAny.from_error) if init_type is None: if ctx.api.options.disallow_untyped_defs: # This is a compromise. If you don't have a type here then the # __init__ will be untyped. But since the __init__ is added it's # pointing at the decorator. So instead we also show the error in the # assignment, which is where you would fix the issue. node = self.info[self.name].node assert node is not None ctx.api.msg.need_annotation_for_var(node, self.context) # Convert type not set to Any. init_type = AnyType(TypeOfAny.unannotated) if self.kw_only: arg_kind = ARG_NAMED_OPT if self.has_default else ARG_NAMED else: arg_kind = ARG_OPT if self.has_default else ARG_POS # Attrs removes leading underscores when creating the __init__ arguments. return Argument(Var(self.name.lstrip("_"), init_type), init_type, None, arg_kind)
def assign_type(self, expr: Expression, type: Type, declared_type: Optional[Type], restrict_any: bool = False) -> None: # We should erase last known value in binder, because if we are using it, # it means that the target is not final, and therefore can't hold a literal. type = remove_instance_last_known_values(type) type = get_proper_type(type) declared_type = get_proper_type(declared_type) if self.type_assignments is not None: # We are in a multiassign from union, defer the actual binding, # just collect the types. self.type_assignments[expr].append((type, declared_type)) return if not isinstance(expr, (IndexExpr, MemberExpr, NameExpr)): return None if not literal(expr): return self.invalidate_dependencies(expr) if declared_type is None: # Not sure why this happens. It seems to mainly happen in # member initialization. return if not is_subtype(type, declared_type): # Pretty sure this is only happens when there's a type error. # Ideally this function wouldn't be called if the # expression has a type error, though -- do other kinds of # errors cause this function to get called at invalid # times? return enclosing_type = get_proper_type(self.most_recent_enclosing_type(expr, type)) if isinstance(enclosing_type, AnyType) and not restrict_any: # If x is Any and y is int, after x = y we do not infer that x is int. # This could be changed. # Instead, since we narrowed type from Any in a recent frame (probably an # isinstance check), but now it is reassigned, we broaden back # to Any (which is the most recent enclosing type) self.put(expr, enclosing_type) # As a special case, when assigning Any to a variable with a # declared Optional type that has been narrowed to None, # replace all the Nones in the declared Union type with Any. # This overrides the normal behavior of ignoring Any assignments to variables # in order to prevent false positives. # (See discussion in #3526) elif (isinstance(type, AnyType) and isinstance(declared_type, UnionType) and any(isinstance(item, NoneType) for item in declared_type.items) and isinstance(get_proper_type(self.most_recent_enclosing_type(expr, NoneType())), NoneType)): # Replace any Nones in the union type with Any new_items = [type if isinstance(item, NoneType) else item for item in declared_type.items] self.put(expr, UnionType(new_items)) elif (isinstance(type, AnyType) and not (isinstance(declared_type, UnionType) and any(isinstance(item, AnyType) for item in declared_type.items))): # Assigning an Any value doesn't affect the type to avoid false negatives, unless # there is an Any item in a declared union type. self.put(expr, declared_type) else: self.put(expr, type) for i in self.try_frames: # XXX This should probably not copy the entire frame, but # just copy this variable into a single stored frame. self.allow_jump(i)