Skip to content

A 'type only' module import should not count as importing a module #11503

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

Open
KotlinIsland opened this issue Nov 9, 2021 · 3 comments
Open
Labels
bug mypy got something wrong

Comments

@KotlinIsland
Copy link
Contributor

KotlinIsland commented Nov 9, 2021

given:

my-app/
├─ package/
│  ├─ __init__.py
│  ├─ things.py
├─ entry.py
├─ mod.py

entry.py:

import mod
import package

mod.func(package.things.Thing())

mod.py:

def func(it):
    ...

In typeshed/bundled stubs: mod.pyi

from package.things import Thing

def func(it: Thing) -> None: ...
OR, if this is fake imported in the .py module: mod.py
from __future__ import annotations
from typing import TYPE_CHECKING
if TYPE_CHECKING
    # do a type import so doesn't cause circular imports or slow down loading
    from package.things import Thing

def func(it: Thing) -> None:
    ...

package.things.py

class Thing:
    ...

When mypy --strict entry.py is executed, no error is generated.
When python entry.py is run: AttributeError: module 'package' has no attribute 'things'

Mypy treats the 'type import' as importing package.things, when in reality it isn't. I understand this is consistent with what TYPE_CHECKING should do, but seems really sus if you ask me.

This more seriously affects typeshed, where all imports are fake and mypy will think all fakely imported modules are really imported.

@KotlinIsland KotlinIsland added the bug mypy got something wrong label Nov 9, 2021
@KotlinIsland KotlinIsland changed the title a 'type import' should not count as an imported module a 'type import' should not count as importing a module Nov 9, 2021
@KotlinIsland
Copy link
Contributor Author

like, in a stub there is no way so specify if a module is actually imported at runtime, or if you are just trying to access a type for type time.

@erictraut
Copy link

The TYPE_CHECKING symbol allows you to "lie" to the type checker, effectively making it see something different from the interpreter at runtime. I warn users that they use TYPE_CHECKING at their own peril because these differences can lead to inconsistencies. Your sample above is one such example but there are many other similar patterns. My recommendation is to avoid using TYPE_CHECKING if at all possible. For example, using it to avoid circular imports is a really bad justification, IMO. The better approach is to refactor your code to avoid the circular dependency.

In a stub, there is a way to specify that the symbol is exported at runtime. PEP 484 documents a mechanism for this. You do so by using a redundant alias form of import, such as from package.things import Thing as Thing.

@KotlinIsland
Copy link
Contributor Author

KotlinIsland commented Nov 9, 2021

@erictraut I understand those limitations and agree, it was more just to explain the issue. (although an import type would be very appreciated)

You haven't got the issue with stubs quite right though.

The issue isn't that Thing is exported or not, look at the example, It's not referencing the Thing from mod it's accessing it from package.things.

The issue is that mypy thinks package.things is imported(because the stub says from package.things import Thing), when it isn't, leading to AttributeError: module 'package' has no attribute 'things'

There is no way to specify in a stub that your import is reflected or not at runtime.

@KotlinIsland KotlinIsland changed the title a 'type import' should not count as importing a module A 'type only' module import should not count as importing a module Nov 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug mypy got something wrong
Projects
None yet
Development

No branches or pull requests

2 participants