#5258/#10642: print fn, line, traceback and continue when .pth file is broken
If a .pth file contained an error, it could cause a traceback in site.py, terminating its processing. In 2.7 and 3.2, the interpreter will then not start. Previously, a message would print saying to use -v to get the traceback. In either case, the traceback generated for a failed .pth file did not include the .pth filename, making it difficult to debug the problem. Now site.py reports not only the .pth filename but also the line number causing the error, and just skips the remainder of the file.
This commit is contained in:
parent
e6f1e435d4
commit
b4ca59b783
12
Lib/site.py
12
Lib/site.py
|
@ -55,6 +55,7 @@ ImportError exception, it is silently ignored.
|
|||
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,9 +142,10 @@ def addpackage(sitedir, name, known_paths):
|
|||
except IOError:
|
||||
return
|
||||
with f:
|
||||
for line in f:
|
||||
for n, line in enumerate(f):
|
||||
if line.startswith("#"):
|
||||
continue
|
||||
try:
|
||||
if line.startswith(("import ", "import\t")):
|
||||
exec(line)
|
||||
continue
|
||||
|
@ -152,6 +154,14 @@ def addpackage(sitedir, name, known_paths):
|
|||
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
|
||||
|
|
|
@ -6,6 +6,7 @@ executing have not been removed.
|
|||
"""
|
||||
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 @@ class HelperFunctionsTests(unittest.TestCase):
|
|||
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 three 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
|
||||
|
|
|
@ -11,6 +11,10 @@ Core and Builtins
|
|||
Library
|
||||
-------
|
||||
|
||||
- Issue #5258/#10642: if site.py encounters a .pth file that generates an error,
|
||||
it now prints the filename, line number, and traceback to stderr and skips
|
||||
the rest of that individual file, instead of stopping processing entirely.
|
||||
|
||||
- Issue #10763: subprocess.communicate() closes stdout and stderr if both are
|
||||
pipes (bug specific to Windows).
|
||||
|
||||
|
|
Loading…
Reference in New Issue