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

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

Closed
vpogrebi opened this issue May 1, 2020 · 8 comments
Closed

Comments

@vpogrebi
Copy link

vpogrebi commented May 1, 2020

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

@JelleZijlstra
Copy link
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.

@vpogrebi
Copy link
Author

vpogrebi commented May 1, 2020

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

@JelleZijlstra
Copy link
Member

You should annotate only the first definition.

@vpogrebi
Copy link
Author

vpogrebi commented May 1, 2020

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.

@hauntsaninja
Copy link
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).

@vpogrebi
Copy link
Author

vpogrebi commented May 1, 2020

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.

@DevilXD
Copy link

DevilXD commented May 2, 2020

@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:
    ...

@AlexWaygood
Copy link
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
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants