-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Make classmethod's first argument be Type[...] #5646
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
Conversation
Currently the first argument to `__new__` and classmethods is a callable type that is constructed during semantic analysis by typechecker code (!) that looks for the `__init__`/`__new__` methods. This causes a number of problems, including not being able to call `object.__new__` in a subclass's `__new__` if it took arguments (#4190) and giving the wrong type if `__init__` appeared after the class method (#1727). Taking a `Type` instead lets us solve those problems, and postpone computing the callable version of the type until typechecking if it is needed. This also lets us drop a bunch of plugin code that tries to fix up the types of its cls arguments post-hoc, sometimes incorrectly (#5263). Fixes #1727. Fixes #4190. Fixes #5263.
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.
Glad to see the cls
argument special casing removed and three bugs fixed with a single PR! Looks good, just left a few minor ideas about tests.
Before merging, can you make sure that this doesn't cause trouble with internal Dropbox codebases?
test-data/unit/check-classes.test
Outdated
T = TypeVar('T') | ||
class B(Generic[T]): | ||
def __new__(cls, foo: T) -> 'B[T]': | ||
return object.__new__(cls) |
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.
Maybe reveal the type of the return value?
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.
Sure, though it is Any
, since that's what the stubs have for object.__new__
. (Which is probably fixable in typeshed/fixtures?)
|
||
@classmethod | ||
def foo(cls) -> None: | ||
reveal_type(cls) # E: Revealed type is 'Type[__main__.A[T`1]]' |
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.
Maybe try calling cls
and reveal the type of the result?
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.
This is an important test case. I would add at least one more where you actually call cls
, so that T
is inferred, for example cls(1)
should be inferred as A[int]
.
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.
The point is that Type[...]
previously didn't work well with generic types, so it is better to double-check it will not regress.
class A: | ||
@classmethod | ||
def make(cls: Type[T]) -> T: | ||
return cls() |
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.
Maybe reveal the type of cls()
?
It causes just a little trouble, which I will fix up. Mostly it creates some redundant cast errors. |
test-data/unit/check-classes.test
Outdated
def __init__(self, baz): | ||
# type: (str) -> None | ||
self.baz = baz | ||
[builtins fixtures/classmethod.pyi] |
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 add two more test cases like this:
- One where
__init__
has a forward reference in its signature - Another with an overloaded classmethod
@msullivan This looks very good, and simplifies the code. I just have two suggestions for more tests. |
0abfec0
to
3390ab5
Compare
Currently the first argument to
__new__
and classmethods is acallable type that is constructed during semantic analysis by
typechecker code (!) that looks for the
__init__
/__new__
methods.This causes a number of problems, including not being able to call
object.__new__
in a subclass's__new__
if it took arguments(#4190) and giving the wrong type if
__init__
appeared after theclass method (#1727).
Taking a
Type
instead lets us solve those problems, and postponecomputing the callable version of the type until typechecking if it is
needed.
This also lets us drop a bunch of plugin code that tries to fix up the
types of its cls arguments post-hoc, sometimes incorrectly (#5263).
Fixes #1727.
Fixes #4190.
Fixes #5263.