Skip to content
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

Fix tuple[Any, ...] subtyping #16108

Merged
merged 11 commits into from
Sep 14, 2023

Conversation

hauntsaninja
Copy link
Collaborator

Follow up to #16073 and #16076
Fix needed for https://github.com/python/mypy/pull/16053/files#r1316481395

I add test cases that would have caught my previous incorrect PR. I add an explicit case for the new desirable behaviour we see with zip.

Follow up to python#16073 and python#16076
Fix needed for https://github.com/python/mypy/pull/16053/files#r1316481395

I add test cases that would have caught my previous incorrect PR. I add
an explicit case for the new desirable behaviour we see with zip.
@github-actions

This comment has been minimized.

@hauntsaninja
Copy link
Collaborator Author

hauntsaninja commented Sep 14, 2023

Hmm, this breaks commutativity of meet. Easy to make an ad hoc patch...

@github-actions

This comment has been minimized.

@hauntsaninja hauntsaninja marked this pull request as ready for review September 14, 2023 05:10
Copy link
Member

@ilevkivskyi ilevkivskyi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good, but there are some suspicious parts, let's see what primer output will be after fixing these.

mypy/meet.py Outdated
@@ -90,10 +90,15 @@ def meet_types(s: Type, t: Type) -> ProperType:
return t

if not isinstance(s, UnboundType) and not isinstance(t, UnboundType):
swap = isinstance(t, TupleType) and not isinstance(s, TupleType)
if swap:
s, t = t, s
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks suspicious, the check below is for proper subtype, that should not now anything about Any.

mypy/subtypes.py Outdated
and len(left.args) == 1
and isinstance(get_proper_type(left.args[0]), AnyType)
):
return True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should return not self.proper_subtype similar to above.

mypy/subtypes.py Outdated
# TODO: we need a special case similar to above to consider (something that maps to)
# tuple[Any, ...] a subtype of Tuple[<whatever>].
if (
left.type.fullname == "builtins.tuple"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should use has_base() + map_instance_to_supertype() logic like above. Otherwise this breaks transitivity of subtyping.

takes_tuple_aa(inst_tuple_aa)
takes_tuple_aa(inst_tuple_aa_subclass)
# This next one should maybe not be an error
takes_tuple_aa(inst_tuple_any_subclass) # E: Argument 1 to "takes_tuple_aa" has incompatible type "tuple_any_subclass"; expected "Tuple[A, A]"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be allowed (see above, if A <: B and B <: C, then A must be <: C).

@github-actions

This comment has been minimized.

@hauntsaninja
Copy link
Collaborator Author

Thanks for the review! We lose several of the true positive warnings on pandas etc (the case tested by testTupleOverloadZipAny), but this is a right change and we should fix by changing overload return inference.

mypy/subtypes.py Outdated
# TODO: we need a special case similar to above to consider (something that maps to)
# tuple[Any, ...] a subtype of Tuple[<whatever>].
if (
mapped.type.has_base("builtins.tuple")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you need the last commit? This condition is always true because of the surrounding if (each tuple fallback has builtins.tuple in MRO), and also now this check looks wrong because it may trigger in unrelated cases involving something like class M(Generic[T], tuple[int, ...]): ....

What you had before was almost right, except you didn't need the check len(mapped.args) == 1, it should be always true if mapped.type.fullname == "builtins.tuple".

Copy link
Collaborator Author

@hauntsaninja hauntsaninja Sep 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I misinterpreted your comment here #16108 (comment)

Ugh, I tried to test the class M(Generic[T], tuple[int, ...]): ... case but I did it backwards I can't actually find a test, even with various NamedTuples

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you will not be able to come up with a test case, don't try to hard. It may be not easy to trigger such edge case (because of how user defined tuple types are represented). You already have enough test cases :-)

(I would still remove the unneeded len check)

@github-actions
Copy link
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

mitmproxy (https://github.com/mitmproxy/mitmproxy)
+ mitmproxy/coretypes/multidict.py:153: error: Unused "type: ignore" comment  [unused-ignore]
+ mitmproxy/coretypes/multidict.py:167: error: Unused "type: ignore" comment  [unused-ignore]
+ mitmproxy/tools/console/grideditor/base.py:170: error: Unused "type: ignore" comment  [unused-ignore]

Expression (https://github.com/cognitedata/Expression)
- expression/collections/block.py:814: error: Overloaded function implementation cannot produce return type of signature 1  [misc]
- expression/collections/block.py:814: error: Overloaded function implementation cannot produce return type of signature 2  [misc]
- expression/collections/block.py:814: error: Overloaded function implementation cannot produce return type of signature 3  [misc]
- expression/extra/parser.py:219: error: Overloaded function implementation cannot produce return type of signature 1  [misc]
- expression/extra/parser.py:219: error: Overloaded function implementation cannot produce return type of signature 2  [misc]

steam.py (https://github.com/Gobot1234/steam.py)
- steam/trade.py:321: error: Incompatible types in assignment (expression has type "tuple[Any, ...]", base class "Asset" defined the type as "tuple[str, str, str, str, str, str, str]")  [assignment]
- steam/ext/csgo/backpack.py:263: error: Incompatible types in assignment (expression has type "tuple[Any, ...]", base class "Asset" defined the type as "tuple[str, str, str, str, str, str, str]")  [assignment]
- steam/ext/csgo/backpack.py:330: error: Incompatible types in assignment (expression has type "tuple[Any, ...]", base class "Asset" defined the type as "tuple[str, str, str, str, str, str, str]")  [assignment]
- steam/ext/csgo/backpack.py:330: error: Cannot determine type of "REPR_ATTRS"  [has-type]
- steam/ext/tf2/backpack.py:80: error: Incompatible types in assignment (expression has type "tuple[Any, ...]", base class "Asset" defined the type as "tuple[str, str, str, str, str, str, str]")  [assignment]

mypy (https://github.com/python/mypy)
+ mypy/main.py:1362: error: Unused "type: ignore" comment  [unused-ignore]

operator (https://github.com/canonical/operator)
- ops/storage.py:191: error: Incompatible types in "yield" (actual type "tuple[Any, ...]", expected type "tuple[str, str, str]")  [misc]

jax (https://github.com/google/jax)
+ jax/_src/scipy/special.py:293: error: Unused "type: ignore" comment  [unused-ignore]

mongo-python-driver (https://github.com/mongodb/mongo-python-driver)
+ pymongo/mongo_client.py:1681: error: Unused "type: ignore" comment  [unused-ignore]

ibis (https://github.com/ibis-project/ibis)
- ibis/util.py:126: error: Incompatible return value type (got "tuple[Any, ...]", expected "tuple[V]")  [return-value]
- ibis/backends/oracle/__init__.py:51: error: Incompatible types in assignment (expression has type "tuple[Any, ...]", base class "ExprTranslator" defined the type as "tuple[type[Lag], type[Lead], type[DenseRank], type[MinRank], type[FirstValue], type[LastValue], type[PercentRank], type[CumeDist], type[NTile]]")  [assignment]
- ibis/backends/trino/compiler.py:23: error: Incompatible types in assignment (expression has type "tuple[Any, ...]", base class "ExprTranslator" defined the type as "tuple[type[Lag], type[Lead], type[DenseRank], type[MinRank], type[FirstValue], type[LastValue], type[PercentRank], type[CumeDist], type[NTile]]")  [assignment]
- ibis/backends/snowflake/__init__.py:58: error: Incompatible types in assignment (expression has type "tuple[Any, ...]", base class "ExprTranslator" defined the type as "tuple[type[Lag], type[Lead], type[DenseRank], type[MinRank], type[FirstValue], type[LastValue], type[PercentRank], type[CumeDist], type[NTile]]")  [assignment]

@hauntsaninja hauntsaninja merged commit 2c2d126 into python:master Sep 14, 2023
17 checks passed
@hauntsaninja hauntsaninja deleted the tuple-any-subtyping branch September 14, 2023 21:27
@hauntsaninja
Copy link
Collaborator Author

Thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants