-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Type checking of class decorators #4544
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,7 +40,7 @@ | |
from mypy.sametypes import is_same_type, is_same_types | ||
from mypy.messages import MessageBuilder, make_inferred_type_note | ||
import mypy.checkexpr | ||
from mypy.checkmember import map_type_from_supertype, bind_self, erase_to_bound | ||
from mypy.checkmember import map_type_from_supertype, bind_self, erase_to_bound, type_object_type | ||
from mypy import messages | ||
from mypy.subtypes import ( | ||
is_subtype, is_equivalent, is_proper_subtype, is_more_precise, | ||
|
@@ -1254,10 +1254,23 @@ def visit_class_def(self, defn: ClassDef) -> None 8000 : | |
# Otherwise we've already found errors; more errors are not useful | ||
self.check_multiple_inheritance(typ) | ||
|
||
for decorator in defn.decorators: | ||
# Currently this only checks that the decorator itself is well typed. | ||
# TODO: Check that applying the decorator to the class would do the right thing. | ||
self.expr_checker.accept(decorator) | ||
if defn.decorators: | ||
sig = type_object_type(defn.info, self.named_type) | ||
# Decorators are applied in reverse order. | ||
for decorator in reversed(defn.decorators): | ||
if (isinstance(decorator, CallExpr) | ||
and isinstance(decorator.analyzed, PromoteExpr)): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This exception deserves a comment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done! |
||
continue | ||
|
||
dec = self.expr_checker.accept(decorator) | ||
temp = self.temp_node(sig) | ||
fullname = None | ||
if isinstance(decorator, RefExpr): | ||
fullname = decorator.fullname | ||
|
||
sig, t2 = self.expr_checker.check_call(dec, [temp], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better use an underscore for throw-away variable. |
||
[nodes.ARG_POS], defn, | ||
callable_name=fullname) | ||
|
||
def check_protocol_variance(self, defn: ClassDef) -> None: | ||
"""Check that protocol definition is compatible with declared | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4236,8 +4236,15 @@ def decorate_forward_ref() -> Callable[[Type[A]], Type[A]]: | |
@decorate(22, 25) # E: Too many arguments for "decorate" | ||
@decorate_forward_ref() | ||
@decorate(11) | ||
class A: | ||
pass | ||
class A: pass | ||
|
||
@decorate # E: Argument 1 to "decorate" has incompatible type "Type[A2]"; expected "int" | ||
class A2: pass | ||
|
||
[case testClassDecoratorIncorrect] | ||
def not_a_class_decorator(x: int) -> int: ... | ||
@not_a_class_decorator(7) # E: "int" not callable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The error message is quite obscure. I would somehow highlight that a class decorator must be a function that accepts a type. If this is too hard, then leave a TODO about this in code. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Left a TODO. Only thing I can figure out is collect all the errors with |
||
class A3: pass | ||
|
||
not_a_function = 17 | ||
@not_a_function() # E: "int" not callable | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also add a test for directly applying @not_a_function
class C: ... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done |
||
|
@@ -4249,5 +4256,3 @@ class C: pass | |
|
||
@undefined # E: Name 'undefined' is not defined | ||
class D: pass | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would leave some kind of
TODO
here. One of main use cases:is still not supported.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added below