This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Empty typing.Tuple
Type: Stage: resolved
Components: Library (Lib) Versions: Python 3.11
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: JelleZijlstra, gvanrossum, kj, serhiy.storchaka
Priority: normal Keywords: patch

Created on 2022-03-11 08:21 by serhiy.storchaka, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 31836 merged serhiy.storchaka, 2022-03-12 09:37
Messages (5)
msg414892 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2022-03-11 08:21
There are two empty typing.Tuple. They have the same repr but are not equal.

>>> from typing import *
>>> t1 = Tuple[()]
>>> t2 = t1.copy_with(())
>>> t1
typing.Tuple[()]
>>> t2
typing.Tuple[()]
>>> t1 == t2
False
>>> t1.__args__
((),)
>>> t2.__args__
()

The only differences is that one has empty __args__, while other has __args__ containing an empty tuple. There is a code purposed to make __args__ containing an empty tuple in this case. What is the purpose?

It is not pure theoretical question. This affects unpacked TypeVarTuple substitution. With natural implementation Tuple[Unpack[Ts]][()] is not equal to Tuple[()] and I still have not figured which and where code should be added to handle this special case. It would be easier if such special case did not exist.

Built-in tuple does not have a special case:

>>> tuple[()].__args__
()
msg414947 - (view) Author: Guido van Rossum (gvanrossum) * (Python committer) Date: 2022-03-11 23:35
Alas, I have no idea. I don't even recall what copy_with() is for (it was apparently introduced in 3.7). Possibly vopy_with() is wrong here? I imaging some of this has to do with the special casing needed so that repr() of an empty Tuple type doesn't print "Tuple[]" (which IIRC it did, long ago).
msg414986 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2022-03-12 09:40
If for repr(Tuple[()]), it is no longer needed.
msg415391 - (view) Author: Serhiy Storchaka (serhiy.storchaka) * (Python committer) Date: 2022-03-17 07:52
New changeset 15df8f8d89a0e563bdd15e4cd6734298736a5a1d by Serhiy Storchaka in branch 'main':
bpo-46981: Remove typing._TypingEmpty (GH-31836)
https://github.com/python/cpython/commit/15df8f8d89a0e563bdd15e4cd6734298736a5a1d
msg416958 - (view) Author: Jelle Zijlstra (JelleZijlstra) * (Python committer) Date: 2022-04-08 05:07
I tried out 3.11 on my pyanalyze type checker and got some failures because of this change, because my previous trick for distinguishing between Tuple and Tuple[()] failed.

3.10:

>>> from typing import get_args, Tuple
>>> get_args(Tuple[()])
((),)
>>> get_args(Tuple)
()


3.11:

>>> from typing import get_args, Tuple
>>> get_args(Tuple[()])
()
>>> get_args(Tuple)
()

However, the new behavior is more consistent: get_args(tuple[()]) always returned (). It's also easy enough to work around (just check `... is Tuple`).

I'll put a note in the What's New for 3.11 about this change.
History
Date User Action Args
2022-04-11 14:59:57adminsetgithub: 91137
2022-04-08 05:07:15JelleZijlstrasetmessages: + msg416958
2022-03-17 08:20:59serhiy.storchakasetstatus: open -> closed
stage: patch review -> resolved
resolution: fixed
versions: + Python 3.11
2022-03-17 07:52:39serhiy.storchakasetmessages: + msg415391
2022-03-12 09:40:10serhiy.storchakasetmessages: + msg414986
2022-03-12 09:37:44serhiy.storchakasetkeywords: + patch
stage: patch review
pull_requests: + pull_request29934
2022-03-11 23:35:09gvanrossumsetmessages: + msg414947
2022-03-11 19:38:58JelleZijlstrasetnosy: + JelleZijlstra
2022-03-11 08:21:12serhiy.storchakacreate