]> granicus.if.org Git - python/commitdiff
#5258/#10642: print fn, line, traceback and continue when .pth file is broken
authorR. David Murray <rdmurray@bitdance.com>
Sun, 26 Dec 2010 19:54:29 +0000 (19:54 +0000)
committerR. David Murray <rdmurray@bitdance.com>
Sun, 26 Dec 2010 19:54:29 +0000 (19:54 +0000)
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.

Lib/site.py
Lib/test/test_site.py
Misc/NEWS

index 9d1084a4a2e0cfd984f7bd6698aa976d34072f75..a2c0becbc1e935a15dcaf4e1921d6ce1d82d149f 100644 (file)
@@ -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,17 +142,26 @@ 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
-            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 94d6699903410a47432ead090713d7918f058d67..0ed4706e7c272ebab17cc15f8aa96089342d2a90 100644 (file)
@@ -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
index 88dd83625ca1671fea7109f7cc54d8b2c9dd21f7..4d86c940e57382239c67fcd3bff231a06c55ff0f 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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).