Skip to content

MyPy disallows Tuple to be instantiated with variable number of items #8758

Closed

Description

@vpogrebi

Tuple in Python - is a fixed-length list ("frozen list"). But there's nothing in Python that prevents same-name Tuple to be instantiated with different number of items (which may well be based on some condition)...

I have an issue related to mypy, conditional import and validation of the __all package attribute (defined as a Tuple containing variable number of items depending on the success of the conditional import). Consider following within package's init.py:

try:
    from .__version__ import version as __version__
    __all__ = (
        '...........',
        '...........',
        '...........',
        '__version__'
    )
except ImportError:
    __all__ = (
        '...........',
        '...........',
        '...........'
    )

When this code is checked by mypy, it raises following error:

______________________________ units/__init__.py _______________________________
15: error: Incompatible types in assignment (expression has type "Tuple[str, str, str]", variable has type "Tuple[str, str, str, str]")
_________________________________ test session _________________________________
mypy exited with status 1.
=============================== warnings summary ===============================

This issue keeps me from building Python package in the presence of 'mypy' check; I had to explicitly disable mypy check in order to build this package.

I'd like to have mypy recognize possibility of variable-size Tuple in the presence of conditional import (and in general - same-name Tuple can be instantiated having different size depending on some conditions), or be able to ignore this error (unfortunately, mypy output does not report error number that can be ignored), or entirely exclude given file from mypy check (similarly to flake8's "exclude" in the setup.cfg).

Activity

  1. JelleZijlstra commented on May 1, 2020

    @JelleZijlstra
    Member

    You can work around this by adding an explicit annotation of Tuple[str, ...]. I guess mypy could implicitly infer such a type for code like the above, but that runs into issues like #1026.

  2. vpogrebi commented on May 1, 2020

    @vpogrebi
    Author

    @JelleZijlstra

    You can work around this by adding an explicit annotation of Tuple[str, ...]. I guess mypy could implicitly infer such a type for code like the above, but that runs into issues like #1026.

    Tested your suggestion, but that does not work:

    try:
        from .__version__ import version as __version__
        __all__: Tuple[str, str, str, str] = (
            '.......',
            '.......',
            '.......',
            '__version__'
        )
    except ImportError:
        __all__: Tuple[str, str, str] = (
            '.......',
            '.......',
            '.......'
        )
    

    ... results in

    =================================== FAILURES ===================================
    ______________________________ units/__init__.py _______________________________
    16: error: Name '__all__' already defined on line 9
    _________________________________ test session _________________________________
    mypy exited with status 1.
    
  3. JelleZijlstra commented on May 1, 2020

    @JelleZijlstra
    Member

    You should annotate only the first definition.

  4. vpogrebi commented on May 1, 2020

    @vpogrebi
    Author

    Tried that, too - which brings back to the initial error:

    try:
        from .__version__ import version as __version__
        __all__: Tuple[str, str, str, str] = (
            '.......',
            '.......',
            '.......',
            '__version__'
        )
    except ImportError:
        __all__ = (
            '.......',
            '.......',
            '.......'
        )
    

    Error:

    =================================== FAILURES ===================================
    ______________________________ units/__init__.py _______________________________
    17: error: Incompatible types in assignment (expression has type "Tuple[str, str, str]", variable has type "Tuple[str, str, str, str]")
    _________________________________ test session _________________________________
    mypy exited with status 1.
    
    
  5. hauntsaninja commented on May 1, 2020

    @hauntsaninja
    Collaborator

    You should annotate it (once) as Tuple[str, ...] (with literal ..., just copy and paste). This is the type for a tuple whose elements are all str, but could be of variable length (https://mypy.readthedocs.io/en/stable/kinds_of_types.html#tuple-types).

  6. vpogrebi commented on May 1, 2020

    @vpogrebi
    Author

    Thank you, that worked. I had my own solution that involved Union, but I like this one better (I was not aware of the ... type annotation).

    But still - it is desired to have these mypy errors eliminated in the future releases.

  7. DevilXD commented on May 2, 2020

    @DevilXD

    @vpogrebi This is not a mypy bug, the tuple infers the first type it encounters when assigned to, and assigning a different type (with different amount of items inside the tuple) later on is an error - this is valid behavior. You need to explicitly tell it to either accept a variable amount of items via Tuple[str, ...], allow redefinition via an appropriate flag, or specify a strict set of expected types before the try-except block:

    __all__: Union[Tuple[str, str, str, str], Tuple[str, str, str]]
    try:
        ...
    except:
        ...
  8. AlexWaygood commented on Mar 25, 2022

    @AlexWaygood
    Member

    I agree with @DevilXD that this doesn't really seem like a bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions