22import sys
33import unittest
44import subprocess
5+ import re
56
67from test .support import script_helper
78from test .support .os_helper import TESTFN , unlink
89
10+
911class TracebackTimestampsTests (unittest .TestCase ):
1012 def setUp (self ):
1113 self .script = """
@@ -20,8 +22,8 @@ def cause_exception():
2022except Exception as e:
2123 traceback.print_exc()
2224"""
23- self .script_path = TESTFN + ' .py'
24- with open (self .script_path , 'w' ) as script_file :
25+ self .script_path = TESTFN + " .py"
26+ with open (self .script_path , "w" ) as script_file :
2527 script_file .write (self .script )
2628 self .addCleanup (unlink , self .script_path )
2729
@@ -30,8 +32,8 @@ def cause_exception():
3032import sys
3133print(repr(sys.flags.traceback_timestamps))
3234"""
33- self .flags_script_path = TESTFN + ' _flag.py'
34- with open (self .flags_script_path , 'w' ) as script_file :
35+ self .flags_script_path = TESTFN + " _flag.py"
36+ with open (self .flags_script_path , "w" ) as script_file :
3537 script_file .write (self .flags_script )
3638 self .addCleanup (unlink , self .flags_script_path )
3739
@@ -43,26 +45,34 @@ def test_no_traceback_timestamps(self):
4345
4446 def test_traceback_timestamps_env_var (self ):
4547 """Test that PYTHON_TRACEBACK_TIMESTAMPS env var enables timestamps"""
46- result = script_helper .assert_python_ok (self .script_path , PYTHON_TRACEBACK_TIMESTAMPS = "us" )
48+ result = script_helper .assert_python_ok (
49+ self .script_path , PYTHON_TRACEBACK_TIMESTAMPS = "us"
50+ )
4751 stderr = result .err .decode ()
4852 self .assertIn ("<@" , stderr ) # Timestamp should be present
4953
5054 def test_traceback_timestamps_flag_us (self ):
5155 """Test -X traceback_timestamps=us flag"""
52- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps=us" , self .script_path )
56+ result = script_helper .assert_python_ok (
57+ "-X" , "traceback_timestamps=us" , self .script_path
58+ )
5359 stderr = result .err .decode ()
5460 self .assertIn ("<@" , stderr ) # Timestamp should be present
5561
5662 def test_traceback_timestamps_flag_ns (self ):
5763 """Test -X traceback_timestamps=ns flag"""
58- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps=ns" , self .script_path )
64+ result = script_helper .assert_python_ok (
65+ "-X" , "traceback_timestamps=ns" , self .script_path
66+ )
5967 stderr = result .err .decode ()
6068 self .assertIn ("<@" , stderr ) # Timestamp should be present
6169 self .assertIn ("ns>" , stderr ) # Should have ns format
6270
6371 def test_traceback_timestamps_flag_iso (self ):
6472 """Test -X traceback_timestamps=iso flag"""
65- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps=iso" , self .script_path )
73+ result = script_helper .assert_python_ok (
74+ "-X" , "traceback_timestamps=iso" , self .script_path
75+ )
6676 stderr = result .err .decode ()
6777 self .assertIn ("<@" , stderr ) # Timestamp should be present
6878 # ISO format with Z suffix for UTC
@@ -76,76 +86,190 @@ def test_traceback_timestamps_flag_value(self):
7686 self .assertEqual (stdout , "''" )
7787
7888 # With us flag
79- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps=us" , self .flags_script_path )
89+ result = script_helper .assert_python_ok (
90+ "-X" , "traceback_timestamps=us" , self .flags_script_path
91+ )
8092 stdout = result .out .decode ().strip ()
8193 self .assertEqual (stdout , "'us'" )
8294
8395 # With ns flag
84- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps=ns" , self .flags_script_path )
96+ result = script_helper .assert_python_ok (
97+ "-X" , "traceback_timestamps=ns" , self .flags_script_path
98+ )
8599 stdout = result .out .decode ().strip ()
86100 self .assertEqual (stdout , "'ns'" )
87101
88102 # With iso flag
89- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps=iso" , self .flags_script_path )
103+ result = script_helper .assert_python_ok (
104+ "-X" , "traceback_timestamps=iso" , self .flags_script_path
105+ )
90106 stdout = result .out .decode ().strip ()
91107 self .assertEqual (stdout , "'iso'" )
92108
93109 def test_traceback_timestamps_env_var_precedence (self ):
94110 """Test that -X flag takes precedence over env var"""
95- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps=us" ,
96- "-c" , "import sys; print(repr(sys.flags.traceback_timestamps))" ,
97- PYTHON_TRACEBACK_TIMESTAMPS = "ns" )
111+ result = script_helper .assert_python_ok (
112+ "-X" ,
113+ "traceback_timestamps=us" ,
114+ "-c" ,
115+ "import sys; print(repr(sys.flags.traceback_timestamps))" ,
116+ PYTHON_TRACEBACK_TIMESTAMPS = "ns" ,
117+ )
98118 stdout = result .out .decode ().strip ()
99119 self .assertEqual (stdout , "'us'" )
100-
120+
101121 def test_traceback_timestamps_flag_no_value (self ):
102122 """Test -X traceback_timestamps with no value defaults to 'us'"""
103- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps" , self .flags_script_path )
123+ result = script_helper .assert_python_ok (
124+ "-X" , "traceback_timestamps" , self .flags_script_path
125+ )
104126 stdout = result .out .decode ().strip ()
105127 self .assertEqual (stdout , "'us'" )
106-
128+
107129 def test_traceback_timestamps_flag_zero (self ):
108130 """Test -X traceback_timestamps=0 disables the feature"""
109131 # Check that setting to 0 results in empty string in sys.flags
110- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps=0" , self .flags_script_path )
132+ result = script_helper .assert_python_ok (
133+ "-X" , "traceback_timestamps=0" , self .flags_script_path
134+ )
111135 stdout = result .out .decode ().strip ()
112136 self .assertEqual (stdout , "''" )
113-
137+
114138 # Check that no timestamps appear in traceback
115- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps=0" , self .script_path )
139+ result = script_helper .assert_python_ok (
140+ "-X" , "traceback_timestamps=0" , self .script_path
141+ )
116142 stderr = result .err .decode ()
117143 self .assertNotIn ("<@" , stderr ) # No timestamp should be present
118-
144+
119145 def test_traceback_timestamps_flag_one (self ):
120146 """Test -X traceback_timestamps=1 is equivalent to 'us'"""
121- result = script_helper .assert_python_ok ("-X" , "traceback_timestamps=1" , self .flags_script_path )
147+ result = script_helper .assert_python_ok (
148+ "-X" , "traceback_timestamps=1" , self .flags_script_path
149+ )
122150 stdout = result .out .decode ().strip ()
123151 self .assertEqual (stdout , "'us'" )
124-
152+
125153 def test_traceback_timestamps_env_var_zero (self ):
126154 """Test PYTHON_TRACEBACK_TIMESTAMPS=0 disables the feature"""
127- result = script_helper .assert_python_ok (self .flags_script_path , PYTHON_TRACEBACK_TIMESTAMPS = "0" )
155+ result = script_helper .assert_python_ok (
156+ self .flags_script_path , PYTHON_TRACEBACK_TIMESTAMPS = "0"
157+ )
128158 stdout = result .out .decode ().strip ()
129159 self .assertEqual (stdout , "''" )
130-
160+
131161 def test_traceback_timestamps_env_var_one (self ):
132162 """Test PYTHON_TRACEBACK_TIMESTAMPS=1 is equivalent to 'us'"""
133- result = script_helper .assert_python_ok (self .flags_script_path , PYTHON_TRACEBACK_TIMESTAMPS = "1" )
163+ result = script_helper .assert_python_ok (
164+ self .flags_script_path , PYTHON_TRACEBACK_TIMESTAMPS = "1"
165+ )
134166 stdout = result .out .decode ().strip ()
135167 self .assertEqual (stdout , "'us'" )
136-
168+
137169 def test_traceback_timestamps_invalid_env_var (self ):
138170 """Test that invalid env var values are silently ignored"""
139- result = script_helper .assert_python_ok (self .flags_script_path , PYTHON_TRACEBACK_TIMESTAMPS = "invalid" )
171+ result = script_helper .assert_python_ok (
172+ self .flags_script_path , PYTHON_TRACEBACK_TIMESTAMPS = "invalid"
173+ )
140174 stdout = result .out .decode ().strip ()
141175 self .assertEqual (stdout , "''" ) # Should default to empty string
142-
176+
143177 def test_traceback_timestamps_invalid_flag (self ):
144178 """Test that invalid flag values cause an error"""
145- result = script_helper .assert_python_failure ("-X" , "traceback_timestamps=invalid" , self .flags_script_path )
179+ result = script_helper .assert_python_failure (
180+ "-X" , "traceback_timestamps=invalid" , self .flags_script_path
181+ )
146182 stderr = result .err .decode ()
147183 self .assertIn ("Invalid -X traceback_timestamps=value option" , stderr )
148184
149185
186+ class StripExcTimestampsTests (unittest .TestCase ):
187+ """Tests for traceback.strip_exc_timestamps function"""
188+
189+ def setUp (self ):
190+ self .script_strip_test = r"""
191+ import sys
192+ import traceback
193+
194+ def error():
195+ print("FakeError: not an exception <@1234567890.123456>\n")
196+ 3/0
197+
198+ def strip(data):
199+ print(traceback.strip_exc_timestamps(data))
200+
201+ if __name__ == "__main__":
202+ if len(sys.argv) <= 1:
203+ error()
204+ else:
205+ strip(sys.argv[1])
206+ """
207+ self .script_strip_path = TESTFN + "_strip.py"
208+ with open (self .script_strip_path , "w" ) as script_file :
209+ script_file .write (self .script_strip_test )
210+ self .addCleanup (unlink , self .script_strip_path )
211+
212+ def test_strip_exc_timestamps_function (self ):
213+ """Test the strip_exc_timestamps function with various inputs"""
214+ for mode in ("us" , "ns" , "iso" ):
215+ with self .subTest (mode ):
216+ result = script_helper .assert_python_failure (
217+ "-X" , f"traceback_timestamps={ mode } " , self .script_strip_path
218+ )
219+ output = result .out .decode () + result .err .decode ()
220+
221+ # call strip_exc_timestamps in a process using the same mode as what generated our output.
222+ result = script_helper .assert_python_ok (
223+ "-X" , f"traceback_timestamps={ mode } " , self .script_strip_path , output
224+ )
225+ stripped_output = result .out .decode () + result .err .decode ()
226+
227+ # Verify original strings have timestamps and stripped ones don't
228+ self .assertIn ("ZeroDivisionError: division by zero <@" , output )
229+ self .assertNotIn ("ZeroDivisionError: division by zero\n " , output )
230+ self .assertIn ("ZeroDivisionError: division by zero\n " , stripped_output )
231+ self .assertIn ("FakeError: not an exception\n " , stripped_output )
232+
233+ def test_strip_exc_timestamps_with_disabled_timestamps (self ):
234+ """Test the strip_exc_timestamps function when timestamps are disabled"""
235+ # Run with timestamps disabled
236+ result = script_helper .assert_python_failure (
237+ "-X" , "traceback_timestamps=0" , self .script_strip_path
238+ )
239+ output = result .out .decode () + result .err .decode ()
240+
241+ # call strip_exc_timestamps in a process using the same mode as what generated our output.
242+ result = script_helper .assert_python_ok (
243+ "-X" , "traceback_timestamps=0" , self .script_strip_path , output
244+ )
245+ stripped_output = result .out .decode () + result .err .decode ()
246+
247+ # All strings should be unchanged by the strip function
248+ self .assertIn ("ZeroDivisionError: division by zero\n " , stripped_output )
249+ # it fits the pattern but traceback timestamps were disabled to strip_exc_timestamps does nothing.
250+ self .assertIn (
251+ "FakeError: not an exception <@1234567890.123456>\n " , stripped_output
252+ )
253+
254+ def test_timestamp_regex_pattern (self ):
255+ """Test the regex pattern used by strip_exc_timestamps"""
256+ # Get the pattern from traceback module
257+ from traceback import TIMESTAMP_AFTER_EXC_MSG_RE_GROUP
258+
259+ pattern = re .compile (TIMESTAMP_AFTER_EXC_MSG_RE_GROUP )
260+
261+ # Test microsecond format
262+ self .assertTrue (pattern .search (" <@1234567890.123456>" ))
263+ # Test nanosecond format
264+ self .assertTrue (pattern .search (" <@1234567890123456789ns>" ))
265+ # Test ISO format
266+ self .assertTrue (pattern .search (" <@2023-04-13T12:34:56.789012Z>" ))
267+
268+ # Test what should not match
269+ self .assertFalse (pattern .search ("<@>" )) # Empty timestamp
270+ self .assertFalse (pattern .search (" <1234567890.123456>" )) # Missing @ sign
271+ self .assertFalse (pattern .search ("<@abc>" )) # Non-numeric timestamp
272+
273+
150274if __name__ == "__main__" :
151275 unittest .main ()
0 commit comments