[Python-checkins] r50803 - in python/trunk: Lib/test/test_traceback.py Lib/traceback.py Misc/NEWS

georg.brandl python-checkins at python.org
Mon Jul 24 16:09:57 CEST 2006


Author: georg.brandl
Date: Mon Jul 24 16:09:56 2006
New Revision: 50803

Modified:
   python/trunk/Lib/test/test_traceback.py
   python/trunk/Lib/traceback.py
   python/trunk/Misc/NEWS
Log:
Patch #1515343: Fix printing of deprecated string exceptions with a
value in the traceback module.


Modified: python/trunk/Lib/test/test_traceback.py
==============================================================================
--- python/trunk/Lib/test/test_traceback.py	(original)
+++ python/trunk/Lib/test/test_traceback.py	Mon Jul 24 16:09:56 2006
@@ -31,8 +31,9 @@
         err = self.get_exception_format(self.syntax_error_with_caret,
                                         SyntaxError)
         self.assert_(len(err) == 4)
-        self.assert_("^" in err[2]) # third line has caret
         self.assert_(err[1].strip() == "return x!")
+        self.assert_("^" in err[2]) # third line has caret
+        self.assert_(err[1].find("!") == err[2].find("^")) # in the right place
 
     def test_nocaret(self):
         if is_jython:
@@ -47,8 +48,9 @@
         err = self.get_exception_format(self.syntax_error_bad_indentation,
                                         IndentationError)
         self.assert_(len(err) == 4)
-        self.assert_("^" in err[2])
         self.assert_(err[1].strip() == "print 2")
+        self.assert_("^" in err[2])
+        self.assert_(err[1].find("2") == err[2].find("^"))
 
     def test_bug737473(self):
         import sys, os, tempfile, time
@@ -109,6 +111,36 @@
         lst = traceback.format_exception_only(e.__class__, e)
         self.assertEqual(lst, ['KeyboardInterrupt\n'])
 
+    # String exceptions are deprecated, but legal.  The quirky form with
+    # separate "type" and "value" tends to break things, because 
+    #     not isinstance(value, type)
+    # and a string cannot be the first argument to issubclass.
+    #
+    # Note that sys.last_type and sys.last_value do not get set if an
+    # exception is caught, so we sort of cheat and just emulate them.
+    #
+    # test_string_exception1 is equivalent to
+    #
+    # >>> raise "String Exception"
+    #
+    # test_string_exception2 is equivalent to
+    #
+    # >>> raise "String Exception", "String Value"
+    #
+    def test_string_exception1(self):
+        str_type = "String Exception"
+        err = traceback.format_exception_only(str_type, None)
+        self.assert_(len(err) == 1)
+        self.assert_(err[0] == str_type + '\n')
+
+    def test_string_exception2(self):
+        str_type = "String Exception"
+        str_value = "String Value"
+        err = traceback.format_exception_only(str_type, str_value)
+        self.assert_(len(err) == 1)
+        self.assert_(err[0] == str_type + ': ' + str_value + '\n')
+        
+
 def test_main():
     run_unittest(TracebackCases)
 

Modified: python/trunk/Lib/traceback.py
==============================================================================
--- python/trunk/Lib/traceback.py	(original)
+++ python/trunk/Lib/traceback.py	Mon Jul 24 16:09:56 2006
@@ -150,51 +150,63 @@
 
     The arguments are the exception type and value such as given by
     sys.last_type and sys.last_value. The return value is a list of
-    strings, each ending in a newline.  Normally, the list contains a
-    single string; however, for SyntaxError exceptions, it contains
-    several lines that (when printed) display detailed information
-    about where the syntax error occurred.  The message indicating
-    which exception occurred is the always last string in the list.
+    strings, each ending in a newline.
+
+    Normally, the list contains a single string; however, for
+    SyntaxError exceptions, it contains several lines that (when
+    printed) display detailed information about where the syntax
+    error occurred.
+
+    The message indicating which exception occurred is always the last
+    string in the list.
+
     """
-    list = []
-    if (type(etype) == types.ClassType
-        or (isinstance(etype, type) and issubclass(etype, BaseException))):
-        stype = etype.__name__
+
+    # An instance should not have a meaningful value parameter, but
+    # sometimes does, particularly for string exceptions, such as
+    # >>> raise string1, string2  # deprecated
+    #
+    # Clear these out first because issubtype(string1, SyntaxError)
+    # would throw another exception and mask the original problem.
+    if (isinstance(etype, BaseException) or
+        isinstance(etype, types.InstanceType) or
+        type(etype) is str):
+        return [_format_final_exc_line(etype, value)]
+        
+    stype = etype.__name__
+
+    if not issubclass(etype, SyntaxError):
+        return [_format_final_exc_line(stype, value)]
+
+    # It was a syntax error; show exactly where the problem was found.
+    try:
+        msg, (filename, lineno, offset, badline) = value
+    except Exception:
+        pass
     else:
-        stype = etype
-    if value is None:
-        list.append(str(stype) + '\n')
+        filename = filename or "<string>"
+        lines = [('  File "%s", line %d\n' % (filename, lineno))]
+        if badline is not None:
+            lines.append('    %s\n' % badline.strip())
+            if offset is not None:
+                caretspace = badline[:offset].lstrip()
+                # non-space whitespace (likes tabs) must be kept for alignment
+                caretspace = ((c.isspace() and c or ' ') for c in caretspace)
+                # only three spaces to account for offset1 == pos 0
+                lines.append('   %s^\n' % ''.join(caretspace))
+            value = msg
+            
+    lines.append(_format_final_exc_line(stype, value))
+    return lines
+
+def _format_final_exc_line(etype, value):
+    """Return a list of a single line -- normal case for format_exception_only"""
+    if value is None or not str(value):   
+        line = "%s\n" % etype
     else:
-        if issubclass(etype, SyntaxError):
-            try:
-                msg, (filename, lineno, offset, line) = value
-            except:
-                pass
-            else:
-                if not filename: filename = "<string>"
-                list.append('  File "%s", line %d\n' %
-                            (filename, lineno))
-                if line is not None:
-                    i = 0
-                    while i < len(line) and line[i].isspace():
-                        i = i+1
-                    list.append('    %s\n' % line.strip())
-                    if offset is not None:
-                        s = '    '
-                        for c in line[i:offset-1]:
-                            if c.isspace():
-                                s = s + c
-                            else:
-                                s = s + ' '
-                        list.append('%s^\n' % s)
-                    value = msg
-        s = _some_str(value)
-        if s:
-            list.append('%s: %s\n' % (str(stype), s))
-        else:
-            list.append('%s\n' % str(stype))
-    return list
-
+        line = "%s: %s\n" % (etype, _some_str(value))
+    return line
+    
 def _some_str(value):
     try:
         return str(value)

Modified: python/trunk/Misc/NEWS
==============================================================================
--- python/trunk/Misc/NEWS	(original)
+++ python/trunk/Misc/NEWS	Mon Jul 24 16:09:56 2006
@@ -39,6 +39,9 @@
 Library
 -------
 
+- Patch #1515343: Fix printing of deprecated string exceptions with a
+  value in the traceback module.
+
 - Resync optparse with Optik 1.5.3: minor tweaks for/to tests.
 
 - Patch #1524429: Use repr() instead of backticks in Tkinter again.


More information about the Python-checkins mailing list