Repository: python/mypy

Issue:
stubgen -m/-p "Failed to import xx skipping it" because of ModuleNotFoundError
`stubgen -m my_module`
`stubgen -p my_package `
output: "Failed to import my_module/my_package skipping it"  Error without details.
Because the environment actually missing packages which generate ModuleNotFoundError.

mypy 0.701
python3.7 



Discussion:
Could you please give some more details? I am not sure what exactly here is the bug.
@xbian0403 demonstrated this to me. The problem was that the module imports another module that's not installed. When using `stubgen -m` (or `stubgen -p`) it tries to import the module (or package) and this import fails due to the missing dependency. However if you point stubgen to the file (e.g. `stubgen some/dir/my_module.py`) it works, it just ignores the bad import.

We spent some time debugging what was going on -- it would have been quicker if stubgen had given us the text of the exception rather than just saying "Failed to import xx". (Note that `xx` was the name of the module we pointed stubgen at, not name of the dependency that wasn't installed.)
OK, thanks for explanation! Yes, showing the error would be definitely helpful.
Working on this issue

Resolving patch:
diff --git a/mypy/stubgen.py b/mypy/stubgen.py
--- a/mypy/stubgen.py
+++ b/mypy/stubgen.py
@@ -912,10 +912,10 @@ def find_module_paths_using_imports(modules: List[str], packages: List[str],
                 result = find_module_path_and_all_py2(mod, interpreter)
             else:
                 result = find_module_path_and_all_py3(mod)
-        except CantImport:
+        except CantImport as e:
             if not quiet:
                 traceback.print_exc()
-            report_missing(mod)
+            report_missing(mod, e.message)
             continue
         if not result:
             c_modules.append(StubSource(mod))
diff --git a/mypy/stubutil.py b/mypy/stubutil.py
--- a/mypy/stubutil.py
+++ b/mypy/stubutil.py
@@ -14,7 +14,9 @@
 
 
 class CantImport(Exception):
-    pass
+    def __init__(self, module: str, message: str):
+        self.module = module
+        self.message = message
 
 
 def is_c_module(module: ModuleType) -> bool:
@@ -103,8 +105,8 @@ def find_module_path_and_all_py2(module: str,
             "print(mod.__file__); print(json.dumps(getattr(mod, '__all__', None)))") % module
     try:
         output_bytes = subprocess.check_output(cmd_template % code, shell=True)
-    except subprocess.CalledProcessError:
-        raise CantImport(module)
+    except subprocess.CalledProcessError as e:
+        raise CantImport(module, str(e))
     output = output_bytes.decode('ascii').strip().splitlines()
     module_path = output[0]
     if not module_path.endswith(('.py', '.pyc', '.pyo')):
@@ -125,8 +127,8 @@ def find_module_path_and_all_py3(module: str) -> Optional[Tuple[str, Optional[Li
     # TODO: Support custom interpreters.
     try:
         mod = importlib.import_module(module)
-    except Exception:
-        raise CantImport(module)
+    except Exception as e:
+        raise CantImport(module, str(e))
     if is_c_module(mod):
         return None
     module_all = getattr(mod, '__all__', None)
@@ -155,8 +157,10 @@ def generate_guarded(mod: str, target: str,
             print('Created %s' % target)
 
 
-def report_missing(mod: str) -> None:
-    print('Failed to import {}; skipping it'.format(mod))
+def report_missing(mod: str, message: Optional[str] = '') -> None:
+    if message:
+        message = ' with error: ' + message
+    print('Failed to import {}{}; skipping it'.format(mod, message))
 
 
 def fail_missing(mod: str) -> None:


Test patch:
diff --git a/mypy/test/teststubgen.py b/mypy/test/teststubgen.py
--- a/mypy/test/teststubgen.py
+++ b/mypy/test/teststubgen.py
@@ -1,4 +1,5 @@
 import glob
+import io
 import os.path
 import shutil
 import sys
@@ -66,6 +67,21 @@ def test_packages_found(self) -> None:
             finally:
                 os.chdir(current)
 
+    def test_module_not_found(self) -> None:
+        current = os.getcwd()
+        captured_output = io.StringIO()
+        sys.stdout = captured_output
+        with tempfile.TemporaryDirectory() as tmp:
+            try:
+                os.chdir(tmp)
+                self.make_file(tmp, 'mymodule.py', content='import a')
+                opts = parse_options(['-m', 'mymodule'])
+                py_mods, c_mods = collect_build_targets(opts, mypy_options(opts))
+                self.assertRegex(captured_output.getvalue(), "No module named 'a'")
+            finally:
+                sys.stdout = sys.__stdout__
+                os.chdir(current)
+
     def make_file(self, *path: str, content: str = '') -> None:
         file = os.path.join(*path)
         with open(file, 'w') as f: