Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b4ca59b

Browse files
committed
#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.
1 parent e6f1e43 commit b4ca59b

3 files changed

Lines changed: 71 additions & 9 deletions

File tree

Lib/site.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import sys
5656
import os
5757
import builtins
58+
import traceback
5859

5960
# Prefixes for site-packages; add additional prefixes like /usr/local here
6061
PREFIXES = [sys.prefix, sys.exec_prefix]
@@ -141,17 +142,26 @@ def addpackage(sitedir, name, known_paths):
141142
except IOError:
142143
return
143144
with f:
144-
for line in f:
145+
for n, line in enumerate(f):
145146
if line.startswith("#"):
146147
continue
147-
if line.startswith(("import ", "import\t")):
148-
exec(line)
149-
continue
150-
line = line.rstrip()
151-
dir, dircase = makepath(sitedir, line)
152-
if not dircase in known_paths and os.path.exists(dir):
153-
sys.path.append(dir)
154-
known_paths.add(dircase)
148+
try:
149+
if line.startswith(("import ", "import\t")):
150+
exec(line)
151+
continue
152+
line = line.rstrip()
153+
dir, dircase = makepath(sitedir, line)
154+
if not dircase in known_paths and os.path.exists(dir):
155+
sys.path.append(dir)
156+
known_paths.add(dircase)
157+
except Exception as err:
158+
print("Error processing line {:d} of {}:\n".format(n+1, fullname),
159+
file=sys.stderr)
160+
for record in traceback.format_exception(*sys.exc_info()):
161+
for line in record.splitlines():
162+
print(' '+line, file=sys.stderr)
163+
print("\nRemainder of file ignored", file=sys.stderr)
164+
break
155165
if reset:
156166
known_paths = None
157167
return known_paths

Lib/test/test_site.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"""
77
import unittest
88
from test.support import run_unittest, TESTFN, EnvironmentVarGuard
9+
from test.support import captured_stderr
910
import builtins
1011
import os
1112
import sys
@@ -90,6 +91,53 @@ def test_addpackage(self):
9091
finally:
9192
pth_file.cleanup()
9293

94+
def make_pth(self, contents, pth_dir='.', pth_name=TESTFN):
95+
# Create a .pth file and return its (abspath, basename).
96+
pth_dir = os.path.abspath(pth_dir)
97+
pth_basename = pth_name + '.pth'
98+
pth_fn = os.path.join(pth_dir, pth_basename)
99+
pth_file = open(pth_fn, 'w', encoding='utf-8')
100+
self.addCleanup(lambda: os.remove(pth_fn))
101+
pth_file.write(contents)
102+
pth_file.close()
103+
return pth_dir, pth_basename
104+
105+
def test_addpackage_import_bad_syntax(self):
106+
# Issue 10642
107+
pth_dir, pth_fn = self.make_pth("import bad)syntax\n")
108+
with captured_stderr() as err_out:
109+
site.addpackage(pth_dir, pth_fn, set())
110+
self.assertRegex(err_out.getvalue(), "line 1")
111+
self.assertRegex(err_out.getvalue(), os.path.join(pth_dir, pth_fn))
112+
# XXX: the previous two should be independent checks so that the
113+
# order doesn't matter. The next three could be a single check
114+
# but my regex foo isn't good enough to write it.
115+
self.assertRegex(err_out.getvalue(), 'Traceback')
116+
self.assertRegex(err_out.getvalue(), r'import bad\)syntax')
117+
self.assertRegex(err_out.getvalue(), 'SyntaxError')
118+
119+
def test_addpackage_import_bad_exec(self):
120+
# Issue 10642
121+
pth_dir, pth_fn = self.make_pth("randompath\nimport nosuchmodule\n")
122+
with captured_stderr() as err_out:
123+
site.addpackage(pth_dir, pth_fn, set())
124+
self.assertRegex(err_out.getvalue(), "line 2")
125+
self.assertRegex(err_out.getvalue(), os.path.join(pth_dir, pth_fn))
126+
# XXX: ditto previous XXX comment.
127+
self.assertRegex(err_out.getvalue(), 'Traceback')
128+
self.assertRegex(err_out.getvalue(), 'ImportError')
129+
130+
def test_addpackage_import_bad_pth_file(self):
131+
# Issue 5258
132+
pth_dir, pth_fn = self.make_pth("abc\x00def\n")
133+
with captured_stderr() as err_out:
134+
site.addpackage(pth_dir, pth_fn, set())
135+
self.assertRegex(err_out.getvalue(), "line 1")
136+
self.assertRegex(err_out.getvalue(), os.path.join(pth_dir, pth_fn))
137+
# XXX: ditto previous XXX comment.
138+
self.assertRegex(err_out.getvalue(), 'Traceback')
139+
self.assertRegex(err_out.getvalue(), 'TypeError')
140+
93141
def test_addsitedir(self):
94142
# Same tests for test_addpackage since addsitedir() essentially just
95143
# calls addpackage() for every .pth file in the directory

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ Core and Builtins
1111
Library
1212
-------
1313

14+
- Issue #5258/#10642: if site.py encounters a .pth file that generates an error,
15+
it now prints the filename, line number, and traceback to stderr and skips
16+
the rest of that individual file, instead of stopping processing entirely.
17+
1418
- Issue #10763: subprocess.communicate() closes stdout and stderr if both are
1519
pipes (bug specific to Windows).
1620

0 commit comments

Comments
 (0)