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](https://github.com/python/mypy/files/8477796/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