[Python-checkins] r87497 - in python/branches/py3k: Lib/site.py Lib/test/test_site.py Misc/NEWS

r.david.murray python-checkins at python.org
Sun Dec 26 20:54:29 CET 2010


Author: r.david.murray
Date: Sun Dec 26 20:54:29 2010
New Revision: 87497

Log:
##5258/#10642: print fn, line, traceback and continue when .pth file is broken

Previously site.py would end with a traceback and not complete the
initialization of site-packages, but the interpreter would start.
now site.py does as much initialization as it can, while reporting
more useful information to help debug the problem in the .pth file.


Modified:
   python/branches/py3k/Lib/site.py
   python/branches/py3k/Lib/test/test_site.py
   python/branches/py3k/Misc/NEWS

Modified: python/branches/py3k/Lib/site.py
==============================================================================
--- python/branches/py3k/Lib/site.py	(original)
+++ python/branches/py3k/Lib/site.py	Sun Dec 26 20:54:29 2010
@@ -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

Modified: python/branches/py3k/Lib/test/test_site.py
==============================================================================
--- python/branches/py3k/Lib/test/test_site.py	(original)
+++ python/branches/py3k/Lib/test/test_site.py	Sun Dec 26 20:54:29 2010
@@ -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 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

Modified: python/branches/py3k/Misc/NEWS
==============================================================================
--- python/branches/py3k/Misc/NEWS	(original)
+++ python/branches/py3k/Misc/NEWS	Sun Dec 26 20:54:29 2010
@@ -11,6 +11,10 @@
 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).
 


More information about the Python-checkins mailing list