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

Empty typing.Tuple #91137

Closed
serhiy-storchaka opened this issue Mar 11, 2022 · 7 comments
Closed

Empty typing.Tuple #91137

serhiy-storchaka opened this issue Mar 11, 2022 · 7 comments
Labels
3.11 only security fixes stdlib Python modules in the Lib dir

Comments

@serhiy-storchaka
Copy link
Member

BPO 46981
Nosy @gvanrossum, @serhiy-storchaka, @JelleZijlstra, @Fidget-Spinner
PRs
  • bpo-46981: Remove typing._TypingEmpty #31836
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2022-03-17.08:20:59.454>
    created_at = <Date 2022-03-11.08:21:12.398>
    labels = ['library', '3.11']
    title = 'Empty typing.Tuple'
    updated_at = <Date 2022-04-08.05:07:15.239>
    user = 'https://github.com/serhiy-storchaka'

    bugs.python.org fields:

    activity = <Date 2022-04-08.05:07:15.239>
    actor = 'JelleZijlstra'
    assignee = 'none'
    closed = True
    closed_date = <Date 2022-03-17.08:20:59.454>
    closer = 'serhiy.storchaka'
    components = ['Library (Lib)']
    creation = <Date 2022-03-11.08:21:12.398>
    creator = 'serhiy.storchaka'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 46981
    keywords = ['patch']
    message_count = 5.0
    messages = ['414892', '414947', '414986', '415391', '416958']
    nosy_count = 4.0
    nosy_names = ['gvanrossum', 'serhiy.storchaka', 'JelleZijlstra', 'kj']
    pr_nums = ['31836']
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = None
    url = 'https://bugs.python.org/issue46981'
    versions = ['Python 3.11']

    @serhiy-storchaka
    Copy link
    Member Author

    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__
    ()

    @serhiy-storchaka serhiy-storchaka added stdlib Python modules in the Lib dir labels Mar 11, 2022
    @gvanrossum
    Copy link
    Member

    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).

    @serhiy-storchaka
    Copy link
    Member Author

    If for repr(Tuple[()]), it is no longer needed.

    @serhiy-storchaka
    Copy link
    Member Author

    New changeset 15df8f8 by Serhiy Storchaka in branch 'main':
    bpo-46981: Remove typing._TypingEmpty (GH-31836)
    15df8f8

    @JelleZijlstra
    Copy link
    Member

    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.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    @serhiy-storchaka
    Copy link
    Member Author

    We get a report about broken pickling of empty typing.Tuple #94245. Shallow and deep copying are broken too.

    >>> copy.copy(Tuple[()])
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/serhiy/py/cpython3.10/Lib/copy.py", line 102, in copy
        return _reconstruct(x, None, *rv)
      File "/home/serhiy/py/cpython3.10/Lib/copy.py", line 265, in _reconstruct
        y = func(*args)
      File "/home/serhiy/py/cpython3.10/Lib/typing.py", line 312, in inner
        return func(*args, **kwds)
      File "/home/serhiy/py/cpython3.10/Lib/typing.py", line 1233, in __getitem__
        params = tuple(_type_check(p, msg) for p in params)
      File "/home/serhiy/py/cpython3.10/Lib/typing.py", line 1233, in <genexpr>
        params = tuple(_type_check(p, msg) for p in params)
      File "/home/serhiy/py/cpython3.10/Lib/typing.py", line 176, in _type_check
        raise TypeError(f"{msg} Got {arg!r:.100}.")
    TypeError: Tuple[t0, t1, ...]: each t must be a type. Got ().
    >>> copy.deepcopy(Tuple[()])
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/home/serhiy/py/cpython3.10/Lib/copy.py", line 172, in deepcopy
        y = _reconstruct(x, memo, *rv)
      File "/home/serhiy/py/cpython3.10/Lib/copy.py", line 265, in _reconstruct
        y = func(*args)
      File "/home/serhiy/py/cpython3.10/Lib/typing.py", line 312, in inner
        return func(*args, **kwds)
      File "/home/serhiy/py/cpython3.10/Lib/typing.py", line 1233, in __getitem__
        params = tuple(_type_check(p, msg) for p in params)
      File "/home/serhiy/py/cpython3.10/Lib/typing.py", line 1233, in <genexpr>
        params = tuple(_type_check(p, msg) for p in params)
      File "/home/serhiy/py/cpython3.10/Lib/typing.py", line 176, in _type_check
        raise TypeError(f"{msg} Got {arg!r:.100}.")
    TypeError: Tuple[t0, t1, ...]: each t must be a type. Got ().
    

    All this can be fixed by backporting this change. But it will break pyanalyze.

    michel-slm added a commit to michel-slm/MonkeyType that referenced this issue Sep 26, 2022
    This has a special case to handle '()', but the test (whether the
    'elements' iterable is truthy) no longer works as it always passes.
    
    Instead, always do the join, then test if it's truthy or not, and
    return the failsafe '()' if not.
    
    This addresses one test failure in Instagram#272:
    ```
    _______________________ TestRenderAnnotation.test_render_annotation[Tuple-Tuple[()]] _______________________
    tests/test_stubs.py:212: in test_render_annotation
        assert render_annotation(annotation) == expected
    E   AssertionError: assert 'Tuple[]' == 'Tuple[()]'
    E     - Tuple[()]
    E     ?       --
    E     + Tuple[]
    ```
    
    Appears to be due to
    python/cpython#91137
    
    Signed-off-by: Michel Alexandre Salim <michel@michel-slm.name>
    michel-slm added a commit to michel-slm/MonkeyType that referenced this issue Sep 26, 2022
    In Python 3.11, `get_args(Tuple[()])` returns `()` instead of `((),)`:
    
    python/cpython#91137
    
    Change our code to be able to handle this. Address this test failure in
    
    ```
    _____________________________ TestTypeConversion.test_type_round_trip[Tuple2] ______________________________
    tests/test_encoding.py:80: in test_type_round_trip
        assert type_from_dict(type_to_dict(typ)) == typ
    E   AssertionError: assert typing.Tuple == typing.Tuple[()]
    E    +  where typing.Tuple = type_from_dict({'module': 'typing', 'qualname': 'Tuple'})
    E    +    where {'module': 'typing', 'qualname': 'Tuple'} = type_to_dict(typing.Tuple[()])
    ```
    
    Signed-off-by: Michel Alexandre Salim <michel@michel-slm.name>
    michel-slm added a commit to michel-slm/MonkeyType that referenced this issue Sep 26, 2022
    Needs to update the special case for `Tuple[()]` for Python 3.11's
    
    python/cpython#91137
    
    Signed-off-by: Michel Alexandre Salim <michel@michel-slm.name>
    @amitbad
    Copy link

    amitbad commented Feb 20, 2024

    I was using python 3.10 and getting the same error while using Gemini-Pro-Vision.

    This resolved my issue - pip install pydantic==1.10.13

    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.11 only security fixes stdlib Python modules in the Lib dir
    Projects
    None yet
    Development

    No branches or pull requests

    4 participants