diff --git a/Lib/cgitb.py b/Lib/cgitb.py --- a/Lib/cgitb.py +++ b/Lib/cgitb.py @@ -267,6 +267,13 @@ class Hook: def __call__(self, etype, evalue, etb): self.handle((etype, evalue, etb)) + def _write(self, msg): + if self.file.errors == 'strict': + # emulate xmlcharrefreplace error handler + encoding = self.file.encoding + msg = msg.encode(encoding, "xmlcharrefreplace").decode(encoding) + self.file.write(msg) + def handle(self, info=None): info = info or sys.exc_info() if self.format == "html": @@ -283,23 +290,22 @@ class Hook: if self.display: if plain: doc = doc.replace('&', '&').replace('<', '<') - self.file.write('
' + doc + '
\n') + self._write('
' + doc + '
\n') else: - self.file.write(doc + '\n') + self._write(doc + '\n') else: - self.file.write('

A problem occurred in a Python script.\n') + self._write('

A problem occurred in a Python script.\n') if self.logdir is not None: suffix = ['.txt', '.html'][self.format=="html"] - (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 + with tempfile.NamedTemporaryFile(suffix=suffix, dir=self.logdir) as tmp: + tmp.write(doc) + tmp.close() + msg = '

%s contains the description of this error.' % path except: - msg = '

Tried to save traceback to %s, but failed.' % path - self.file.write(msg + '\n') + msg = '

Tried to save traceback to a temporary file, but failed.' + self._write(msg + '\n') try: self.file.flush() except: pass diff --git a/Lib/test/test_cgitb.py b/Lib/test/test_cgitb.py --- a/Lib/test/test_cgitb.py +++ b/Lib/test/test_cgitb.py @@ -37,13 +37,13 @@ class TestCgitb(unittest.TestCase): self.assertIn("Hello World", text) def test_hook(self): - proc = subprocess.Popen([sys.executable, '-c', - ('import cgitb;' - 'cgitb.enable();' - 'raise ValueError("Hello World")')], - stdout=subprocess.PIPE) - out = proc.stdout.read().decode(sys.getfilesystemencoding()) - self.addCleanup(proc.stdout.close) + code = ('import cgitb;' + 'cgitb.enable();' + 'raise ValueError("Hello World")') + args = [sys.executable, '-c', code] + with subprocess.Popen(args, stdout=subprocess.PIPE) as proc: + stdout, stderr = proc.communicate() + out = stdout.decode(sys.getfilesystemencoding()) self.assertIn("ValueError", out) self.assertIn("Hello World", out)