diff --git a/mypy/plugin.py b/mypy/plugin.py index 4d62c2bd184b..38016191de8f 100644 --- a/mypy/plugin.py +++ b/mypy/plugin.py @@ -247,7 +247,7 @@ def fail( @abstractmethod def named_generic_type(self, name: str, args: list[Type]) -> Instance: - """Construct an instance of a builtin type with given type arguments.""" + """Construct an instance of a generic type with given type arguments.""" raise NotImplementedError @abstractmethod diff --git a/test-data/unit/plugins/add_classmethod.py b/test-data/unit/plugins/add_classmethod.py index 5aacc69a8f01..9bc2c4e079dd 100644 --- a/test-data/unit/plugins/add_classmethod.py +++ b/test-data/unit/plugins/add_classmethod.py @@ -1,4 +1,6 @@ -from typing import Callable, Optional +from __future__ import annotations + +from typing import Callable from mypy.nodes import ARG_POS, Argument, Var from mypy.plugin import ClassDefContext, Plugin @@ -7,7 +9,7 @@ class ClassMethodPlugin(Plugin): - def get_base_class_hook(self, fullname: str) -> Optional[Callable[[ClassDefContext], None]]: + def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: if "BaseAddMethod" in fullname: return add_extra_methods_hook return None @@ -24,5 +26,5 @@ def add_extra_methods_hook(ctx: ClassDefContext) -> None: ) -def plugin(version): +def plugin(version: str) -> type[ClassMethodPlugin]: return ClassMethodPlugin diff --git a/test-data/unit/plugins/arg_kinds.py b/test-data/unit/plugins/arg_kinds.py index 5392e64c4f11..388a3c738b62 100644 --- a/test-data/unit/plugins/arg_kinds.py +++ b/test-data/unit/plugins/arg_kinds.py @@ -1,18 +1,19 @@ -from typing import Optional, Callable -from mypy.plugin import Plugin, MethodContext, FunctionContext +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionContext, MethodContext, Plugin from mypy.types import Type class ArgKindsPlugin(Plugin): - def get_function_hook(self, fullname: str - ) -> Optional[Callable[[FunctionContext], Type]]: - if 'func' in fullname: + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if "func" in fullname: return extract_arg_kinds_from_function return None - def get_method_hook(self, fullname: str - ) -> Optional[Callable[[MethodContext], Type]]: - if 'Class.method' in fullname: + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: + if "Class.method" in fullname: return extract_arg_kinds_from_method return None @@ -27,5 +28,5 @@ def extract_arg_kinds_from_method(ctx: MethodContext) -> Type: return ctx.default_return_type -def plugin(version): +def plugin(version: str) -> type[ArgKindsPlugin]: return ArgKindsPlugin diff --git a/test-data/unit/plugins/arg_names.py b/test-data/unit/plugins/arg_names.py index 6c1cbb9415cc..981c1a2eb12d 100644 --- a/test-data/unit/plugins/arg_names.py +++ b/test-data/unit/plugins/arg_names.py @@ -1,35 +1,51 @@ -from typing import Optional, Callable +from __future__ import annotations -from mypy.plugin import Plugin, MethodContext, FunctionContext +from typing import Callable + +from mypy.nodes import StrExpr +from mypy.plugin import FunctionContext, MethodContext, Plugin from mypy.types import Type class ArgNamesPlugin(Plugin): - def get_function_hook(self, fullname: str - ) -> Optional[Callable[[FunctionContext], Type]]: - if fullname in {'mod.func', 'mod.func_unfilled', 'mod.func_star_expr', - 'mod.ClassInit', 'mod.Outer.NestedClassInit'}: + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname in { + "mod.func", + "mod.func_unfilled", + "mod.func_star_expr", + "mod.ClassInit", + "mod.Outer.NestedClassInit", + }: return extract_classname_and_set_as_return_type_function return None - def get_method_hook(self, fullname: str - ) -> Optional[Callable[[MethodContext], Type]]: - if fullname in {'mod.Class.method', 'mod.Class.myclassmethod', 'mod.Class.mystaticmethod', - 'mod.ClassUnfilled.method', 'mod.ClassStarExpr.method', - 'mod.ClassChild.method', 'mod.ClassChild.myclassmethod'}: + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: + if fullname in { + "mod.Class.method", + "mod.Class.myclassmethod", + "mod.Class.mystaticmethod", + "mod.ClassUnfilled.method", + "mod.ClassStarExpr.method", + "mod.ClassChild.method", + "mod.ClassChild.myclassmethod", + }: return extract_classname_and_set_as_return_type_method return None def extract_classname_and_set_as_return_type_function(ctx: FunctionContext) -> Type: - classname = ctx.args[ctx.callee_arg_names.index('classname')][0].value - return ctx.api.named_generic_type(classname, []) + arg = ctx.args[ctx.callee_arg_names.index("classname")][0] + if not isinstance(arg, StrExpr): + return ctx.default_return_type + return ctx.api.named_generic_type(arg.value, []) def extract_classname_and_set_as_return_type_method(ctx: MethodContext) -> Type: - classname = ctx.args[ctx.callee_arg_names.index('classname')][0].value - return ctx.api.named_generic_type(classname, []) + arg = ctx.args[ctx.callee_arg_names.index("classname")][0] + if not isinstance(arg, StrExpr): + return ctx.default_return_type + return ctx.api.named_generic_type(arg.value, []) -def plugin(version): +def plugin(version: str) -> type[ArgNamesPlugin]: return ArgNamesPlugin diff --git a/test-data/unit/plugins/attrhook.py b/test-data/unit/plugins/attrhook.py index c177072aa47f..9500734daa6c 100644 --- a/test-data/unit/plugins/attrhook.py +++ b/test-data/unit/plugins/attrhook.py @@ -1,12 +1,14 @@ -from typing import Optional, Callable +from __future__ import annotations -from mypy.plugin import Plugin, AttributeContext -from mypy.types import Type, Instance +from typing import Callable + +from mypy.plugin import AttributeContext, Plugin +from mypy.types import Instance, Type class AttrPlugin(Plugin): - def get_attribute_hook(self, fullname: str) -> Optional[Callable[[AttributeContext], Type]]: - if fullname == 'm.Signal.__call__': + def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None: + if fullname == "m.Signal.__call__": return signal_call_callback return None @@ -17,5 +19,5 @@ def signal_call_callback(ctx: AttributeContext) -> Type: return ctx.default_attr_type -def plugin(version): +def plugin(version: str) -> type[AttrPlugin]: return AttrPlugin diff --git a/test-data/unit/plugins/attrhook2.py b/test-data/unit/plugins/attrhook2.py index cc14341a6f97..2d41a0fdf52f 100644 --- a/test-data/unit/plugins/attrhook2.py +++ b/test-data/unit/plugins/attrhook2.py @@ -1,14 +1,16 @@ -from typing import Optional, Callable +from __future__ import annotations -from mypy.plugin import Plugin, AttributeContext -from mypy.types import Type, AnyType, TypeOfAny +from typing import Callable + +from mypy.plugin import AttributeContext, Plugin +from mypy.types import AnyType, Type, TypeOfAny class AttrPlugin(Plugin): - def get_attribute_hook(self, fullname: str) -> Optional[Callable[[AttributeContext], Type]]: - if fullname == 'm.Magic.magic_field': + def get_attribute_hook(self, fullname: str) -> Callable[[AttributeContext], Type] | None: + if fullname == "m.Magic.magic_field": return magic_field_callback - if fullname == 'm.Magic.nonexistent_field': + if fullname == "m.Magic.nonexistent_field": return nonexistent_field_callback return None @@ -22,5 +24,5 @@ def nonexistent_field_callback(ctx: AttributeContext) -> Type: return AnyType(TypeOfAny.from_error) -def plugin(version): +def plugin(version: str) -> type[AttrPlugin]: return AttrPlugin diff --git a/test-data/unit/plugins/badreturn.py b/test-data/unit/plugins/badreturn.py index fd7430606dd6..9dce3b3e99c2 100644 --- a/test-data/unit/plugins/badreturn.py +++ b/test-data/unit/plugins/badreturn.py @@ -1,2 +1,2 @@ -def plugin(version): +def plugin(version: str) -> None: pass diff --git a/test-data/unit/plugins/badreturn2.py b/test-data/unit/plugins/badreturn2.py index c7e0447841c1..1ae551ecbf20 100644 --- a/test-data/unit/plugins/badreturn2.py +++ b/test-data/unit/plugins/badreturn2.py @@ -1,5 +1,9 @@ +from __future__ import annotations + + class MyPlugin: pass -def plugin(version): + +def plugin(version: str) -> type[MyPlugin]: return MyPlugin diff --git a/test-data/unit/plugins/callable_instance.py b/test-data/unit/plugins/callable_instance.py index 40e7df418539..a9f562effb34 100644 --- a/test-data/unit/plugins/callable_instance.py +++ b/test-data/unit/plugins/callable_instance.py @@ -1,23 +1,30 @@ +from __future__ import annotations + +from typing import Callable + from mypy.plugin import MethodContext, Plugin from mypy.types import Instance, Type + class CallableInstancePlugin(Plugin): - def get_function_hook(self, fullname): - assert not fullname.endswith(' of Foo') + def get_function_hook(self, fullname: str) -> None: + assert not fullname.endswith(" of Foo") - def get_method_hook(self, fullname): + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: # Ensure that all names are fully qualified - assert not fullname.endswith(' of Foo') + assert not fullname.endswith(" of Foo") - if fullname == '__main__.Class.__call__': + if fullname == "__main__.Class.__call__": return my_hook return None + def my_hook(ctx: MethodContext) -> Type: if isinstance(ctx.type, Instance) and len(ctx.type.args) == 1: return ctx.type.args[0] return ctx.default_return_type -def plugin(version): + +def plugin(version: str) -> type[CallableInstancePlugin]: return CallableInstancePlugin diff --git a/test-data/unit/plugins/class_attr_hook.py b/test-data/unit/plugins/class_attr_hook.py index 348e5df0ee03..5d6a87df48bb 100644 --- a/test-data/unit/plugins/class_attr_hook.py +++ b/test-data/unit/plugins/class_attr_hook.py @@ -1,20 +1,23 @@ -from typing import Callable, Optional +from __future__ import annotations + +from typing import Callable from mypy.plugin import AttributeContext, Plugin from mypy.types import Type as MypyType class ClassAttrPlugin(Plugin): - def get_class_attribute_hook(self, fullname: str - ) -> Optional[Callable[[AttributeContext], MypyType]]: - if fullname == '__main__.Cls.attr': + def get_class_attribute_hook( + self, fullname: str + ) -> Callable[[AttributeContext], MypyType] | None: + if fullname == "__main__.Cls.attr": return my_hook return None def my_hook(ctx: AttributeContext) -> MypyType: - return ctx.api.named_generic_type('builtins.int', []) + return ctx.api.named_generic_type("builtins.int", []) -def plugin(_version: str): +def plugin(_version: str) -> type[ClassAttrPlugin]: return ClassAttrPlugin diff --git a/test-data/unit/plugins/class_callable.py b/test-data/unit/plugins/class_callable.py index 07f75ec80ac1..9fab30e60458 100644 --- a/test-data/unit/plugins/class_callable.py +++ b/test-data/unit/plugins/class_callable.py @@ -1,32 +1,43 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + from mypy.nodes import NameExpr -from mypy.types import UnionType, NoneType, Instance +from mypy.plugin import FunctionContext, Plugin +from mypy.types import Instance, NoneType, Type, UnionType, get_proper_type + class AttrPlugin(Plugin): - def get_function_hook(self, fullname): - if fullname.startswith('mod.Attr'): + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname.startswith("mod.Attr"): return attr_hook return None -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 + +def attr_hook(ctx: FunctionContext) -> Type: + default = get_proper_type(ctx.default_return_type) + assert isinstance(default, Instance) + if default.type.fullname == "mod.Attr": + attr_base = default else: attr_base = None - for base in ctx.default_return_type.type.bases: - if base.type.fullname == 'mod.Attr': + for base in default.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): + 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) + return Instance( + attr_base.type, + [UnionType([arg_type, NoneType()])], + line=default.line, + column=default.column, + ) + -def plugin(version): +def plugin(version: str) -> type[AttrPlugin]: return AttrPlugin diff --git a/test-data/unit/plugins/common_api_incremental.py b/test-data/unit/plugins/common_api_incremental.py index 2dcd559777ec..b14b2f92073e 100644 --- a/test-data/unit/plugins/common_api_incremental.py +++ b/test-data/unit/plugins/common_api_incremental.py @@ -1,44 +1,48 @@ -from mypy.plugin import Plugin -from mypy.nodes import ( - ClassDef, Block, TypeInfo, SymbolTable, SymbolTableNode, MDEF, GDEF, Var -) +from __future__ import annotations + +from typing import Callable + +from mypy.nodes import GDEF, MDEF, Block, ClassDef, SymbolTable, SymbolTableNode, TypeInfo, Var +from mypy.plugin import ClassDefContext, DynamicClassDefContext, Plugin class DynPlugin(Plugin): - def get_dynamic_class_hook(self, fullname): - if fullname == 'lib.declarative_base': + def get_dynamic_class_hook( + self, fullname: str + ) -> Callable[[DynamicClassDefContext], None] | None: + if fullname == "lib.declarative_base": return add_info_hook return None - def get_base_class_hook(self, fullname: str): + def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: sym = self.lookup_fully_qualified(fullname) if sym and isinstance(sym.node, TypeInfo): - if sym.node.metadata.get('magic'): + if sym.node.metadata.get("magic"): return add_magic_hook return None -def add_info_hook(ctx) -> None: +def add_info_hook(ctx: DynamicClassDefContext) -> None: class_def = ClassDef(ctx.name, Block([])) class_def.fullname = ctx.api.qualified_name(ctx.name) info = TypeInfo(SymbolTable(), class_def, ctx.api.cur_mod_id) class_def.info = info - obj = ctx.api.named_type('builtins.object') + obj = ctx.api.named_type("builtins.object", []) info.mro = [info, obj.type] info.bases = [obj] ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info)) - info.metadata['magic'] = True + info.metadata["magic"] = {"value": True} -def add_magic_hook(ctx) -> None: +def add_magic_hook(ctx: ClassDefContext) -> None: info = ctx.cls.info - str_type = ctx.api.named_type_or_none('builtins.str', []) + str_type = ctx.api.named_type_or_none("builtins.str", []) assert str_type is not None - var = Var('__magic__', str_type) + var = Var("__magic__", str_type) var.info = info - info.names['__magic__'] = SymbolTableNode(MDEF, var) + info.names["__magic__"] = SymbolTableNode(MDEF, var) -def plugin(version): +def plugin(version: str) -> type[DynPlugin]: return DynPlugin diff --git a/test-data/unit/plugins/config_data.py b/test-data/unit/plugins/config_data.py index 059e036d5e32..9b828bc9ac0a 100644 --- a/test-data/unit/plugins/config_data.py +++ b/test-data/unit/plugins/config_data.py @@ -1,6 +1,7 @@ -import os -import json +from __future__ import annotations +import json +import os from typing import Any from mypy.plugin import Plugin, ReportConfigContext @@ -8,11 +9,11 @@ class ConfigDataPlugin(Plugin): def report_config_data(self, ctx: ReportConfigContext) -> Any: - path = os.path.join('tmp/test.json') + path = os.path.join("tmp/test.json") with open(path) as f: data = json.load(f) return data.get(ctx.id) -def plugin(version): +def plugin(version: str) -> type[ConfigDataPlugin]: return ConfigDataPlugin diff --git a/test-data/unit/plugins/custom_errorcode.py b/test-data/unit/plugins/custom_errorcode.py index 0e2209a32eca..0af87658e59f 100644 --- a/test-data/unit/plugins/custom_errorcode.py +++ b/test-data/unit/plugins/custom_errorcode.py @@ -1,20 +1,24 @@ +from __future__ import annotations + +from typing import Callable + from mypy.errorcodes import ErrorCode -from mypy.plugin import Plugin -from mypy.types import AnyType, TypeOfAny +from mypy.plugin import FunctionContext, Plugin +from mypy.types import AnyType, Type, TypeOfAny CUSTOM_ERROR = ErrorCode(code="custom", description="", category="Custom") class CustomErrorCodePlugin(Plugin): - def get_function_hook(self, fullname): + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: if fullname.endswith(".main"): return self.emit_error return None - def emit_error(self, ctx): + def emit_error(self, ctx: FunctionContext) -> Type: ctx.api.fail("Custom error", ctx.context, code=CUSTOM_ERROR) return AnyType(TypeOfAny.from_error) -def plugin(version): +def plugin(version: str) -> type[CustomErrorCodePlugin]: return CustomErrorCodePlugin diff --git a/test-data/unit/plugins/customentry.py b/test-data/unit/plugins/customentry.py index b3dacfd4cf44..1a7ed3348e12 100644 --- a/test-data/unit/plugins/customentry.py +++ b/test-data/unit/plugins/customentry.py @@ -1,14 +1,22 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionContext, Plugin +from mypy.types import Type + class MyPlugin(Plugin): - def get_function_hook(self, fullname): - if fullname == '__main__.f': + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname == "__main__.f": return my_hook assert fullname return None -def my_hook(ctx): - return ctx.api.named_generic_type('builtins.int', []) -def register(version): +def my_hook(ctx: FunctionContext) -> Type: + return ctx.api.named_generic_type("builtins.int", []) + + +def register(version: str) -> type[MyPlugin]: return MyPlugin diff --git a/test-data/unit/plugins/customize_mro.py b/test-data/unit/plugins/customize_mro.py index 0f2396d98965..3b13b2e9d998 100644 --- a/test-data/unit/plugins/customize_mro.py +++ b/test-data/unit/plugins/customize_mro.py @@ -1,10 +1,17 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import ClassDefContext, Plugin + class DummyPlugin(Plugin): - def get_customize_class_mro_hook(self, fullname): - def analyze(classdef_ctx): + def get_customize_class_mro_hook(self, fullname: str) -> Callable[[ClassDefContext], None]: + def analyze(classdef_ctx: ClassDefContext) -> None: pass + return analyze -def plugin(version): + +def plugin(version: str) -> type[DummyPlugin]: return DummyPlugin diff --git a/test-data/unit/plugins/decimal_to_int.py b/test-data/unit/plugins/decimal_to_int.py index 94aa33ef6df1..2318b2367d33 100644 --- a/test-data/unit/plugins/decimal_to_int.py +++ b/test-data/unit/plugins/decimal_to_int.py @@ -1,14 +1,21 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import AnalyzeTypeContext, Plugin +from mypy.types import Type class MyPlugin(Plugin): - def get_type_analyze_hook(self, fullname): + def get_type_analyze_hook(self, fullname: str) -> Callable[[AnalyzeTypeContext], Type] | None: if fullname in ("decimal.Decimal", "_decimal.Decimal"): return decimal_to_int_hook return None -def plugin(version): - return MyPlugin -def decimal_to_int_hook(ctx): - return ctx.api.named_type('builtins.int', []) +def decimal_to_int_hook(ctx: AnalyzeTypeContext) -> Type: + return ctx.api.named_type("builtins.int", []) + + +def plugin(version: str) -> type[MyPlugin]: + return MyPlugin diff --git a/test-data/unit/plugins/depshook.py b/test-data/unit/plugins/depshook.py index 76277f3cb82b..bb2460de1196 100644 --- a/test-data/unit/plugins/depshook.py +++ b/test-data/unit/plugins/depshook.py @@ -1,15 +1,15 @@ -from typing import List, Tuple +from __future__ import annotations -from mypy.plugin import Plugin from mypy.nodes import MypyFile +from mypy.plugin import Plugin class DepsPlugin(Plugin): - def get_additional_deps(self, file: MypyFile) -> List[Tuple[int, str, int]]: - if file.fullname == '__main__': - return [(10, 'err', -1)] + def get_additional_deps(self, file: MypyFile) -> list[tuple[int, str, int]]: + if file.fullname == "__main__": + return [(10, "err", -1)] return [] -def plugin(version): +def plugin(version: str) -> type[DepsPlugin]: return DepsPlugin diff --git a/test-data/unit/plugins/descriptor.py b/test-data/unit/plugins/descriptor.py index afbadcdfb671..d38853367906 100644 --- a/test-data/unit/plugins/descriptor.py +++ b/test-data/unit/plugins/descriptor.py @@ -1,28 +1,38 @@ -from mypy.plugin import Plugin -from mypy.types import NoneType, CallableType +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import MethodContext, MethodSigContext, Plugin +from mypy.types import CallableType, NoneType, Type, get_proper_type class DescriptorPlugin(Plugin): - def get_method_hook(self, fullname): + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: if fullname == "__main__.Desc.__get__": return get_hook return None - def get_method_signature_hook(self, fullname): + def get_method_signature_hook( + self, fullname: str + ) -> Callable[[MethodSigContext], CallableType] | None: if fullname == "__main__.Desc.__set__": return set_hook return None -def get_hook(ctx): - if isinstance(ctx.arg_types[0][0], NoneType): - return ctx.api.named_type("builtins.str") - return ctx.api.named_type("builtins.int") +def get_hook(ctx: MethodContext) -> Type: + arg = get_proper_type(ctx.arg_types[0][0]) + if isinstance(arg, NoneType): + return ctx.api.named_generic_type("builtins.str", []) + return ctx.api.named_generic_type("builtins.int", []) -def set_hook(ctx): +def set_hook(ctx: MethodSigContext) -> CallableType: return CallableType( - [ctx.api.named_type("__main__.Cls"), ctx.api.named_type("builtins.int")], + [ + ctx.api.named_generic_type("__main__.Cls", []), + ctx.api.named_generic_type("builtins.int", []), + ], ctx.default_signature.arg_kinds, ctx.default_signature.arg_names, ctx.default_signature.ret_type, @@ -30,5 +40,5 @@ def set_hook(ctx): ) -def plugin(version): +def plugin(version: str) -> type[DescriptorPlugin]: return DescriptorPlugin diff --git a/test-data/unit/plugins/dyn_class.py b/test-data/unit/plugins/dyn_class.py index 54bf377aa8ef..18e948e3dd2a 100644 --- a/test-data/unit/plugins/dyn_class.py +++ b/test-data/unit/plugins/dyn_class.py @@ -1,47 +1,57 @@ -from mypy.plugin import Plugin -from mypy.nodes import ( - ClassDef, Block, TypeInfo, SymbolTable, SymbolTableNode, GDEF, Var -) -from mypy.types import Instance +from __future__ import annotations + +from typing import Callable + +from mypy.nodes import GDEF, Block, ClassDef, SymbolTable, SymbolTableNode, TypeInfo, Var +from mypy.plugin import ClassDefContext, DynamicClassDefContext, Plugin +from mypy.types import Instance, get_proper_type DECL_BASES = set() + class DynPlugin(Plugin): - def get_dynamic_class_hook(self, fullname): - if fullname == 'mod.declarative_base': + def get_dynamic_class_hook( + self, fullname: str + ) -> Callable[[DynamicClassDefContext], None] | None: + if fullname == "mod.declarative_base": return add_info_hook return None - def get_base_class_hook(self, fullname: str): + def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None: if fullname in DECL_BASES: return replace_col_hook return None -def add_info_hook(ctx): + +def add_info_hook(ctx: DynamicClassDefContext) -> None: class_def = ClassDef(ctx.name, Block([])) class_def.fullname = ctx.api.qualified_name(ctx.name) info = TypeInfo(SymbolTable(), class_def, ctx.api.cur_mod_id) class_def.info = info - obj = ctx.api.named_type('builtins.object') + obj = ctx.api.named_type("builtins.object") info.mro = [info, obj.type] info.bases = [obj] ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info)) DECL_BASES.add(class_def.fullname) -def replace_col_hook(ctx): + +def replace_col_hook(ctx: ClassDefContext) -> None: info = ctx.cls.info for sym in info.names.values(): node = sym.node - if isinstance(node, Var) and isinstance(node.type, Instance): - if node.type.type.fullname == 'mod.Column': - new_sym = ctx.api.lookup_fully_qualified_or_none('mod.Instr') + if isinstance(node, Var) and isinstance( + (node_type := get_proper_type(node.type)), Instance + ): + if node_type.type.fullname == "mod.Column": + new_sym = ctx.api.lookup_fully_qualified_or_none("mod.Instr") if new_sym: new_info = new_sym.node assert isinstance(new_info, TypeInfo) - node.type = Instance(new_info, node.type.args, - node.type.line, - node.type.column) + node.type = Instance( + new_info, node_type.args, node_type.line, node_type.column + ) + -def plugin(version): +def plugin(version: str) -> type[DynPlugin]: return DynPlugin diff --git a/test-data/unit/plugins/dyn_class_from_method.py b/test-data/unit/plugins/dyn_class_from_method.py index 4c3904907750..b84754654084 100644 --- a/test-data/unit/plugins/dyn_class_from_method.py +++ b/test-data/unit/plugins/dyn_class_from_method.py @@ -1,28 +1,38 @@ -from mypy.nodes import (Block, ClassDef, GDEF, SymbolTable, SymbolTableNode, TypeInfo) +from __future__ import annotations + +from typing import Callable + +from mypy.nodes import GDEF, Block, ClassDef, RefExpr, SymbolTable, SymbolTableNode, TypeInfo from mypy.plugin import DynamicClassDefContext, Plugin from mypy.types import Instance class DynPlugin(Plugin): - def get_dynamic_class_hook(self, fullname): - if 'from_queryset' in fullname: + def get_dynamic_class_hook( + self, fullname: str + ) -> Callable[[DynamicClassDefContext], None] | None: + if "from_queryset" in fullname: return add_info_hook return None -def add_info_hook(ctx: DynamicClassDefContext): +def add_info_hook(ctx: DynamicClassDefContext) -> None: class_def = ClassDef(ctx.name, Block([])) class_def.fullname = ctx.api.qualified_name(ctx.name) info = TypeInfo(SymbolTable(), class_def, ctx.api.cur_mod_id) class_def.info = info + assert isinstance(ctx.call.args[0], RefExpr) queryset_type_fullname = ctx.call.args[0].fullname - queryset_info = ctx.api.lookup_fully_qualified_or_none(queryset_type_fullname).node # type: TypeInfo - obj = ctx.api.named_type('builtins.object') + queryset_node = ctx.api.lookup_fully_qualified_or_none(queryset_type_fullname) + assert queryset_node is not None + queryset_info = queryset_node.node + assert isinstance(queryset_info, TypeInfo) + obj = ctx.api.named_type("builtins.object") info.mro = [info, queryset_info, obj.type] info.bases = [Instance(queryset_info, [])] ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(GDEF, info)) -def plugin(version): +def plugin(version: str) -> type[DynPlugin]: return DynPlugin diff --git a/test-data/unit/plugins/fnplugin.py b/test-data/unit/plugins/fnplugin.py index 684d6343458e..a5a7e57101c2 100644 --- a/test-data/unit/plugins/fnplugin.py +++ b/test-data/unit/plugins/fnplugin.py @@ -1,14 +1,22 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionContext, Plugin +from mypy.types import Type + class MyPlugin(Plugin): - def get_function_hook(self, fullname): - if fullname == '__main__.f': + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname == "__main__.f": return my_hook assert fullname is not None return None -def my_hook(ctx): - return ctx.api.named_generic_type('builtins.int', []) -def plugin(version): +def my_hook(ctx: FunctionContext) -> Type: + return ctx.api.named_generic_type("builtins.int", []) + + +def plugin(version: str) -> type[MyPlugin]: return MyPlugin diff --git a/test-data/unit/plugins/fully_qualified_test_hook.py b/test-data/unit/plugins/fully_qualified_test_hook.py index 529cf25a1215..9230091bba1a 100644 --- a/test-data/unit/plugins/fully_qualified_test_hook.py +++ b/test-data/unit/plugins/fully_qualified_test_hook.py @@ -1,16 +1,28 @@ -from mypy.plugin import CallableType, MethodSigContext, Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import MethodSigContext, Plugin +from mypy.types import CallableType + class FullyQualifiedTestPlugin(Plugin): - def get_method_signature_hook(self, fullname): + def get_method_signature_hook( + self, fullname: str + ) -> Callable[[MethodSigContext], CallableType] | None: # Ensure that all names are fully qualified - if 'FullyQualifiedTest' in fullname: - assert fullname.startswith('__main__.') and not ' of ' in fullname, fullname + if "FullyQualifiedTest" in fullname: + assert fullname.startswith("__main__.") and " of " not in fullname, fullname return my_hook return None + def my_hook(ctx: MethodSigContext) -> CallableType: - return ctx.default_signature.copy_modified(ret_type=ctx.api.named_generic_type('builtins.int', [])) + return ctx.default_signature.copy_modified( + ret_type=ctx.api.named_generic_type("builtins.int", []) + ) + -def plugin(version): +def plugin(version: str) -> type[FullyQualifiedTestPlugin]: return FullyQualifiedTestPlugin diff --git a/test-data/unit/plugins/function_sig_hook.py b/test-data/unit/plugins/function_sig_hook.py index 4d901b96716e..a8d3cf058062 100644 --- a/test-data/unit/plugins/function_sig_hook.py +++ b/test-data/unit/plugins/function_sig_hook.py @@ -1,9 +1,16 @@ -from mypy.plugin import CallableType, FunctionSigContext, Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionSigContext, Plugin +from mypy.types import CallableType class FunctionSigPlugin(Plugin): - def get_function_signature_hook(self, fullname): - if fullname == '__main__.dynamic_signature': + def get_function_signature_hook( + self, fullname: str + ) -> Callable[[FunctionSigContext], CallableType] | None: + if fullname == "__main__.dynamic_signature": return my_hook return None @@ -13,11 +20,8 @@ def my_hook(ctx: FunctionSigContext) -> CallableType: if len(arg1_args) != 1: return ctx.default_signature arg1_type = ctx.api.get_expression_type(arg1_args[0]) - return ctx.default_signature.copy_modified( - arg_types=[arg1_type], - ret_type=arg1_type, - ) + return ctx.default_signature.copy_modified(arg_types=[arg1_type], ret_type=arg1_type) -def plugin(version): +def plugin(version: str) -> type[FunctionSigPlugin]: return FunctionSigPlugin diff --git a/test-data/unit/plugins/method_in_decorator.py b/test-data/unit/plugins/method_in_decorator.py index 99774dfcc7ef..3fba7692266c 100644 --- a/test-data/unit/plugins/method_in_decorator.py +++ b/test-data/unit/plugins/method_in_decorator.py @@ -1,19 +1,25 @@ -from mypy.types import CallableType, Type -from typing import Callable, Optional +from __future__ import annotations + +from typing import Callable + from mypy.plugin import MethodContext, Plugin +from mypy.types import CallableType, Type, get_proper_type class MethodDecoratorPlugin(Plugin): - def get_method_hook(self, fullname: str) -> Optional[Callable[[MethodContext], Type]]: - if 'Foo.a' in fullname: + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: + if "Foo.a" in fullname: return method_decorator_callback return None + def method_decorator_callback(ctx: MethodContext) -> Type: - if isinstance(ctx.default_return_type, CallableType): - str_type = ctx.api.named_generic_type('builtins.str', []) - return ctx.default_return_type.copy_modified(ret_type=str_type) + default = get_proper_type(ctx.default_return_type) + if isinstance(default, CallableType): + str_type = ctx.api.named_generic_type("builtins.str", []) + return default.copy_modified(ret_type=str_type) return ctx.default_return_type -def plugin(version): + +def plugin(version: str) -> type[MethodDecoratorPlugin]: return MethodDecoratorPlugin diff --git a/test-data/unit/plugins/method_sig_hook.py b/test-data/unit/plugins/method_sig_hook.py index 25c2842e6620..b78831cc45d5 100644 --- a/test-data/unit/plugins/method_sig_hook.py +++ b/test-data/unit/plugins/method_sig_hook.py @@ -1,30 +1,41 @@ -from mypy.plugin import CallableType, CheckerPluginInterface, MethodSigContext, Plugin -from mypy.types import Instance, Type +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import CheckerPluginInterface, MethodSigContext, Plugin +from mypy.types import CallableType, Instance, Type, get_proper_type + class MethodSigPlugin(Plugin): - def get_method_signature_hook(self, fullname): + def get_method_signature_hook( + self, fullname: str + ) -> Callable[[MethodSigContext], CallableType] | None: # Ensure that all names are fully qualified - assert not fullname.endswith(' of Foo') + assert not fullname.endswith(" of Foo") - if fullname.startswith('__main__.Foo.'): + if fullname.startswith("__main__.Foo."): return my_hook return None + def _str_to_int(api: CheckerPluginInterface, typ: Type) -> Type: + typ = get_proper_type(typ) if isinstance(typ, Instance): - if typ.type.fullname == 'builtins.str': - return api.named_generic_type('builtins.int', []) + if typ.type.fullname == "builtins.str": + return api.named_generic_type("builtins.int", []) elif typ.args: return typ.copy_modified(args=[_str_to_int(api, t) for t in typ.args]) return typ + def my_hook(ctx: MethodSigContext) -> CallableType: return ctx.default_signature.copy_modified( arg_types=[_str_to_int(ctx.api, t) for t in ctx.default_signature.arg_types], ret_type=_str_to_int(ctx.api, ctx.default_signature.ret_type), ) -def plugin(version): + +def plugin(version: str) -> type[MethodSigPlugin]: return MethodSigPlugin diff --git a/test-data/unit/plugins/named_callable.py b/test-data/unit/plugins/named_callable.py index e40d181d2bad..c37e11c32125 100644 --- a/test-data/unit/plugins/named_callable.py +++ b/test-data/unit/plugins/named_callable.py @@ -1,28 +1,33 @@ -from mypy.plugin import Plugin -from mypy.types import CallableType +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionContext, Plugin +from mypy.types import CallableType, Type, get_proper_type class MyPlugin(Plugin): - def get_function_hook(self, fullname): - if fullname == 'm.decorator1': + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname == "m.decorator1": return decorator_call_hook - if fullname == 'm._decorated': # This is a dummy name generated by the plugin + if fullname == "m._decorated": # This is a dummy name generated by the plugin return decorate_hook return None -def decorator_call_hook(ctx): - if isinstance(ctx.default_return_type, CallableType): - return ctx.default_return_type.copy_modified(name='m._decorated') +def decorator_call_hook(ctx: FunctionContext) -> Type: + default = get_proper_type(ctx.default_return_type) + if isinstance(default, CallableType): + return default.copy_modified(name="m._decorated") return ctx.default_return_type -def decorate_hook(ctx): - if isinstance(ctx.default_return_type, CallableType): - return ctx.default_return_type.copy_modified( - ret_type=ctx.api.named_generic_type('builtins.str', [])) +def decorate_hook(ctx: FunctionContext) -> Type: + default = get_proper_type(ctx.default_return_type) + if isinstance(default, CallableType): + return default.copy_modified(ret_type=ctx.api.named_generic_type("builtins.str", [])) return ctx.default_return_type -def plugin(version): +def plugin(version: str) -> type[MyPlugin]: return MyPlugin diff --git a/test-data/unit/plugins/plugin2.py b/test-data/unit/plugins/plugin2.py index b530a62d23aa..e486d96ea8bf 100644 --- a/test-data/unit/plugins/plugin2.py +++ b/test-data/unit/plugins/plugin2.py @@ -1,13 +1,21 @@ -from mypy.plugin import Plugin +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import FunctionContext, Plugin +from mypy.types import Type + class Plugin2(Plugin): - def get_function_hook(self, fullname): - if fullname in ('__main__.f', '__main__.g'): + def get_function_hook(self, fullname: str) -> Callable[[FunctionContext], Type] | None: + if fullname in ("__main__.f", "__main__.g"): return str_hook return None -def str_hook(ctx): - return ctx.api.named_generic_type('builtins.str', []) -def plugin(version): +def str_hook(ctx: FunctionContext) -> Type: + return ctx.api.named_generic_type("builtins.str", []) + + +def plugin(version: str) -> type[Plugin2]: return Plugin2 diff --git a/test-data/unit/plugins/type_anal_hook.py b/test-data/unit/plugins/type_anal_hook.py index 86d18d8c8611..c380bbe873fe 100644 --- a/test-data/unit/plugins/type_anal_hook.py +++ b/test-data/unit/plugins/type_anal_hook.py @@ -1,22 +1,23 @@ -from typing import Optional, Callable +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import AnalyzeTypeContext, Plugin -from mypy.plugin import Plugin, AnalyzeTypeContext -from mypy.types import Type, TypeList, AnyType, CallableType, TypeOfAny # The official name changed to NoneType but we have an alias for plugin compat reasons # so we'll keep testing that here. -from mypy.types import NoneTyp +from mypy.types import AnyType, CallableType, NoneTyp, Type, TypeList, TypeOfAny + class TypeAnalyzePlugin(Plugin): - def get_type_analyze_hook(self, fullname: str - ) -> Optional[Callable[[AnalyzeTypeContext], Type]]: - if fullname == 'm.Signal': + def get_type_analyze_hook(self, fullname: str) -> Callable[[AnalyzeTypeContext], Type] | None: + if fullname == "m.Signal": return signal_type_analyze_callback return None def signal_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type: - if (len(ctx.type.args) != 1 - or not isinstance(ctx.type.args[0], TypeList)): + if len(ctx.type.args) != 1 or not isinstance(ctx.type.args[0], TypeList): ctx.api.fail('Invalid "Signal" type (expected "Signal[[t, ...]]")', ctx.context) return AnyType(TypeOfAny.from_error) @@ -27,13 +28,11 @@ def signal_type_analyze_callback(ctx: AnalyzeTypeContext) -> Type: return AnyType(TypeOfAny.from_error) # Error generated elsewhere arg_types, arg_kinds, arg_names = analyzed arg_types = [ctx.api.analyze_type(arg) for arg in arg_types] - type_arg = CallableType(arg_types, - arg_kinds, - arg_names, - NoneTyp(), - ctx.api.named_type('builtins.function', [])) - return ctx.api.named_type('m.Signal', [type_arg]) + type_arg = CallableType( + arg_types, arg_kinds, arg_names, NoneTyp(), ctx.api.named_type("builtins.function", []) + ) + return ctx.api.named_type("m.Signal", [type_arg]) -def plugin(version): +def plugin(version: str) -> type[TypeAnalyzePlugin]: return TypeAnalyzePlugin diff --git a/test-data/unit/plugins/union_method.py b/test-data/unit/plugins/union_method.py index a7621553f6ad..7c62ffb8c0cc 100644 --- a/test-data/unit/plugins/union_method.py +++ b/test-data/unit/plugins/union_method.py @@ -1,34 +1,40 @@ -from mypy.plugin import ( - CallableType, CheckerPluginInterface, MethodSigContext, MethodContext, Plugin -) -from mypy.types import Instance, Type +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import CheckerPluginInterface, MethodContext, MethodSigContext, Plugin +from mypy.types import CallableType, Instance, Type, get_proper_type class MethodPlugin(Plugin): - def get_method_signature_hook(self, fullname): - if fullname.startswith('__main__.Foo.'): + def get_method_signature_hook( + self, fullname: str + ) -> Callable[[MethodSigContext], CallableType] | None: + if fullname.startswith("__main__.Foo."): return my_meth_sig_hook return None - def get_method_hook(self, fullname): - if fullname.startswith('__main__.Bar.'): + def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None: + if fullname.startswith("__main__.Bar."): return my_meth_hook return None def _str_to_int(api: CheckerPluginInterface, typ: Type) -> Type: + typ = get_proper_type(typ) if isinstance(typ, Instance): - if typ.type.fullname == 'builtins.str': - return api.named_generic_type('builtins.int', []) + if typ.type.fullname == "builtins.str": + return api.named_generic_type("builtins.int", []) elif typ.args: return typ.copy_modified(args=[_str_to_int(api, t) for t in typ.args]) return typ def _float_to_int(api: CheckerPluginInterface, typ: Type) -> Type: + typ = get_proper_type(typ) if isinstance(typ, Instance): - if typ.type.fullname == 'builtins.float': - return api.named_generic_type('builtins.int', []) + if typ.type.fullname == "builtins.float": + return api.named_generic_type("builtins.int", []) elif typ.args: return typ.copy_modified(args=[_float_to_int(api, t) for t in typ.args]) return typ @@ -45,5 +51,5 @@ def my_meth_hook(ctx: MethodContext) -> Type: return _float_to_int(ctx.api, ctx.default_return_type) -def plugin(version): +def plugin(version: str) -> type[MethodPlugin]: return MethodPlugin diff --git a/tox.ini b/tox.ini index a809c4d2c570..e07acdc5200d 100644 --- a/tox.ini +++ b/tox.ini @@ -55,3 +55,4 @@ passenv = commands = python runtests.py self python -m mypy --config-file mypy_self_check.ini misc --exclude misc/sync-typeshed.py + python -m mypy --config-file mypy_self_check.ini test-data/unit/plugins