From 6bcf3f25ba80c5d6f1eb8e89b83c69d9d0a88561 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Thu, 7 Sep 2023 01:11:18 -0400 Subject: [PATCH 1/2] wip --- mypy/semanal.py | 4 +- test-data/unit/check-custom-plugin.test | 40 +++++++++++++++++++ test-data/unit/check-dataclasses.test | 11 +++++ .../unit/plugins/class_decorator_hook_2.py | 22 ++++++++++ 4 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 test-data/unit/plugins/class_decorator_hook_2.py diff --git a/mypy/semanal.py b/mypy/semanal.py index ec4d32aefeb9..663a7b010953 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1826,8 +1826,8 @@ def apply_class_plugin_hooks(self, defn: ClassDef) -> None: def get_fullname_for_hook(self, expr: Expression) -> str | None: if isinstance(expr, CallExpr): - return self.get_fullname_for_hook(expr.callee) - elif isinstance(expr, IndexExpr): + expr = expr.callee + if isinstance(expr, IndexExpr): return self.get_fullname_for_hook(expr.base) elif isinstance(expr, RefExpr): if expr.fullname: diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 9a0668f98c21..65964f4749ca 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -1029,3 +1029,43 @@ reveal_type(1) # N: Revealed type is "Literal[1]?" [file mypy.ini] \[mypy] plugins=/test-data/unit/plugins/custom_errorcode.py + +[case testClassDecoratorHook2] +# flags: --config-file tmp/mypy.ini +from typing import Any, TypeVar +T = TypeVar('T') + +def my_decorator(t: Any) -> Any: + raise + +@my_decorator +class C: + pass + +obj = C() +reveal_type(obj.magic) # N: Revealed type is "builtins.str" +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/class_decorator_hook_2.py + +[typing fixtures/typing-medium.pyi] + +[case testClassDecoratorHook2_incorrect] +# flags: --config-file tmp/mypy.ini +from typing import Any, TypeVar +T = TypeVar('T') + +def my_decorator(t: Any) -> Any: + raise + +@my_decorator()() # E: Missing positional argument "t" in call to "my_decorator" +class C: + pass + +obj = C() +_ = obj.magic # E: "C" has no attribute "magic" +[file mypy.ini] +\[mypy] +plugins=/test-data/unit/plugins/class_decorator_hook_2.py + +[typing fixtures/typing-medium.pyi] diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 8a50e7124d05..ed477ccaf486 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2515,3 +2515,14 @@ a: MyDataclass b = [a, a] # trigger joining the types [builtins fixtures/dataclasses.pyi] + +[case testDataclassesInvalidDecorator] +from dataclasses import dataclass + +@dataclass()() # E: Too few arguments +class C: # E: Cannot instantiate type "Type[Never]" + a: str + +reveal_type(C) # N: Revealed type is "def () -> __main__.C" + +[builtins fixtures/dataclasses.pyi] diff --git a/test-data/unit/plugins/class_decorator_hook_2.py b/test-data/unit/plugins/class_decorator_hook_2.py new file mode 100644 index 000000000000..ceeb83802443 --- /dev/null +++ b/test-data/unit/plugins/class_decorator_hook_2.py @@ -0,0 +1,22 @@ +from __future__ import annotations + +from typing import Callable + +from mypy.plugin import ClassDefContext, Plugin +from mypy.plugins.common import add_attribute_to_class + + +class MyPlugin(Plugin): + def get_class_decorator_hook_2(self, fullname: str) -> Callable[[ClassDefContext], bool] | None: + if fullname == "__main__.my_decorator": + return transform_hook + return None + + +def transform_hook(ctx: ClassDefContext) -> bool: + add_attribute_to_class(ctx.api, ctx.cls, 'magic', ctx.api.named_type('builtins.str')) + return True + + +def plugin(version: str) -> type[MyPlugin]: + return MyPlugin From 4098a8fb4ce22bf19f5d3d44110f231b8c60cef7 Mon Sep 17 00:00:00 2001 From: Ilya Priven Date: Thu, 7 Sep 2023 10:14:57 -0400 Subject: [PATCH 2/2] adjust for PEP-614 --- test-data/unit/check-custom-plugin.test | 11 +++++++++-- test-data/unit/check-dataclasses.test | 14 +++++++++++--- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/test-data/unit/check-custom-plugin.test b/test-data/unit/check-custom-plugin.test index 65964f4749ca..ad04cf7f61f4 100644 --- a/test-data/unit/check-custom-plugin.test +++ b/test-data/unit/check-custom-plugin.test @@ -1058,12 +1058,19 @@ T = TypeVar('T') def my_decorator(t: Any) -> Any: raise -@my_decorator()() # E: Missing positional argument "t" in call to "my_decorator" +@my_decorator()() class C: pass obj = C() -_ = obj.magic # E: "C" has no attribute "magic" +_ = obj.magic +[out] +main:8: error: invalid syntax + +[out version>=3.9] +main:8: error: Missing positional argument "t" in call to "my_decorator" +main:13: error: "C" has no attribute "magic" + [file mypy.ini] \[mypy] plugins=/test-data/unit/plugins/class_decorator_hook_2.py diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index ed477ccaf486..9d656af017c4 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -2519,10 +2519,18 @@ b = [a, a] # trigger joining the types [case testDataclassesInvalidDecorator] from dataclasses import dataclass -@dataclass()() # E: Too few arguments -class C: # E: Cannot instantiate type "Type[Never]" +@dataclass()() +class C: a: str -reveal_type(C) # N: Revealed type is "def () -> __main__.C" +reveal_type(C) + +[out] +main:3: error: invalid syntax + +[out version>=3.9] +main:3: error: Too few arguments +main:4: error: Cannot instantiate type "Type[Never]" +main:7: note: Revealed type is "def () -> __main__.C" [builtins fixtures/dataclasses.pyi]