|
19 | 19 | 'walk_stack', 'walk_tb', 'print_list'] |
20 | 20 |
|
21 | 21 |
|
22 | | -ENABLE_TRACEBACK_TIMESTAMPS = os.environ.get("PYTHON_TRACEBACK_TIMESTAMPS", "") == "1" |
23 | | - |
24 | | - |
25 | 22 | # |
26 | 23 | # Formatting and printing lists of traceback lines. |
27 | 24 | # |
@@ -182,23 +179,43 @@ def format_exception_only(exc, /, value=_sentinel, *, show_group=False, **kwargs |
182 | 179 | return list(te.format_exception_only(show_group=show_group, colorize=colorize)) |
183 | 180 |
|
184 | 181 |
|
| 182 | +_TIMESTAMP_FORMAT = os.environ.get("PYTHON_TRACEBACK_TIMESTAMPS", "") |
| 183 | +match _TIMESTAMP_FORMAT: |
| 184 | + case "us" | "1": |
| 185 | + def _timestamp_formatter(ns): |
| 186 | + return f"<@{ns/1e9:.6f}>" |
| 187 | + case "ns": |
| 188 | + def _timestamp_formatter(ns): |
| 189 | + return f"<@{ns}ns>" |
| 190 | + case "iso": |
| 191 | + def _timestamp_formatter(ns): |
| 192 | + from datetime import datetime |
| 193 | + return f"<@{datetime.fromtimestamp(ns/1e9).isoformat()}>" |
| 194 | + case _: |
| 195 | + _TIMESTAMP_FORMAT = "" |
| 196 | + |
| 197 | + |
185 | 198 | # -- not official API but folk probably use these two functions. |
186 | 199 |
|
187 | | -def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize=False, timestamp=0): |
| 200 | +def _format_final_exc_line(etype, value, *, insert_final_newline=True, colorize=False, timestamp_ns=0): |
188 | 201 | valuestr = _safe_string(value, 'exception') |
189 | | - end_char = "\n" if insert_final_newline else "" |
190 | | - ts = f" <@T={timestamp:.6f}>" if timestamp else "" |
| 202 | + try: |
| 203 | + ts = _timestamp_formatter(timestamp_ns) if timestamp_ns else "" |
| 204 | + except Exception: |
| 205 | + ts = "" |
| 206 | + end = f"\n" if insert_final_newline else "" |
191 | 207 | if colorize: |
192 | | - timestamp = f"{ANSIColors.GREY}{ts}{ANSIColors.RESET}" if timestamp else "" |
| 208 | + end = f" {ANSIColors.GREY}{ts}{ANSIColors.RESET}{end}" if ts else end |
193 | 209 | if value is None or not valuestr: |
194 | | - line = f"{ANSIColors.BOLD_MAGENTA}{etype}{ANSIColors.RESET}{ts}{end_char}" |
| 210 | + line = f"{ANSIColors.BOLD_MAGENTA}{etype}{ANSIColors.RESET}{end}" |
195 | 211 | else: |
196 | | - line = f"{ANSIColors.BOLD_MAGENTA}{etype}{ANSIColors.RESET}: {ANSIColors.MAGENTA}{valuestr}{ANSIColors.RESET}{ts}{end_char}" |
| 212 | + line = f"{ANSIColors.BOLD_MAGENTA}{etype}{ANSIColors.RESET}: {ANSIColors.MAGENTA}{valuestr}{ANSIColors.RESET}{end}" |
197 | 213 | else: |
| 214 | + end = f" {ts}{end}" if ts else end |
198 | 215 | if value is None or not valuestr: |
199 | | - line = f"{etype}{ts}{end_char}" |
| 216 | + line = f"{etype}{end}" |
200 | 217 | else: |
201 | | - line = f"{etype}: {valuestr}{ts}{end_char}" |
| 218 | + line = f"{etype}: {valuestr}{end}" |
202 | 219 | return line |
203 | 220 |
|
204 | 221 |
|
@@ -1012,11 +1029,11 @@ class TracebackException: |
1012 | 1029 | - :attr:`__cause__` A TracebackException of the original *__cause__*. |
1013 | 1030 | - :attr:`__context__` A TracebackException of the original *__context__*. |
1014 | 1031 | - :attr:`__notes__` A reference to the original *__notes__* list. |
1015 | | - - :attr:`_timestamp` When the exception was created (seconds), if enabled. |
1016 | 1032 | - :attr:`exceptions` For exception groups - a list of TracebackException |
1017 | 1033 | instances for the nested *exceptions*. ``None`` for other exceptions. |
1018 | 1034 | - :attr:`__suppress_context__` The *__suppress_context__* value from the |
1019 | 1035 | original exception. |
| 1036 | + - :attr:`_timestamp_ns` When the exception was created if enabled, or 0. |
1020 | 1037 | - :attr:`stack` A `StackSummary` representing the traceback. |
1021 | 1038 | - :attr:`exc_type` (deprecated) The class of the original traceback. |
1022 | 1039 | - :attr:`exc_type_str` String display of exc_type |
@@ -1066,10 +1083,10 @@ def __init__(self, exc_type, exc_value, exc_traceback, *, limit=None, |
1066 | 1083 | self.__notes__ = [ |
1067 | 1084 | f'Ignored error getting __notes__: {_safe_string(e, '__notes__', repr)}'] |
1068 | 1085 |
|
1069 | | - if ENABLE_TRACEBACK_TIMESTAMPS: |
1070 | | - self._timestamp = exc_value.__timestamp_ns__ / 1_000_000_000 |
| 1086 | + if _TIMESTAMP_FORMAT: |
| 1087 | + self._timestamp_ns = exc_value.__timestamp_ns__ |
1071 | 1088 | else: |
1072 | | - self._timestamp = 0 |
| 1089 | + self._timestamp_ns = 0 |
1073 | 1090 |
|
1074 | 1091 | self._is_syntax_error = False |
1075 | 1092 | self._have_exc_type = exc_type is not None |
@@ -1242,22 +1259,22 @@ def format_exception_only(self, *, show_group=False, _depth=0, **kwargs): |
1242 | 1259 |
|
1243 | 1260 | indent = 3 * _depth * ' ' |
1244 | 1261 | if not self._have_exc_type: |
1245 | | - yield indent + _format_final_exc_line(None, self._str, colorize=colorize, timestamp=self._timestamp) |
| 1262 | + yield indent + _format_final_exc_line(None, self._str, colorize=colorize, timestamp_ns=self._timestamp_ns) |
1246 | 1263 | return |
1247 | 1264 |
|
1248 | 1265 | stype = self.exc_type_str |
1249 | 1266 | if not self._is_syntax_error: |
1250 | 1267 | if _depth > 0: |
1251 | 1268 | # Nested exceptions needs correct handling of multiline messages. |
1252 | 1269 | formatted = _format_final_exc_line( |
1253 | | - stype, self._str, insert_final_newline=False, colorize=colorize, timestamp=self._timestamp |
| 1270 | + stype, self._str, insert_final_newline=False, colorize=colorize, timestamp_ns=self._timestamp_ns |
1254 | 1271 | ).split('\n') |
1255 | 1272 | yield from [ |
1256 | 1273 | indent + l + '\n' |
1257 | 1274 | for l in formatted |
1258 | 1275 | ] |
1259 | 1276 | else: |
1260 | | - yield _format_final_exc_line(stype, self._str, colorize=colorize, timestamp=self._timestamp) |
| 1277 | + yield _format_final_exc_line(stype, self._str, colorize=colorize, timestamp_ns=self._timestamp_ns) |
1261 | 1278 | else: |
1262 | 1279 | yield from [indent + l for l in self._format_syntax_error(stype, colorize=colorize)] |
1263 | 1280 |
|
|
0 commit comments