diff --git a/mypy/checker.py b/mypy/checker.py
index c1c31538b7de..87bd9915af97 100644
--- a/mypy/checker.py
+++ b/mypy/checker.py
@@ -6793,6 +6793,9 @@ def has_valid_attribute(self, typ: Type, name: str) -> bool:
)
return not watcher.has_new_errors()
+ def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type:
+ return self.expr_checker.accept(node, type_context=type_context)
+
class CollectArgTypeVarTypes(TypeTraverserVisitor):
"""Collects the non-nested argument types in a set."""
diff --git a/mypy/plugin.py b/mypy/plugin.py
index cf124b45d04f..4d62c2bd184b 100644
--- a/mypy/plugin.py
+++ b/mypy/plugin.py
@@ -250,6 +250,11 @@ def named_generic_type(self, name: str, args: list[Type]) -> Instance:
"""Construct an instance of a builtin type with given type arguments."""
raise NotImplementedError
+ @abstractmethod
+ def get_expression_type(self, node: Expression, type_context: Type | None = None) -> Type:
+ """Checks the type of the given expression."""
+ raise NotImplementedError
+
@trait
class SemanticAnalyzerPluginInterface:
diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py
index afd9423d6820..e05a8e44b2f8 100644
--- a/mypy/plugins/attrs.py
+++ b/mypy/plugins/attrs.py
@@ -9,7 +9,6 @@
import mypy.plugin # To avoid circular imports.
from mypy.applytype import apply_generic_arguments
-from mypy.checker import TypeChecker
from mypy.errorcodes import LITERAL_REQ
from mypy.expandtype import expand_type, expand_type_by_instance
from mypy.exprtotype import TypeTranslationError, expr_to_unanalyzed_type
@@ -1048,13 +1047,7 @@ def evolve_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl
return ctx.default_signature # leave it to the type checker to complain
inst_arg = ctx.args[0][0]
-
- #
- assert isinstance(ctx.api, TypeChecker)
- inst_type = ctx.api.expr_checker.accept(inst_arg)
- #
-
- inst_type = get_proper_type(inst_type)
+ inst_type = get_proper_type(ctx.api.get_expression_type(inst_arg))
inst_type_str = format_type_bare(inst_type, ctx.api.options)
attr_types = _get_expanded_attr_types(ctx, inst_type, inst_type, inst_type)
@@ -1074,14 +1067,10 @@ def evolve_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl
def fields_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> CallableType:
"""Provide the signature for `attrs.fields`."""
- if not ctx.args or len(ctx.args) != 1 or not ctx.args[0] or not ctx.args[0][0]:
+ if len(ctx.args) != 1 or len(ctx.args[0]) != 1:
return ctx.default_signature
- #
- assert isinstance(ctx.api, TypeChecker)
- inst_type = ctx.api.expr_checker.accept(ctx.args[0][0])
- #
- proper_type = get_proper_type(inst_type)
+ proper_type = get_proper_type(ctx.api.get_expression_type(ctx.args[0][0]))
# fields(Any) -> Any, fields(type[Any]) -> Any
if (
@@ -1098,7 +1087,7 @@ def fields_function_sig_callback(ctx: mypy.plugin.FunctionSigContext) -> Callabl
inner = get_proper_type(proper_type.upper_bound)
if isinstance(inner, Instance):
# We need to work arg_types to compensate for the attrs stubs.
- arg_types = [inst_type]
+ arg_types = [proper_type]
cls = inner.type
elif isinstance(proper_type, CallableType):
cls = proper_type.type_object()
diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test
index c81de675d808..ec5bce219dbd 100644
--- a/test-data/unit/check-custom-plugin.test
+++ b/test-data/unit/check-custom-plugin.test
@@ -887,7 +887,10 @@ plugins=/test-data/unit/plugins/descriptor.py
# flags: --config-file tmp/mypy.ini
def dynamic_signature(arg1: str) -> str: ...
-reveal_type(dynamic_signature(1)) # N: Revealed type is "builtins.int"
+a: int = 1
+reveal_type(dynamic_signature(a)) # N: Revealed type is "builtins.int"
+b: bytes = b'foo'
+reveal_type(dynamic_signature(b)) # N: Revealed type is "builtins.bytes"
[file mypy.ini]
\[mypy]
plugins=/test-data/unit/plugins/function_sig_hook.py
diff --git a/test-data/unit/plugins/function_sig_hook.py b/test-data/unit/plugins/function_sig_hook.py
index d83c7df26209..4d901b96716e 100644
--- a/test-data/unit/plugins/function_sig_hook.py
+++ b/test-data/unit/plugins/function_sig_hook.py
@@ -1,5 +1,5 @@
-from mypy.plugin import CallableType, CheckerPluginInterface, FunctionSigContext, Plugin
-from mypy.types import Instance, Type
+from mypy.plugin import CallableType, FunctionSigContext, Plugin
+
class FunctionSigPlugin(Plugin):
def get_function_signature_hook(self, fullname):
@@ -7,20 +7,17 @@ def get_function_signature_hook(self, fullname):
return my_hook
return None
-def _str_to_int(api: CheckerPluginInterface, typ: Type) -> Type:
- if isinstance(typ, Instance):
- 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: FunctionSigContext) -> CallableType:
+ arg1_args = ctx.args[0]
+ 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=[_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),
+ arg_types=[arg1_type],
+ ret_type=arg1_type,
)
+
def plugin(version):
return FunctionSigPlugin