From 2565917de82ca3e43ae5ef661a3950295ceac154 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 17 Jun 2018 02:37:05 +0100 Subject: [PATCH 1/3] Fix patching parent modules with missing imports --- mypy/semanal.py | 13 +++++++++++- test-data/unit/check-modules.test | 33 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index 9249547d3593..cdf1e04d22ca 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1335,7 +1335,18 @@ def add_submodules_to_parent_modules(self, id: str, module_public: bool) -> None sym = SymbolTableNode(MODULE_REF, child_mod, module_public=module_public, no_serialize=True) - parent_mod.names[child] = sym + else: + # Construct a dummy Var with Any type. + any_type = AnyType(TypeOfAny.from_unimported_type, + missing_import_name=id) + var = Var(child, any_type) + var._fullname = child + var.is_ready = True + var.is_suppressed_import = True + sym = SymbolTableNode(GDEF, var, + module_public=module_public, + no_serialize=True) + parent_mod.names[child] = sym id = parent def add_module_symbol(self, id: str, as_id: str, module_public: bool, diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index 3e28bd5e9c6e..ee74787f0bad 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -2312,3 +2312,36 @@ def run() -> None: [out] tmp/p/b.py:4: error: Revealed type is 'builtins.int' tmp/p/__init__.py:3: error: Revealed type is 'builtins.int' + +[case testMissingSubmoduleImportedWithIgnoreMissingImports] +# flags: --ignore-missing-imports +import whatever.works +import a.b + +x = whatever.works.f() +y = a.b.f() +[file a/__init__.py] +# empty +[out] + +[case testMissingSubmoduleImportedWithIgnoreMissingImportsStub] +# flags: --ignore-missing-imports --follow-imports=skip +import whatever.works +import a.b + +x = whatever.works.f() +y = a.b.f() +[file a/__init__.pyi] +# empty +[out] + +[case testMissingSubmoduleImportedWithIgnoreMissingImportsNested] +# flags: --ignore-missing-imports +import a.b.c.d + +y = a.b.c.d.f() +[file a/__init__.py] +# empty +[file a/b/__init__.py] +# empty +[out] From 6f3de976a58c3ea97513979e8484edd79afd0683 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 17 Jun 2018 04:05:17 +0100 Subject: [PATCH 2/3] Fix incremental; update tests --- mypy/semanal.py | 10 +++++++++- test-data/unit/check-modules.test | 3 --- test-data/unit/fine-grained-modules.test | 3 --- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/mypy/semanal.py b/mypy/semanal.py index cdf1e04d22ca..058fc36221d7 100644 --- a/mypy/semanal.py +++ b/mypy/semanal.py @@ -1329,7 +1329,7 @@ def add_submodules_to_parent_modules(self, id: str, module_public: bool) -> None while '.' in id: parent, child = id.rsplit('.', 1) parent_mod = self.modules.get(parent) - if parent_mod and child not in parent_mod.names: + if parent_mod and self.allow_patching(parent_mod, child): child_mod = self.modules.get(id) if child_mod: sym = SymbolTableNode(MODULE_REF, child_mod, @@ -1349,6 +1349,14 @@ def add_submodules_to_parent_modules(self, id: str, module_public: bool) -> None parent_mod.names[child] = sym id = parent + def allow_patching(self, parent_mod: MypyFile, child: str) -> bool: + if child not in parent_mod.names: + return True + node = parent_mod.names[child].node + if isinstance(node, Var) and node.is_suppressed_import: + return True + return False + def add_module_symbol(self, id: str, as_id: str, module_public: bool, context: Context, module_hidden: bool = False) -> None: if id in self.modules: diff --git a/test-data/unit/check-modules.test b/test-data/unit/check-modules.test index ee74787f0bad..a9c6fa0e7523 100644 --- a/test-data/unit/check-modules.test +++ b/test-data/unit/check-modules.test @@ -2317,7 +2317,6 @@ tmp/p/__init__.py:3: error: Revealed type is 'builtins.int' # flags: --ignore-missing-imports import whatever.works import a.b - x = whatever.works.f() y = a.b.f() [file a/__init__.py] @@ -2328,7 +2327,6 @@ y = a.b.f() # flags: --ignore-missing-imports --follow-imports=skip import whatever.works import a.b - x = whatever.works.f() y = a.b.f() [file a/__init__.pyi] @@ -2338,7 +2336,6 @@ y = a.b.f() [case testMissingSubmoduleImportedWithIgnoreMissingImportsNested] # flags: --ignore-missing-imports import a.b.c.d - y = a.b.c.d.f() [file a/__init__.py] # empty diff --git a/test-data/unit/fine-grained-modules.test b/test-data/unit/fine-grained-modules.test index 3506c5636d58..1442a7807d4d 100644 --- a/test-data/unit/fine-grained-modules.test +++ b/test-data/unit/fine-grained-modules.test @@ -221,7 +221,6 @@ main:1: error: Cannot find module named 'p.a' == main:1: error: Cannot find module named 'p.a' main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) -main:2: error: Module has no attribute "a" == main:2: error: Argument 1 to "f" has incompatible type "int"; expected "str" [builtins fixtures/module.pyi] @@ -826,7 +825,6 @@ a.py:2: error: Too many arguments for "g" == a.py:1: error: Cannot find module named 'm.x' a.py:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) -a.py:2: error: Module has no attribute "x" [case testDeletePackage1] import p.a @@ -873,7 +871,6 @@ main:2: error: Argument 1 to "f" has incompatible type "int"; expected "str" == main:1: error: Cannot find module named 'p.a' main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) -main:2: error: Module has no attribute "a" == main:1: error: Cannot find module named 'p' main:1: note: (Perhaps setting MYPYPATH or using the "--ignore-missing-imports" flag would help) From 1e928dd1157e3d1388cf27ce3116b0921e3eba89 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Thu, 21 Jun 2018 16:23:35 +0100 Subject: [PATCH 3/3] Add a docs update --- docs/source/command_line.rst | 15 +++++++++++++++ docs/source/config_file.rst | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/docs/source/command_line.rst b/docs/source/command_line.rst index d4dfad6c28ad..9f074ee6e5f5 100644 --- a/docs/source/command_line.rst +++ b/docs/source/command_line.rst @@ -297,6 +297,21 @@ Here are some more useful flags: - ``--ignore-missing-imports`` suppresses error messages about imports that cannot be resolved (see :ref:`follow-imports` for some examples). + This doesn't suppress errors about missing names in successfully resolved + modules. For example, if one has the following files:: + + package/__init__.py + package/mod.py + + Then mypy will generate the following errors with ``--ignore-missing-imports``: + + .. code-block:: python + + import package.unknown # No error, ignored + x = package.unknown.func() # OK + + from package import unknown # No error, ignored + from package.mod import NonExisting # Error: Module has no attribute 'NonExisting' - ``--no-strict-optional`` disables strict checking of ``Optional[...]`` types and ``None`` values. With this option, mypy doesn't diff --git a/docs/source/config_file.rst b/docs/source/config_file.rst index 82dd7961a4cc..dcd152328693 100644 --- a/docs/source/config_file.rst +++ b/docs/source/config_file.rst @@ -201,7 +201,7 @@ overridden by the pattern sections matching the module name. strict Optional checks. If False, mypy treats ``None`` as compatible with every type. - **Note::** This was False by default + **Note:** This was False by default in mypy versions earlier than 0.600. - ``disallow_any_unimported`` (Boolean, default false) disallows usage of types that come