Skip to content

Issues with conditional imports #1297

Closed

Description

@JukkaL

Even though #649 was closed, there still remains several issues related to conditional imports. This issue is a top-level issue for these.

Related, more specific issues:

Activity

  1. added this to the milestone on Mar 17, 2016
  2. gvanrossum commented on Mar 17, 2016

    @gvanrossum
    Member

    Also #1289.

  3. JukkaL commented on Apr 5, 2016

    @JukkaL
    CollaboratorAuthor

    Also #1327.

  4. JukkaL commented on Apr 6, 2016

    @JukkaL
    CollaboratorAuthor

    And #1334.

  5. modified the milestones: , on Apr 14, 2016
  6. added a commit that references this issue on Oct 20, 2016
  7. removed this from the milestone on Mar 29, 2017
  8. cjerdonek commented on Jul 5, 2019

    @cjerdonek
    Member

    I found that the following pattern can be used as a workaround in cases where you want to set a failed import to None on a failed import and still have the revealed type be Union[_importlib_modulespec.ModuleType, None] afterwards:

    try:
        from pip._vendor import colorama as _colorama
     except Exception:
        colorama = None
    else:
        colorama = _colorama

    This is to avoid the following error in the except block:

    Incompatible types in assignment (expression has type "None", variable has type Module)

  9. deleted a comment from ebukaoguchi on Oct 4, 2019
  10. deleted a comment from ebukaoguchi on Oct 4, 2019
  11. emmatyping commented on Dec 24, 2019

    @emmatyping
    Member

    As a (more) general solution to this problem, perhaps we could treat blocks like:

    try:
        from foo import A
    except ImportError:
        A = None

    as A = TypeVar('A', foo.A, None)

    and

    try:
        from foo import A
    except ImportError:
        from bar import A # ( or even from bar import AA as A)

    as A = TypeVar('A', foo.A, bar.A)

    And so on.

    This is nice because you can do things like

    from typing import TypeVar
    
    
    class A:
        def __add__(self, a: A) -> str:
            ...
            
    class AA:
        def __add__(self, a: AA) -> str:
            ...
            
    T = TypeVar('T', A, AA)
    
    def add(a: T, b: T) -> T:
        return a + b

    if A overrides __add__ and the like.

    This would lead to two issues:

    1. isinstance/issubclass would be broken, this should be easy to fix, as we just need to silence the warning for the special form (the type var would only exist in mypy)

    2. This would lead to many more unsolved type variables (which currently will result in Anys). Perhaps we can have unsolved but bounded type variables generate more Unions of their bounds?

    For example, if you call the add function above with two variables of type T, you will get an Any, which is clearly not desirable, but if we change things so that you get Union[A, AA], this may be possible?

    Hopefully this suggestion makes sense, let me know if there are questions or holes I am missing.

  12. JukkaL commented on Jan 6, 2020

    @JukkaL
    CollaboratorAuthor

    An interesting idea! Treating conditionally imported types as type variables probably wouldn't work in some basic use cases, unfortunately. Here are some things that could cause trouble:

    • Using an unbound type variable as the type of a variable is not supported.
    • A base class such as List[A] would make the class a generic class, which could be surprising.
    • Various error messages related to type variable could be confusing.
    • Having a type variable as the return type (say, in def f() -> A: ...) would result in unresolved type variables on most call sites, as you pointed out.
    • Fairly often a conditional import would be used for Python 2/3 compatibility, and only one of the target modules will exist on the target Python version.

    Some of these could be improved/fixed, but there may be other problematic things that we don't know about yet.

  13. ilevkivskyi commented on Jan 25, 2020

    @ilevkivskyi
    Member

    Another example is #8319 (closing it as a duplicate).

  14. vpogrebi commented on May 1, 2020

    @vpogrebi

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

  15. JukkaL commented on May 1, 2020

    @JukkaL
    CollaboratorAuthor

    @vpogrebi This looks a little different from the other issues discussed here. Can you open a separate issue about conditionally defined __all__? Here the correct fix may be easier (don't infer a fixed-length tuple type for __all__ as an ad hoc fix).

  16. vpogrebi commented on May 1, 2020

    @vpogrebi

    New issue #8758 has been created.

  17. rodrigc commented on May 8, 2020

    @rodrigc

    @JukkaL While introducing mypy to the CI for the Twisted codebase, I ran into this problem which is similar to the examples mentioned in this issue, similar to the one brought up by @ethanhs:

    twisted/twisted#1261 (comment)

    What is the way to deal with this:

    try:
        from foo import A
    except ImportError:
        A = None
    

    Is there something that needs to be fixed in mypy?
    For now, I just put:

    A = None # type:ignore

    to turn off mypy errors on that line. As much as possible, I want to avoid turning off mypy like that.

    The other option is to follow the example mentioned by @cjerdonek here: #1297 (comment)

    and do:

    try:
       from foo import A as _A
    except ImportError:
       A = None
    else:
       A = _A
    
  18. added a commit that references this issue on Jun 8, 2022
  19. adam-grant-hendry commented on Jun 12, 2022

    @adam-grant-hendry

    I found that the following pattern can be used as a workaround in cases where you want to set a failed import to None on a failed import and still have the revealed type be Union[_importlib_modulespec.ModuleType, None] afterwards:

    try:
        from pip._vendor import colorama as _colorama
     except Exception:
        colorama = None
    else:
        colorama = _colorama

    This is to avoid the following error in the except block:

    Incompatible types in assignment (expression has type "None", variable has type Module)

    Interesting. Adam Johnson tried something similar, which fails in this gist. I've tried yours in this gist and it does work.

    Nice workaround.

  20. hauntsaninja commented on Aug 7, 2022

    @hauntsaninja
    Collaborator

    Closing, see here: #1153 (comment)

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions