Index: Lib/site.py =================================================================== --- Lib/site.py (revision 87478) +++ Lib/site.py (working copy) @@ -55,6 +55,7 @@ import sys import os import builtins +import traceback # Prefixes for site-packages; add additional prefixes like /usr/local here PREFIXES = [sys.prefix, sys.exec_prefix] @@ -141,17 +142,26 @@ except IOError: return with f: - for line in f: + for n, line in enumerate(f): if line.startswith("#"): continue - if line.startswith(("import ", "import\t")): - exec(line) - continue - line = line.rstrip() - dir, dircase = makepath(sitedir, line) - if not dircase in known_paths and os.path.exists(dir): - sys.path.append(dir) - known_paths.add(dircase) + try: + if line.startswith(("import ", "import\t")): + exec(line) + continue + line = line.rstrip() + dir, dircase = makepath(sitedir, line) + if not dircase in known_paths and os.path.exists(dir): + sys.path.append(dir) + known_paths.add(dircase) + except Exception as err: + print("Error processing line {:d} of {}:\n".format(n+1, fullname), + file=sys.stderr) + for record in traceback.format_exception(*sys.exc_info()): + for line in record.splitlines(): + print(' '+line, file=sys.stderr) + print("\nRemainder of file ignored", file=sys.stderr) + break if reset: known_paths = None return known_paths Index: Lib/test/test_site.py =================================================================== --- Lib/test/test_site.py (revision 87478) +++ Lib/test/test_site.py (working copy) @@ -6,6 +6,7 @@ """ import unittest from test.support import run_unittest, TESTFN, EnvironmentVarGuard +from test.support import captured_stderr import builtins import os import sys @@ -90,6 +91,53 @@ finally: pth_file.cleanup() + def make_pth(self, contents, pth_dir='.', pth_name=TESTFN): + # Create a .pth file and return its (abspath, basename). + pth_dir = os.path.abspath(pth_dir) + pth_basename = pth_name + '.pth' + pth_fn = os.path.join(pth_dir, pth_basename) + pth_file = open(pth_fn, 'w', encoding='utf-8') + self.addCleanup(lambda: os.remove(pth_fn)) + pth_file.write(contents) + pth_file.close() + return pth_dir, pth_basename + + def test_addpackage_import_bad_syntax(self): + # Issue 10642 + pth_dir, pth_fn = self.make_pth("import bad)syntax\n") + with captured_stderr() as err_out: + site.addpackage(pth_dir, pth_fn, set()) + self.assertRegex(err_out.getvalue(), "line 1") + self.assertRegex(err_out.getvalue(), os.path.join(pth_dir, pth_fn)) + # XXX: the previous two should be independent checks so that the + # order doesn't matter. The next two could be a single check + # but my regex foo isn't good enough to write it. + self.assertRegex(err_out.getvalue(), 'Traceback') + self.assertRegex(err_out.getvalue(), r'import bad\)syntax') + self.assertRegex(err_out.getvalue(), 'SyntaxError') + + def test_addpackage_import_bad_exec(self): + # Issue 10642 + pth_dir, pth_fn = self.make_pth("randompath\nimport nosuchmodule\n") + with captured_stderr() as err_out: + site.addpackage(pth_dir, pth_fn, set()) + self.assertRegex(err_out.getvalue(), "line 2") + self.assertRegex(err_out.getvalue(), os.path.join(pth_dir, pth_fn)) + # XXX: ditto previous XXX comment. + self.assertRegex(err_out.getvalue(), 'Traceback') + self.assertRegex(err_out.getvalue(), 'ImportError') + + def test_addpackage_import_bad_pth_file(self): + # Issue 5258 + pth_dir, pth_fn = self.make_pth("abc\x00def\n") + with captured_stderr() as err_out: + site.addpackage(pth_dir, pth_fn, set()) + self.assertRegex(err_out.getvalue(), "line 1") + self.assertRegex(err_out.getvalue(), os.path.join(pth_dir, pth_fn)) + # XXX: ditto previous XXX comment. + self.assertRegex(err_out.getvalue(), 'Traceback') + self.assertRegex(err_out.getvalue(), 'TypeError') + def test_addsitedir(self): # Same tests for test_addpackage since addsitedir() essentially just # calls addpackage() for every .pth file in the directory