1616 'format_tb' , 'print_exc' , 'format_exc' , 'print_exception' ,
1717 'print_last' , 'print_stack' , 'print_tb' , 'clear_frames' ,
1818 'FrameSummary' , 'StackSummary' , 'TracebackException' ,
19- 'walk_stack' , 'walk_tb' , 'print_list' ]
19+ 'walk_stack' , 'walk_tb' , 'print_list' , 'strip_exc_timestamps' ]
2020
2121#
2222# Formatting and printing lists of traceback lines.
@@ -126,8 +126,9 @@ def print_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
126126 position of the error.
127127 """
128128 colorize = kwargs .get ("colorize" , False )
129+ no_timestamp = kwargs .get ("no_timestamp" , False )
129130 value , tb = _parse_value_tb (exc , value , tb )
130- te = TracebackException (type (value ), value , tb , limit = limit , compact = True )
131+ te = TracebackException (type (value ), value , tb , limit = limit , compact = True , no_timestamp = no_timestamp )
131132 te .print (file = file , chain = chain , colorize = colorize )
132133
133134
@@ -151,8 +152,9 @@ def format_exception(exc, /, value=_sentinel, tb=_sentinel, limit=None, \
151152 printed as does print_exception().
152153 """
153154 colorize = kwargs .get ("colorize" , False )
155+ no_timestamp = kwargs .get ("no_timestamp" , False )
154156 value , tb = _parse_value_tb (exc , value , tb )
155- te = TracebackException (type (value ), value , tb , limit = limit , compact = True )
157+ te = TracebackException (type (value ), value , tb , limit = limit , compact = True , no_timestamp = no_timestamp )
156158 return list (te .format (chain = chain , colorize = colorize ))
157159
158160
@@ -172,9 +174,10 @@ def format_exception_only(exc, /, value=_sentinel, *, show_group=False, **kwargs
172174 well, recursively, with indentation relative to their nesting depth.
173175 """
174176 colorize = kwargs .get ("colorize" , False )
177+ no_timestamp = kwargs .get ("no_timestamp" , False )
175178 if value is _sentinel :
176179 value = exc
177- te = TracebackException (type (value ), value , None , compact = True )
180+ te = TracebackException (type (value ), value , None , compact = True , no_timestamp = no_timestamp )
178181 return list (te .format_exception_only (show_group = show_group , colorize = colorize ))
179182
180183
@@ -194,6 +197,25 @@ def _timestamp_formatter(ns):
194197 _TIMESTAMP_FORMAT = ""
195198
196199
200+ # The regular expression to match timestamps as formatted in tracebacks.
201+ # Not compiled to avoid importing the re module by default.
202+ TIMESTAMP_AFTER_EXC_MSG_RE_GROUP = r"(?P<timestamp> <@[0-9:.Tsnu-]{18,26}>)"
203+
204+
205+ def strip_exc_timestamps (output ):
206+ """Remove exception timestamps from output; for use by tests."""
207+ if _TIMESTAMP_FORMAT :
208+ import re
209+ if isinstance (output , str ):
210+ pattern = TIMESTAMP_AFTER_EXC_MSG_RE_GROUP
211+ empty = ""
212+ else :
213+ pattern = TIMESTAMP_AFTER_EXC_MSG_RE_GROUP .encode ()
214+ empty = b""
215+ return re .sub (pattern , empty , output )
216+ return output
217+
218+
197219# -- not official API but folk probably use these two functions.
198220
199221def _format_final_exc_line (etype , value , * , insert_final_newline = True , colorize = False , timestamp_ns = 0 ):
@@ -1053,7 +1075,8 @@ class TracebackException:
10531075
10541076 def __init__ (self , exc_type , exc_value , exc_traceback , * , limit = None ,
10551077 lookup_lines = True , capture_locals = False , compact = False ,
1056- max_group_width = 15 , max_group_depth = 10 , save_exc_type = True , _seen = None ):
1078+ max_group_width = 15 , max_group_depth = 10 , save_exc_type = True ,
1079+ no_timestamp = False , _seen = None ):
10571080 # NB: we need to accept exc_traceback, exc_value, exc_traceback to
10581081 # permit backwards compat with the existing API, otherwise we
10591082 # need stub thunk objects just to glue it together.
@@ -1082,15 +1105,17 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None,
10821105 self .__notes__ = [
10831106 f'Ignored error getting __notes__: { _safe_string (e , '__notes__' , repr )} ' ]
10841107
1085- self ._timestamp_ns = exc_value .__timestamp_ns__ if _TIMESTAMP_FORMAT else 0
10861108 self ._is_syntax_error = False
10871109 self ._have_exc_type = exc_type is not None
10881110 if exc_type is not None :
10891111 self .exc_type_qualname = exc_type .__qualname__
10901112 self .exc_type_module = exc_type .__module__
1113+ self ._timestamp_ns = (exc_value .__timestamp_ns__
1114+ if _TIMESTAMP_FORMAT and not no_timestamp else 0 )
10911115 else :
10921116 self .exc_type_qualname = None
10931117 self .exc_type_module = None
1118+ self ._timestamp_ns = 0
10941119
10951120 if exc_type and issubclass (exc_type , SyntaxError ):
10961121 # Handle SyntaxError's specially
@@ -1227,7 +1252,20 @@ def _load_lines(self):
12271252
12281253 def __eq__ (self , other ):
12291254 if isinstance (other , TracebackException ):
1230- return self .__dict__ == other .__dict__
1255+ # It is unlikely anything would ever be equal when timestamp
1256+ # collection is enabled without this. We avoid extra work when
1257+ # it is not enabled.
1258+ if self ._timestamp_ns :
1259+ s_dict = self .__dict__ .copy ()
1260+ s_dict ["_timestamp_ns" ] = 0
1261+ else :
1262+ s_dict = self .__dict__
1263+ if other ._timestamp_ns :
1264+ o_dict = other .__dict__ .copy ()
1265+ o_dict ["_timestamp_ns" ] = 0
1266+ else :
1267+ o_dict = other .__dict__
1268+ return s_dict == o_dict
12311269 return NotImplemented
12321270
12331271 def __str__ (self ):
0 commit comments