Skip to content

Commit 7c83ebf

Browse files
committed
Make the testsuite pass with timestamps turned on.
Tested with `PYTHON_TRACEBACK_TIMESTAMPS=ns` set. First pass. Further review could rework some of these changes. Explicit tests for the new feature have yet to be added.
1 parent 0d83447 commit 7c83ebf

21 files changed

Lines changed: 183 additions & 57 deletions

Lib/doctest.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def _test():
5656
'ELLIPSIS',
5757
'SKIP',
5858
'IGNORE_EXCEPTION_DETAIL',
59+
'IGNORE_EXCEPTION_TIMESTAMPS',
5960
'COMPARISON_FLAGS',
6061
'REPORT_UDIFF',
6162
'REPORT_CDIFF',
@@ -157,13 +158,15 @@ def register_optionflag(name):
157158
ELLIPSIS = register_optionflag('ELLIPSIS')
158159
SKIP = register_optionflag('SKIP')
159160
IGNORE_EXCEPTION_DETAIL = register_optionflag('IGNORE_EXCEPTION_DETAIL')
161+
IGNORE_EXCEPTION_TIMESTAMPS = register_optionflag('IGNORE_EXCEPTION_TIMESTAMPS')
160162

161163
COMPARISON_FLAGS = (DONT_ACCEPT_TRUE_FOR_1 |
162164
DONT_ACCEPT_BLANKLINE |
163165
NORMALIZE_WHITESPACE |
164166
ELLIPSIS |
165167
SKIP |
166-
IGNORE_EXCEPTION_DETAIL)
168+
IGNORE_EXCEPTION_DETAIL |
169+
IGNORE_EXCEPTION_TIMESTAMPS)
167170

168171
REPORT_UDIFF = register_optionflag('REPORT_UDIFF')
169172
REPORT_CDIFF = register_optionflag('REPORT_CDIFF')
@@ -273,7 +276,7 @@ def _exception_traceback(exc_info):
273276
# Get a traceback message.
274277
excout = StringIO()
275278
exc_type, exc_val, exc_tb = exc_info
276-
traceback.print_exception(exc_type, exc_val, exc_tb, file=excout)
279+
traceback.print_exception(exc_type, exc_val, exc_tb, file=excout, no_timestamp=True)
277280
return excout.getvalue()
278281

279282
# Override some StringIO methods.
@@ -1414,7 +1417,7 @@ def __run(self, test, compileflags, out):
14141417

14151418
# The example raised an exception: check if it was expected.
14161419
else:
1417-
formatted_ex = traceback.format_exception_only(*exception[:2])
1420+
formatted_ex = traceback.format_exception_only(*exception[:2], no_timestamp=True)
14181421
if issubclass(exception[0], SyntaxError):
14191422
# SyntaxError / IndentationError is special:
14201423
# we don't care about the carets / suggestions / etc
@@ -1749,6 +1752,15 @@ def check_output(self, want, got, optionflags):
17491752
if got == want:
17501753
return True
17511754

1755+
# This flag removes everything that looks like a timestamp as can
1756+
# be configured to display after exception messages in tracebacks.
1757+
# We're assuming nobody will ever write these in their 'want' docs
1758+
# as the feature is off by default, intended for production use.
1759+
if optionflags & IGNORE_EXCEPTION_TIMESTAMPS:
1760+
got = traceback.strip_exc_timestamps(got)
1761+
if got == want:
1762+
return True
1763+
17521764
# The ELLIPSIS flag says to let the sequence "..." in `want`
17531765
# match any substring in `got`.
17541766
if optionflags & ELLIPSIS:

Lib/idlelib/idle_test/test_grep.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from test.support import captured_stdout
1111
from idlelib.idle_test.mock_tk import Var
1212
import os
13+
from pprint import pformat
1314
import re
1415

1516

@@ -139,7 +140,7 @@ def test_found(self):
139140

140141
pat = '""" !Changing this line will break Test_findfile.test_found!'
141142
lines = self.report(pat)
142-
self.assertEqual(len(lines), 5)
143+
self.assertEqual(len(lines), 5, msg=f"{pformat(lines)}")
143144
self.assertIn(pat, lines[0])
144145
self.assertIn('py: 1:', lines[1]) # line number 1
145146
self.assertIn('2', lines[3]) # hits found 2

Lib/idlelib/idle_test/test_run.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import idlelib
1010
from idlelib.idle_test.mock_idle import Func
1111
from test.support import force_not_colorized
12+
import traceback
1213

1314
idlelib.testing = True # Use {} for executing test user code.
1415

@@ -56,6 +57,7 @@ def test_get_message(self):
5657
except exc:
5758
typ, val, tb = sys.exc_info()
5859
actual = run.get_message_lines(typ, val, tb)[0]
60+
actual = traceback.strip_exc_timestamps(actual)
5961
expect = f'{exc.__name__}: {msg}'
6062
self.assertEqual(actual, expect)
6163

@@ -77,6 +79,7 @@ def test_get_multiple_message(self, mock):
7779
with captured_stderr() as output:
7880
run.print_exception()
7981
actual = output.getvalue()
82+
actual = traceback.strip_exc_timestamps(actual)
8083
self.assertIn(msg1, actual)
8184
self.assertIn(msg2, actual)
8285
subtests += 1

Lib/test/support/__init__.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2849,6 +2849,49 @@ def is_slot_wrapper(name, value):
28492849
yield name, True
28502850

28512851

2852+
@contextlib.contextmanager
2853+
def no_traceback_timestamps():
2854+
import traceback
2855+
from .os_helper import EnvironmentVarGuard
2856+
2857+
with (
2858+
swap_attr(traceback, "_TIMESTAMP_FORMAT", ""),
2859+
EnvironmentVarGuard() as env,
2860+
):
2861+
# This prevents it from being on in child processes.
2862+
env.unset("PYTHON_TRACEBACK_TIMESTAMPS")
2863+
# Silence our other-path pythonrun.c print_exception_message().
2864+
tf = getattr(traceback, "_timestamp_formatter", "Nope!")
2865+
if tf != "Nope!":
2866+
del traceback._timestamp_formatter
2867+
yield
2868+
if tf != "Nope!":
2869+
traceback._timestamp_formatter = tf
2870+
2871+
2872+
def force_no_traceback_timestamps(func):
2873+
"""Callable decorator: Force timestamps on tracebacks to be off."""
2874+
@functools.wraps(func)
2875+
def wrapper(*args, **kwargs):
2876+
with no_traceback_timestamps():
2877+
return func(*args, **kwargs)
2878+
return wrapper
2879+
2880+
2881+
def force_no_traceback_timestamps_test_class(cls):
2882+
"""Class decorator: Force timestamps off for the entire test class."""
2883+
original_setUpClass = cls.setUpClass
2884+
2885+
@classmethod
2886+
@functools.wraps(cls.setUpClass)
2887+
def new_setUpClass(cls):
2888+
cls.enterClassContext(no_traceback_timestamps())
2889+
original_setUpClass()
2890+
2891+
cls.setUpClass = new_setUpClass
2892+
return cls
2893+
2894+
28522895
@contextlib.contextmanager
28532896
def no_color():
28542897
import _colorize

Lib/test/test_cmd_line_script.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import os.path
1111
import py_compile
1212
import subprocess
13+
import traceback
1314
import io
1415

1516
import textwrap
@@ -699,6 +700,7 @@ def test_source_lines_are_shown_when_running_source(self):
699700
b' 1/0',
700701
b' ~^~',
701702
b'ZeroDivisionError: division by zero']
703+
stderr = traceback.strip_exc_timestamps(stderr)
702704
self.assertEqual(stderr.splitlines(), expected_lines)
703705

704706
def test_syntaxerror_does_not_crash(self):

Lib/test/test_code_module.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,9 @@ def test_sysexcepthook(self):
129129
self.assertIs(type(self.sysmod.last_value), ValueError)
130130
self.assertIs(self.sysmod.last_traceback, self.sysmod.last_value.__traceback__)
131131
self.assertIs(self.sysmod.last_exc, self.sysmod.last_value)
132-
self.assertEqual(traceback.format_exception(self.sysmod.last_exc), [
132+
self.assertEqual(
133+
traceback.format_exception(self.sysmod.last_exc, no_timestamp=True),
134+
[
133135
'Traceback (most recent call last):\n',
134136
' File "<console>", line 1, in <module>\n',
135137
' File "<console>", line 2, in f\n',
@@ -152,7 +154,9 @@ def test_sysexcepthook_syntax_error(self):
152154
self.assertIsNone(self.sysmod.last_traceback)
153155
self.assertIsNone(self.sysmod.last_value.__traceback__)
154156
self.assertIs(self.sysmod.last_exc, self.sysmod.last_value)
155-
self.assertEqual(traceback.format_exception(self.sysmod.last_exc), [
157+
self.assertEqual(
158+
traceback.format_exception(self.sysmod.last_exc, no_timestamp=True),
159+
[
156160
' File "<console>", line 2\n',
157161
' x = ?\n',
158162
' ^\n',
@@ -172,7 +176,9 @@ def test_sysexcepthook_indentation_error(self):
172176
self.assertIsNone(self.sysmod.last_traceback)
173177
self.assertIsNone(self.sysmod.last_value.__traceback__)
174178
self.assertIs(self.sysmod.last_exc, self.sysmod.last_value)
175-
self.assertEqual(traceback.format_exception(self.sysmod.last_exc), [
179+
self.assertEqual(
180+
traceback.format_exception(self.sysmod.last_exc, no_timestamp=True),
181+
[
176182
' File "<console>", line 1\n',
177183
' 1\n',
178184
'IndentationError: unexpected indent\n'])
@@ -257,6 +263,7 @@ def test_cause_tb(self):
257263
EOFError('Finished')]
258264
self.console.interact()
259265
output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
266+
output = traceback.strip_exc_timestamps(output)
260267
expected = dedent("""
261268
AttributeError
262269
@@ -278,6 +285,7 @@ def test_context_tb(self):
278285
EOFError('Finished')]
279286
self.console.interact()
280287
output = ''.join(''.join(call[1]) for call in self.stderr.method_calls)
288+
output = traceback.strip_exc_timestamps(output)
281289
expected = dedent("""
282290
Traceback (most recent call last):
283291
File "<console>", line 1, in <module>

Lib/test/test_doctest/test_doctest.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,11 @@ def test_Example(): r"""
201201
>>> exc_msg = 'IndexError: pop from an empty list'
202202
>>> example = doctest.Example('[].pop()', '', exc_msg,
203203
... lineno=5, indent=4,
204-
... options={doctest.ELLIPSIS: True})
204+
... options={
205+
... doctest.IGNORE_EXCEPTION_TIMESTAMPS: True})
205206
>>> (example.source, example.want, example.exc_msg,
206207
... example.lineno, example.indent, example.options)
207-
('[].pop()\n', '', 'IndexError: pop from an empty list\n', 5, 4, {8: True})
208+
('[].pop()\n', '', 'IndexError: pop from an empty list\n', 5, 4, {64: True})
208209
209210
The constructor normalizes the `source` string to end in a newline:
210211
@@ -2209,7 +2210,7 @@ def test_pdb_set_trace_nested():
22092210
... runner.run(test)
22102211
... finally:
22112212
... sys.stdin = real_stdin
2212-
... # doctest: +REPORT_NDIFF
2213+
... # doctest: +REPORT_NDIFF +IGNORE_EXCEPTION_TIMESTAMPS
22132214
> <doctest test.test_doctest.test_doctest.test_pdb_set_trace_nested[0]>(4)calls_set_trace()
22142215
-> import pdb; pdb.set_trace()
22152216
(Pdb) step
@@ -2629,7 +2630,7 @@ def test_unittest_reportflags():
26292630
Now, when we run the test:
26302631
26312632
>>> result = suite.run(unittest.TestResult())
2632-
>>> print(result.failures[0][1]) # doctest: +ELLIPSIS
2633+
>>> print(result.failures[0][1]) # doctest: +ELLIPSIS +IGNORE_EXCEPTION_TIMESTAMPS
26332634
Traceback ...
26342635
Failed example:
26352636
favorite_color
@@ -2654,7 +2655,7 @@ def test_unittest_reportflags():
26542655
*NOTE*: These doctest are intentionally not placed in raw string to depict
26552656
the trailing whitespace using `\x20` in the diff below.
26562657
2657-
>>> print(result.failures[0][1]) # doctest: +ELLIPSIS
2658+
>>> print(result.failures[0][1]) # doctest: +ELLIPSIS +IGNORE_EXCEPTION_TIMESTAMPS
26582659
Traceback ...
26592660
Failed example:
26602661
favorite_color

Lib/test/test_exceptions.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
cpython_only, gc_collect,
1616
no_tracing, script_helper,
1717
SuppressCrashReport,
18-
force_not_colorized)
18+
force_not_colorized,
19+
force_no_traceback_timestamps)
1920
from test.support.import_helper import import_module
2021
from test.support.os_helper import TESTFN, unlink
2122
from test.support.warnings_helper import check_warnings
@@ -2055,6 +2056,7 @@ def tearDown(self):
20552056
unlink(TESTFN)
20562057

20572058
@force_not_colorized
2059+
@force_no_traceback_timestamps
20582060
def test_assertion_error_location(self):
20592061
cases = [
20602062
('assert None',
@@ -2153,6 +2155,7 @@ def test_assertion_error_location(self):
21532155
self.assertEqual(result[-3:], expected)
21542156

21552157
@force_not_colorized
2158+
@force_no_traceback_timestamps
21562159
def test_multiline_not_highlighted(self):
21572160
cases = [
21582161
("""

Lib/test/test_import/__init__.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import textwrap
2323
import threading
2424
import time
25+
import traceback
2526
import types
2627
import unittest
2728
from unittest import mock
@@ -1001,7 +1002,9 @@ def test_script_shadowing_third_party(self):
10011002

10021003
expected_error = error + (
10031004
rb" \(consider renaming '.*numpy.py' if it has the "
1004-
rb"same name as a library you intended to import\)\s+\Z"
1005+
rb"same name as a library you intended to import\)"
1006+
+ traceback.TIMESTAMP_AFTER_EXC_MSG_RE_GROUP.encode() +
1007+
rb"?\s+\Z"
10051008
)
10061009

10071010
popen = script_helper.spawn_python(os.path.join(tmp, "numpy.py"))
@@ -1022,14 +1025,18 @@ def test_script_maybe_not_shadowing_third_party(self):
10221025
f.write("this_script_does_not_attempt_to_import_numpy = True")
10231026

10241027
expected_error = (
1025-
rb"AttributeError: module 'numpy' has no attribute 'attr'\s+\Z"
1028+
rb"AttributeError: module 'numpy' has no attribute 'attr'"
1029+
+ traceback.TIMESTAMP_AFTER_EXC_MSG_RE_GROUP.encode() +
1030+
rb"?\s+\Z"
10261031
)
10271032
popen = script_helper.spawn_python('-c', 'import numpy; numpy.attr', cwd=tmp)
10281033
stdout, stderr = popen.communicate()
10291034
self.assertRegex(stdout, expected_error)
10301035

10311036
expected_error = (
1032-
rb"ImportError: cannot import name 'attr' from 'numpy' \(.*\)\s+\Z"
1037+
rb"ImportError: cannot import name 'attr' from 'numpy' \(.*\)"
1038+
+ traceback.TIMESTAMP_AFTER_EXC_MSG_RE_GROUP.encode() +
1039+
rb"?\s+\Z"
10331040
)
10341041
popen = script_helper.spawn_python('-c', 'from numpy import attr', cwd=tmp)
10351042
stdout, stderr = popen.communicate()

Lib/test/test_interpreters/utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import tempfile
1010
from textwrap import dedent
1111
import threading
12+
import traceback
1213
import types
1314
import unittest
1415
import warnings
@@ -472,6 +473,7 @@ def assert_python_ok(self, *argv):
472473
def assert_python_failure(self, *argv):
473474
exitcode, stdout, stderr = self.run_python(*argv)
474475
self.assertNotEqual(exitcode, 0)
476+
stderr = traceback.strip_exc_timestamps(stderr)
475477
return stdout, stderr
476478

477479
def assert_ns_equal(self, ns1, ns2, msg=None):

0 commit comments

Comments
 (0)