diff -r f8eabfed9a1d Doc/library/cgitb.rst --- a/Doc/library/cgitb.rst Tue Aug 21 01:08:17 2012 +0200 +++ b/Doc/library/cgitb.rst Tue Aug 21 14:32:04 2012 +1000 @@ -38,16 +38,25 @@ .. index:: single: excepthook() (in module sys) This function causes the :mod:`cgitb` module to take over the interpreter's - default handling for exceptions by setting the value of :attr:`sys.excepthook`. + default handling for exceptions by setting the value of + :attr:`sys.excepthook`. - The optional argument *display* defaults to ``1`` and can be set to ``0`` to - suppress sending the traceback to the browser. If the argument *logdir* is - present, the traceback reports are written to files. The value of *logdir* - should be a directory where these files will be placed. The optional argument - *context* is the number of lines of context to display around the current line - of source code in the traceback; this defaults to ``5``. If the optional - argument *format* is ``"html"``, the output is formatted as HTML. Any other - value forces plain text output. The default value is ``"html"``. + The optional argument *display* defaults to ``1`` and can be set to ``0`` + to suppress sending the traceback to the browser. A message single line + message declaring that "A problem has occurred ..." will continue to be + generated. + + If the argument *logdir* is present, the traceback reports are written to + files. The value of *logdir* should be a directory where these files will + be placed. + + The optional argument *context* is the number of lines of context to + display around the current line of source code in the traceback; this + defaults to ``5``. + + If the optional argument *format* is ``"html"``, the output is formatted as + HTML. Any other value forces plain text output. The default value is + ``"html"``. .. function:: handler(info=None) diff -r f8eabfed9a1d Lib/cgitb.py --- a/Lib/cgitb.py Tue Aug 21 01:08:17 2012 +0200 +++ b/Lib/cgitb.py Tue Aug 21 14:32:04 2012 +1000 @@ -269,11 +269,14 @@ def handle(self, info=None): info = info or sys.exc_info() + + # always reset browser to a known state if rendering html if self.format == "html": self.file.write(reset()) - formatter = (self.format=="html") and html or text + formatter = html if self.format == "html" else text plain = False + prefix = "

" if self.format == "html" else "" try: doc = formatter(info, self.context) except: # just in case something goes wrong @@ -287,18 +290,20 @@ else: self.file.write(doc + '\n') else: - self.file.write('

A problem occurred in a Python script.\n') + self.file.write(prefix + 'A problem occurred in a Python script.\n') if self.logdir is not None: - suffix = ['.txt', '.html'][self.format=="html"] + suffix = '.html' if self.format == "html" else '.txt' (fd, path) = tempfile.mkstemp(suffix=suffix, dir=self.logdir) try: file = os.fdopen(fd, 'w') file.write(doc) file.close() - msg = '

%s contains the description of this error.' % path + msg = '%s %s contains the description of this error.' % ( + prefix, path) except: - msg = '

Tried to save traceback to %s, but failed.' % path + msg = '%s Tried to save traceback to %s, but failed.' % ( + prefix, path) self.file.write(msg + '\n') try: self.file.flush() diff -r f8eabfed9a1d Lib/test/test_cgitb.py --- a/Lib/test/test_cgitb.py Tue Aug 21 01:08:17 2012 +0200 +++ b/Lib/test/test_cgitb.py Tue Aug 21 14:32:04 2012 +1000 @@ -3,9 +3,22 @@ import sys import subprocess import cgitb +from contextlib import ExitStack +from unittest import mock class TestCgitb(unittest.TestCase): + def mock_sys(self): + "Mock system environment for cgitb" + # use exit stack to match patch context managers to addCleanup + stack = ExitStack() + self.addCleanup(stack.close) + self.stdout = stack.enter_context(mock.patch('cgitb.sys.stdout')) + self.stderr = stack.enter_context(mock.patch('cgitb.sys.stderr')) + prepatch = mock.patch('cgitb.sys', wraps=cgitb.sys, spec=cgitb.sys) + self.sysmod = stack.enter_context(prepatch) + self.sysmod.excepthook = self.sysmod.__excepthook__ + def test_fonts(self): text = "Hello Robbie!" self.assertEqual(cgitb.small(text), "{}".format(text)) @@ -47,6 +60,54 @@ self.assertIn("ValueError", out) self.assertIn("Hello World", out) + def test_text_displayed(self): + self.mock_sys() + cgitb.enable(display=1, format="text") + try: + raise ValueError("Just a little scratch!") + except: + cgitb.sys.excepthook(*self.sysmod.exc_info()) + self.assertEqual(self.stdout.write.call_count, 1) + written = self.stdout.write.call_args[0][0] + self.assertIn("ValueError", written) + self.assertNotRegex(written, "^

") + + def test_text_suppressed(self): + self.mock_sys() + cgitb.enable(display=0, format="text") + try: + raise ValueError("Just a little scratch!") + except: + cgitb.sys.excepthook(*self.sysmod.exc_info()) + self.assertEqual(self.stdout.write.call_count, 1) + written = self.stdout.write.call_args[0][0] + self.assertNotIn("ValueError", written) + self.assertNotRegex(written, "^

") + + def test_html_displayed(self): + self.mock_sys() + cgitb.enable(display=1, format="html") + try: + raise ValueError("Just a little scratch!") + except: + cgitb.sys.excepthook(*self.sysmod.exc_info()) + self.assertGreater(self.stdout.write.call_count, 0) + written = self.stdout.write.call_args[0][0] + self.assertIn("ValueError", written) + self.assertRegex(written, "^") + def test_main(): run_unittest(TestCgitb) diff -r f8eabfed9a1d Lib/test/test_sundry.py --- a/Lib/test/test_sundry.py Tue Aug 21 01:08:17 2012 +0200 +++ b/Lib/test/test_sundry.py Tue Aug 21 14:32:04 2012 +1000 @@ -8,7 +8,6 @@ def test_at_least_import_untested_modules(self): with support.check_warnings(quiet=True): import bdb - import cgitb import distutils.bcppcompiler import distutils.ccompiler