-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Believe more __new__ return types #16020
base: master
Are you sure you want to change the base?
Conversation
Fixes python#1020 (comment) Surprisingly popular comment on a closed issue. We still issue the warning, but we do trust the return type instead of overruling it. Maybe fixes python#16012
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
There's an issue where we turn TypeType's into CallableType, but the type_obj of CallableType just looks at the return type of the constructor. So this doesn't work as expected:
|
61b6476
to
25c9ef0
Compare
This comment has been minimized.
This comment has been minimized.
25c9ef0
to
5b791f7
Compare
5b791f7
to
393b303
Compare
This comment has been minimized.
This comment has been minimized.
Thanks so much @hauntsaninja, couldn't get this far due to limited knowledge of the mypy internals |
This comment has been minimized.
This comment has been minimized.
mypy/checkmember.py
Outdated
# The following is a hack. mypy often represents types as CallableType, where the signature of | ||
# CallableType is determined by __new__ or __init__ of the type (this logic is in | ||
# type_object_type). Then if we ever need the TypeInfo or an instance of the type, we fish | ||
# around for the return type in CallableType.type_object. Unfortunately, this is incorrect if |
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.
we fish around for the return type in CallableType.type_object
Where do we do this? This function certainly didn't, going for simple .ret_type
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 clarified the comment a little bit. type_object
also just uses ret_type
. Also, thanks for the suggestion to use bound_args, I added you as a co-author of this PR!
This comment has been minimized.
This comment has been minimized.
# see e.g. testGenericClassMethodUnboundOnClass. So just copy them over to our type. | ||
# This does the wrong thing with custom __new__, see testNewReturnType15, but is | ||
# no worse than previous behaviour. | ||
ret_type = bound_arg.copy_modified(args=ret_type.args) |
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 you can
ret_type = bound_arg.copy_modified(args=ret_type.args) | |
ret_type = bound_arg.copy_modified() |
if you change checkexpr.py's apply_generic_arguments
to expand bound_args
(not sure if it's correct to do...):
return callable.copy_modified(
ret_type=expand_type(callable.ret_type, id_to_type),
+ bound_args=[
+ expand_type(ba, id_to_type) if ba is not None else None
+ for ba in callable.bound_args
+ ],
variables=remaining_tvars,
type_guard=type_guard,
)
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.
TBH I don't like this. This introduces some hack(s) that can backfire unexpectedly in "regular" cases to support a truly weird (even if somewhat common) use case.
Yeah, it's a little icky :-( I'm curious what "regular" cases you think this makes problematic? I do think it's something we should support, so would like to find a way forward. E.g. it came up again yesterday in python/typeshed#10662 (comment) I'd also definitely be open to removing this back compat hack https://github.com/python/mypy/pull/16020/files#diff-1dce7e2a19559e2ff4e08fd46877b87c9cfec3cb5e6d465157788542948e11e4R206-R208 . It does result in a few mypy_primer hits, but they're technically correct. |
I think various operations with class objects, like type applications, subtyping/inference against them, member access on them etc. Also TBH I don't even remember why do we need |
I'd also rather have the type in a specialized attribute than |
Do you think the following use case with generics will be supported? from typing import Generic, TypeVar
T = TypeVar("T")
class A(Generic[T]):
# Having `T` returned at runtime would only be possible with a metaclass.
# This is a hack that should most probably be in an `if TYPE_CHECKING` block
# To describe the runtime behaviour of the metaclass' `__call__` method for example.
def __new__(cls, *args, **kwargs) -> T:
...
class Model:
pass
reveal_type(A[Model]()) # Type of "A[Model]()" is "Model"
class B(A[Model]):
pass
reveal_type(B()) # Type of "B()" is "Model" This is a workaround for FactoryBoy/factory_boy#903 (comment), and could probably be used for any situation where we want to take into account the return type of
|
# if __new__ returns an unrelated type, but we can kind of salvage things here using | ||
# CallableType.bound_args | ||
self_type: Type = typ | ||
if len(item.bound_args) == 1 and item.bound_args[0]: |
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 agree with other comments that using bound_args
doesn't look like something we want to do. I think storing the real self type in a new attribute might be a better approach. All type operations should be updated to do something reasonable with the attribute.
It seems like the above should also fix the issue that no error is currently reported here, and perhaps other related issues:
class C:
def __new__(self) -> D:
return D()
class D(C):
x: int
C.x # No Error, but fails at runtime
We'd use use the new attribute when looking up class attributes.
I appreciate this PR. It helps correctly type the dominate library, which currently cannot be correctly typed for some of it's API. |
Diff from mypy_primer, showing the effect of this PR on open source code: dd-trace-py (https://github.com/DataDog/dd-trace-py)
+ ddtrace/propagation/http.py:1059: error: Unused "type: ignore" comment [unused-ignore]
+ ddtrace/propagation/http.py:1061: error: Argument 2 to "_attach_baggage_to_context" has incompatible type "None"; expected "Context" [arg-type]
+ ddtrace/propagation/http.py:1062: error: Incompatible return value type (got "None", expected "Context") [return-value]
steam.py (https://github.com/Gobot1234/steam.py)
- steam/client.py:1321: error: Invalid self argument "type[TradeOffer[Any, Any, Any]]" to attribute function "_from_history" with type "Callable[[type[TradeOffer[MovedItem[UserT], MovedItem[ClientUser], UserT]], ConnectionState, TradeOfferHistoryTrade, Sequence[Description]], TradeOffer[MovedItem[UserT], MovedItem[ClientUser], UserT]]" [misc]
+ steam/client.py:1321: error: Invalid self argument "type[TradeOffer[ReceivingAssetT, SendingAssetT, UserT]]" to attribute function "_from_history" with type "Callable[[type[TradeOffer[MovedItem[UserT], MovedItem[ClientUser], UserT]], ConnectionState, TradeOfferHistoryTrade, Sequence[Description]], TradeOffer[MovedItem[UserT], MovedItem[ClientUser], UserT]]" [misc]
pandera (https://github.com/pandera-dev/pandera)
+ pandera/api/dataframe/model.py:395: error: "type[DataFrameBase[Any]]" has no attribute "Config" [attr-defined]
+ tests/strategies/test_strategies.py:996: error: Need type annotation for "sample_data" [var-annotated]
+ tests/dask/test_dask.py:30: error: Need type annotation for "ddf" [var-annotated]
+ tests/dask/test_dask.py:30: note: Error code "var-annotated" not covered by "type: ignore" comment
+ tests/dask/test_dask.py:33: error: Need type annotation for "ddf" [var-annotated]
+ tests/dask/test_dask.py:33: note: Error code "var-annotated" not covered by "type: ignore" comment
|
@hauntsaninja Perhaps you're already aware, but the official typing specs have a section on the |
This new spec chapter is pretty complex, I don't know how much of it is being already implemented in mypy (e.g. #14122 is not). Perhaps this PR could be considered as a quick win? |
I agree @Viicos; all progress towards compatibility with the typing spec is a win. The reason I mentioned it is that if one is unaware of it; there's a chance of deviating away from it. |
You can be assured that @hauntsaninja is aware of the spec change; he signed off on it after all (python/typing-council#25) :) Fully implementing that spec change in mypy will be more work but this PR seems like a step in the right direction, so hopefully we'll be able to get it ready to merge soon. |
Fixes #1020 (comment) Surprisingly popular comment on a closed issue.
We still issue the warning, but we do trust the return type instead of overruling it.
Fixes #14471. Fixes #8330.
We avoid fixing #15182 for now.
Co-authored-by: ikonst