Repository: python/mypy

Issue:
"has no attribute" then suggests another missing attribute
Output in a mypy run:
```
vaults.py:14: error: Module 'datatypes' has no attribute 'VaultDataResponse'; maybe "VaultSummaryResponse"?  [attr-defined]
vaults.py:14: error: Module 'datatypes' has no attribute 'VaultSummaryResponse'; maybe "VaultDataResponse"?  [attr-defined]
```

Hopefully the error in the above output is self explanatory, it's contradicting itself.


Discussion:
Note that the attributes do exist, but I'm running with the `--no-implicit-reexport` option. So, it looks like the suggestions are generated without that option or something.

In this case, as the variable does exist, the suggestion should probably indicate something about implicit reexport, rather than another variable.
Yeah, this is a bug. Would you like to contribute a fix -- it should be pretty easy? You can look at the implementation of `--no-implicit-reexport` for context (#6562).
I can probably look at it in a few weeks.

Resolving patch:
diff --git a/mypy/semanal.py b/mypy/semanal.py
--- a/mypy/semanal.py
+++ b/mypy/semanal.py
@@ -1802,11 +1802,15 @@ def report_missing_module_attribute(self, import_id: str, source_id: str, import
         # Suggest alternatives, if any match is found.
         module = self.modules.get(import_id)
         if module:
-            alternatives = set(module.names.keys()).difference({source_id})
-            matches = best_matches(source_id, alternatives)[:3]
-            if matches:
-                suggestion = "; maybe {}?".format(pretty_seq(matches, "or"))
-                message += "{}".format(suggestion)
+            if not self.options.implicit_reexport and source_id in module.names.keys():
+                message = ("Module '{}' does not explicitly export attribute '{}'"
+                           "; implicit reexport disabled".format(import_id, source_id))
+            else:
+                alternatives = set(module.names.keys()).difference({source_id})
+                matches = best_matches(source_id, alternatives)[:3]
+                if matches:
+                    suggestion = "; maybe {}?".format(pretty_seq(matches, "or"))
+                    message += "{}".format(suggestion)
         self.fail(message, context, code=codes.ATTR_DEFINED)
         self.add_unknown_imported_symbol(imported_id, context)
 


Test patch:
diff --git a/test-data/unit/check-flags.test b/test-data/unit/check-flags.test
--- a/test-data/unit/check-flags.test
+++ b/test-data/unit/check-flags.test
@@ -1210,7 +1210,7 @@ a = 5
 [file other_module_2.py]
 from other_module_1 import a
 [out]
-main:2: error: Module 'other_module_2' has no attribute 'a'
+main:2: error: Module 'other_module_2' does not explicitly export attribute 'a'; implicit reexport disabled
 
 [case testNoImplicitReexportRespectsAll]
 # flags: --no-implicit-reexport
@@ -1224,7 +1224,7 @@ from other_module_1 import a, b
 __all__ = ('b',)
 [builtins fixtures/tuple.pyi]
 [out]
-main:2: error: Module 'other_module_2' has no attribute 'a'
+main:2: error: Module 'other_module_2' does not explicitly export attribute 'a'; implicit reexport disabled
 
 [case testNoImplicitReexportStarConsideredImplicit]
 # flags: --no-implicit-reexport
@@ -1234,7 +1234,7 @@ a = 5
 [file other_module_2.py]
 from other_module_1 import *
 [out]
-main:2: error: Module 'other_module_2' has no attribute 'a'
+main:2: error: Module 'other_module_2' does not explicitly export attribute 'a'; implicit reexport disabled
 
 [case testNoImplicitReexportStarCanBeReexportedWithAll]
 # flags: --no-implicit-reexport
@@ -1248,7 +1248,19 @@ from other_module_1 import *
 __all__ = ('b',)
 [builtins fixtures/tuple.pyi]
 [out]
-main:2: error: Module 'other_module_2' has no attribute 'a'
+main:2: error: Module 'other_module_2' does not explicitly export attribute 'a'; implicit reexport disabled
+
+[case textNoImplicitReexportSuggestions]
+# flags: --no-implicit-reexport
+from other_module_2 import attr_1
+
+[file other_module_1.py]
+attr_1 = 5
+attr_2 = 6
+[file other_module_2.py]
+from other_module_1 import attr_1, attr_2
+[out]
+main:2: error: Module 'other_module_2' does not explicitly export attribute 'attr_1'; implicit reexport disabled
 
 [case testNoImplicitReexportMypyIni]
 # flags: --config-file tmp/mypy.ini