-
Notifications
You must be signed in to change notification settings - Fork 262
Added draft chapter to typing spec for constructors. #1667
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
Merged
Merged
Changes from 1 commit
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
79a38e0
Added draft chapter to typing spec for constructors.
erictraut 6694bd8
Update docs/spec/constructors.rst
erictraut aed835f
Update docs/spec/constructors.rst
erictraut 983d6e3
Update docs/spec/constructors.rst
erictraut 1c64a6c
Added section on signature consistency between `__new__` and `__init__`.
erictraut c5dfb1f
Incorporated PR feedback.
erictraut 07b0d11
Added clarification based on question in forum.
erictraut 7b68ca2
Incorporated feedback about callable conversion. Clarified behaviors …
erictraut 252421d
Tweaked the spec based on Jelle's feedback about a `__new__` method t…
erictraut 21f18a5
Updated handling of `Any` return types for `__call__` and `__new__` m…
erictraut 0ec500a
Incorporated feedback from @gvanrossum.
erictraut File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Updated handling of
Any
return types for __call__
and __new__
m…
…ethods to reflect suggestion from @rchen152 in [this post](https://discuss.python.org/t/draft-typing-spec-chapter-for-constructors/49744/22).
- Loading branch information
commit 21f18a5a717f4ba545c8d48c370f4f7aaf0979f2
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,12 +25,12 @@ method. If so, it should evaluate the call of this method using the supplied | |
arguments. If the metaclass is ``type``, this step can be skipped. | ||
|
||
If the evaluated return type of the ``__call__`` method is something other than | ||
``Any`` or an instance of the class being constructed, a type checker should | ||
an instance of the class being constructed, a type checker should | ||
assume that the metaclass ``__call__`` method is overriding ``type.__call__`` | ||
in some special manner, and it should not attempt to evaluate the ``__new__`` | ||
or ``__init__`` methods on the class. For example, some metaclass ``__call__`` | ||
methods are annotated to return ``NoReturn`` to indicate that constructor | ||
calls are not supported for that class. | ||
calls are not supported for that class. | ||
|
||
:: | ||
|
||
|
@@ -44,6 +44,11 @@ calls are not supported for that class. | |
|
||
assert_type(MyClass(), Never) | ||
|
||
If no return type annotation is provided for ``__call__``, a type checker may | ||
assume that it does not override ``type.__call__`` in a special manner and | ||
proceed as though the return type is an instance of the type specified by | ||
the ``cls`` parameter. | ||
|
||
|
||
``__new__`` Method | ||
================== | ||
|
@@ -91,47 +96,50 @@ them. | |
assert_type(MyClass(1), MyClass[int]) | ||
assert_type(MyClass(""), MyClass[str]) | ||
|
||
For most classes, the return type for ``__new__`` method is typically ``Self``, | ||
but other types are also allowed. For example, the ``__new__`` method may return | ||
an instance of a subclass or an instance of some completely unrelated class. | ||
For most classes, the return type for the ``__new__`` method is typically | ||
``Self``, but other types are also allowed. For example, the ``__new__`` | ||
method may return an instance of a subclass or an instance of some completely | ||
unrelated class. | ||
|
||
If the return type of the ``__new__`` method evaluates to ``Any`` or a union that | ||
includes ``Any``, a type checker should proceed to evaluate the ``__init__`` | ||
method as if the return type of ``__new__`` was ``Self``. However, the final | ||
evaluated type of the constructor call should include ``Any`` in this case, | ||
unioned with the type informed by the evaluation of the ``__init__`` call. | ||
If the evaluated return type of ``__new__`` is not an instance of the class | ||
being constructed (or a subclass thereof) or is a union that includes such | ||
a class, a type checker should assume that the ``__init__`` method will not be | ||
called. This is consistent with the runtime behavior of the ``type.__call__`` | ||
method. | ||
|
||
:: | ||
|
||
class MyClass: | ||
def __new__(cls, *args, **kwargs) -> Any: | ||
return super().__new__(*args, **kwargs) | ||
def __new__(cls) -> int: | ||
return 0 | ||
|
||
def __init__(self): | ||
# The __init__ method will not be called in this case, so | ||
# it should not be evaluated. | ||
def __init__(self, x: int): | ||
pass | ||
|
||
# Constructor call evaluates to `Any` (from __new__ method) | ||
# unioned with `MyClass` (from __init__ method). | ||
assert_type(MyClass(), Any | MyClass) | ||
assert_type(MyClass(), int) | ||
|
||
If the evaluated return type of ``__new__`` is not an instance of the class | ||
being constructed (or a subclass thereof) or is a union that includes such | ||
a class, a type checker should assume that the ``__init__`` method will not be | ||
called. This is consistent with the runtime behavior of the ``type.__call__`` | ||
method. | ||
For purposes of this test, an explicit return type of ``Any`` (or a | ||
union containing ``Any``) should be treated as a type that is not an instance | ||
of the class being constructed. | ||
|
||
:: | ||
|
||
class MyClass: | ||
def __new__(cls) -> int: | ||
def __new__(cls) -> Any: | ||
return 0 | ||
|
||
# The __init__ method will not be called in this case, so | ||
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. s/will/may 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. Not sure I understand what you mean, but I rephrased the comment for clarity. |
||
# it should not be evaluated. | ||
def __init__(self, x: int): | ||
pass | ||
|
||
assert_type(MyClass(), int) | ||
assert_type(MyClass(), Any) | ||
|
||
If the return type of ``__new__`` is not annotated, a type checker may assume | ||
that the return type is ``Self`` and proceed with the assumption that the | ||
``__init__`` method will be called. | ||
|
||
If the class is generic, it is possible for a ``__new__`` method to override | ||
the specialized class type and return a class instance that is specialized | ||
|
@@ -357,16 +365,19 @@ When converting a class to a callable type, a type checker should use the | |
following rules: | ||
|
||
1. If the class has a custom metaclass that defines a ``__call__`` method | ||
that is annotated with a return type other than ``Any`` or a subclass of the | ||
class being constructed, a type checker should assume that the metaclass | ||
``__call__`` method is overriding ``type.__call__`` in some special manner. | ||
In this case, the callable should be synthesized from the parameters and return | ||
type of the metaclass ``__call__`` method after it is bound to the class, | ||
and the ``__new__`` or ``__init__`` methods (if present) should be ignored. | ||
This is an uncommon case. In the more typical case where there is no custom | ||
metaclass that overrides ``type.__call__`` in a special manner, the metaclass | ||
``__call__`` signature should be ignored for purposes of converting to a | ||
callable type. | ||
that is annotated with a return type other than a subclass of the | ||
class being constructed (or a union that contains such a type), a type | ||
gvanrossum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
checker should assume that the metaclass ``__call__`` method is overriding | ||
``type.__call__`` in some special manner. In this case, the callable should | ||
be synthesized from the parameters and return type of the metaclass | ||
``__call__`` method after it is bound to the class, and the ``__new__`` or | ||
``__init__`` methods (if present) should be ignored. This is an uncommon | ||
case. In the more typical case where there is no custom metaclass that | ||
overrides ``type.__call__`` in a special manner, the metaclass ``__call__`` | ||
signature should be ignored for purposes of converting to a callable type. | ||
If a custom metaclass ``__call__`` method is present but does not have an | ||
annotated return type, type checkers may assume that the method acts like | ||
``type.__call__`` and proceed to the next step. | ||
gvanrossum marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
2. If the class defines a ``__new__`` method or inherits a ``__new__`` method | ||
from a base class other than ``object``, a type checker should synthesize a | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.