# HG changeset patch # Parent 9994e0172a0c023475e903f87d59aea4d7dbf1e8 diff -r 9994e0172a0c Lib/runpy.py --- a/Lib/runpy.py Tue Mar 10 22:35:24 2015 +0100 +++ b/Lib/runpy.py Wed Mar 11 08:22:00 2015 +0000 @@ -152,16 +152,9 @@ else: # i.e. directory or zipfile execution mod_name, mod_spec, code = _get_main_module_details() except ImportError as exc: - # Try to provide a good error message - # for directories, zip files and the -m switch - if alter_argv: - # For -m switch, just display the exception - info = str(exc) - else: - # For directories/zipfiles, let the user - # know what the code was looking for - info = "can't find '__main__' module in %r" % sys.argv[0] - msg = "%s: %s" % (sys.executable, info) + # TODO: This incorrectly suppresses traceback reporting when the + # exception was raised by user code in __init__.py; see Issue 14285 + msg = "%s: %s" % (sys.executable, exc) sys.exit(msg) main_globals = sys.modules["__main__"].__dict__ if alter_argv: diff -r 9994e0172a0c Lib/test/test_cmd_line_script.py --- a/Lib/test/test_cmd_line_script.py Tue Mar 10 22:35:24 2015 +0100 +++ b/Lib/test/test_cmd_line_script.py Wed Mar 11 08:22:00 2015 +0000 @@ -402,16 +402,70 @@ # If a module is invoked with the -m command line flag # and results in an error that the return code to the # shell is '1' - with temp_dir() as script_dir: - with support.change_cwd(path=script_dir): - pkg_dir = os.path.join(script_dir, 'test_pkg') - make_pkg(pkg_dir) - script_name = _make_test_script(pkg_dir, 'other', - "if __name__ == '__main__': raise ValueError") - rc, out, err = assert_python_failure('-m', 'test_pkg.other', *example_args) - if verbose > 1: - print(out) + with self.setup_test_pkg() as pkg_dir: + script_name = _make_test_script(pkg_dir, 'other', + "if __name__ == '__main__': raise ValueError") + err = self.dash_m_failure_test('test_pkg.other', *example_args) + self.assertIn(b'ValueError', err) + + def test_dash_m_errors(self): + # Exercise error reporting for various invalid package executions + tests = ( + ('builtins', br'No code object available'), + ('builtins.x', br'Error while finding spec.*AttributeError'), + ('builtins.x.y', br'Error while finding spec.*' + br'ImportError.*No module named.*not a package'), + ('os.path', br'loader cannot handle'), + ('importlib', br'No module named.*' + br'is a package and cannot be directly executed'), + ('importlib.nonexistant', br'No module named'), + ) + for name, regex in tests: + with self.subTest(name): + rc, _, err = assert_python_failure('-m', name) self.assertEqual(rc, 1) + self.assertRegex(err, regex) + self.assertNotIn(b'Traceback', err) + + @unittest.expectedFailure + def test_dash_m_init_traceback(self): + # These are wrapped in an ImportError and tracebacks are suppressed; + # see Issue 14285 + exceptions = (ImportError, AttributeError, TypeError, ValueError) + for exception in exceptions: + exception = exception.__name__ + init = "raise {0}('Exception in __init__.py')".format(exception) + with self.subTest(exception), \ + self.setup_test_pkg(init) as pkg_dir: + err = self.dash_m_failure_test('test_pkg') + self.assertIn(exception.encode('ascii'), err) + self.assertIn(b'Exception in __init__.py', err) + self.assertIn(b'Traceback', err) + + def test_dash_m_main_traceback(self): + # Ensure that an ImportError's traceback is reported + with self.setup_test_pkg() as pkg_dir: + main = "raise ImportError('Exception in __main__ module')" + _make_test_script(pkg_dir, '__main__', main) + err = self.dash_m_failure_test('test_pkg') + self.assertIn(b'ImportError', err) + self.assertIn(b'Exception in __main__ module', err) + self.assertIn(b'Traceback', err) + + @contextlib.contextmanager + def setup_test_pkg(self, *args): + with temp_dir() as script_dir, \ + support.change_cwd(path=script_dir): + pkg_dir = os.path.join(script_dir, 'test_pkg') + make_pkg(pkg_dir, *args) + yield pkg_dir + + def dash_m_failure_test(self, *args): + rc, out, err = assert_python_failure('-m', *args, __isolated=False) + if verbose > 1: + print(out) + self.assertEqual(rc, 1) + return err def test_pep_409_verbiage(self): # Make sure PEP 409 syntax properly suppresses