-
-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
I have a project, which I've reduced to a minimum which shows the same problem. It has 9 modules, excerpts are:
debugging.py:
from typing import *
if TYPE_CHECKING:
from .instr_common import InstrCtx
from .instruct import InstrTab
----------
instr_common.py:
from __future__ import annotations
from .common import *
from . import debugging
class InstrCtx(NamedTuple):
tab : InstrTab
----------------
instruct.py:
from __future__ import annotations
from .instr_merge import *
The other modules, with the above, form an SCC of imports, so all 9 are analyzed together.
Expected Behavior
solver\debugging.py:12:2: error: Module "solver.instruct" has no attribute "InstrTab"
The InstrTab import is an error because the name does not exist in the imported module.
The class def of InstrCtx
should be OK, since the import from common
is importing the same thing.
Actual Behavior
solver\debugging.py:11:2: error: Module "solver.instr_common" has no attribute "InstrCtx"
solver\debugging.py:12:2: error: Module "solver.instruct" has no attribute "InstrTab"
solver\instr_common.py:12:1: error: Name "InstrCtx" already defined (possibly by an import)
Discussion
In some cases, a circular import would be a programming error, as the runtime results could vary depending on which of the modules in the SCC is imported first.
In my case, the import in solver.debugging
is guarded by TYPE_CHECKING
, and so it has no effect at runtime. mypy makes a placeholder for solver.debugging.InstrCtx
, then later this is imported into solver.instr_common
as a Var
object.
Now if the placeholder resulted from a guarded import, then mypy should not consider that InstrCtx
is imported at runtime, and so it should be ignored.
In effect, the import of solver.debugging.InstrCtx
is private. It is not visible to other modules, and is visible within solver.debugging
only for type analysis purposes.
I propose adding the following to semanal.py
to make imported names non-public when MYPY-guarded:
in SemanticAnalyzer.visit_import:
if as_id is not None:
base_id = id
imported_id = as_id
module_public = use_implicit_reexport or id.split(".")[-1] == as_id
else:
base_id = id.split('.')[0]
imported_id = base_id
module_public = use_implicit_reexport
---> if i.is_mypy_only: module_public = False
in SemanticAnalyzer.visit_import_all:
module_public = self.is_stub_file or self.options.implicit_reexport
---> if i.is_mypy_only: module_public = False
in SemanticAnalyzer.visit_import_from:
module_public = use_implicit_reexport or (as_id is not None and id == as_id)
---> if imp.is_mypy_only: module_public = False
Attached is a zip file with:
- All the project modules.
- The config file.
log.txt
from mypy run before making changes.log2.txt
from mypy run after making changes.
Exp3.zip
Your Environment
- Mypy version used: 0.931
- Mypy command-line flags: -v
- Mypy configuration options: see attached
- Python version used: 3.7
- Operating system and version: Windows 10