beautiful-traceback 0.4.0__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -129,6 +129,46 @@ TEST_PATHS: typ.List[str] = []
129
129
  PWD = os.getcwd()
130
130
 
131
131
 
132
+ def _compile_exclude_patterns(
133
+ exclude_patterns: typ.Sequence[str],
134
+ ) -> list[re.Pattern[str]]:
135
+ if not exclude_patterns:
136
+ return []
137
+
138
+ return [re.compile(pattern) for pattern in exclude_patterns]
139
+
140
+
141
+ def _row_matches_exclude_patterns(
142
+ row: Row, exclude_patterns: typ.Sequence[re.Pattern[str]]
143
+ ) -> bool:
144
+ if not exclude_patterns:
145
+ return False
146
+
147
+ # Try multiple representations so patterns can match different parts:
148
+ # 1. short_module: "_pytest/runner.py" (aliased path without prefix)
149
+ # 2. full_module: "/path/to/site-packages/_pytest/runner.py" (absolute path)
150
+ # 3. haystack_full: "<site> /path/to/site-packages/_pytest/runner.py:353 from_call result: ..."
151
+ # 4. haystack_short: "<site> _pytest/runner.py:353 from_call result: ..."
152
+ haystack_full = (
153
+ f"{row.alias} {row.full_module}:{row.lineno} {row.call} {row.context}"
154
+ )
155
+ haystack_short = (
156
+ f"{row.alias} {row.short_module}:{row.lineno} {row.call} {row.context}"
157
+ )
158
+ candidates = [
159
+ row.short_module,
160
+ row.full_module,
161
+ haystack_full,
162
+ haystack_short,
163
+ ]
164
+ for pattern in exclude_patterns:
165
+ for candidate in candidates:
166
+ if pattern.search(candidate):
167
+ return True
168
+
169
+ return False
170
+
171
+
132
172
  def _py_paths() -> typ.List[str]:
133
173
  if TEST_PATHS:
134
174
  return TEST_PATHS
@@ -227,7 +267,9 @@ def _iter_entry_rows(
227
267
 
228
268
 
229
269
  def _init_entries_context(
230
- entries: StackFrameEntryList, term_width: typ.Optional[int] = None
270
+ entries: StackFrameEntryList,
271
+ term_width: typ.Optional[int] = None,
272
+ exclude_patterns: typ.Sequence[str] = (),
231
273
  ) -> Context:
232
274
  if term_width is None:
233
275
  _term_width = _get_terminal_width()
@@ -246,6 +288,19 @@ def _init_entries_context(
246
288
  max_row_width = _term_width - 10
247
289
 
248
290
  rows = list(_iter_entry_rows(aliases, entry_paths, entries))
291
+ compiled_exclude_patterns = _compile_exclude_patterns(exclude_patterns)
292
+ if compiled_exclude_patterns:
293
+ rows = [
294
+ row
295
+ for row in rows
296
+ if not _row_matches_exclude_patterns(row, compiled_exclude_patterns)
297
+ ]
298
+
299
+ used_aliases = {row.alias for row in rows if row.alias}
300
+ if used_aliases:
301
+ aliases = [alias for alias in aliases if alias[0] in used_aliases]
302
+ else:
303
+ aliases = []
249
304
 
250
305
  if rows:
251
306
  max_short_module_len = max(
@@ -426,9 +481,15 @@ def _format_traceback(
426
481
 
427
482
 
428
483
  def format_traceback(
429
- traceback: ExceptionTraceback, color: bool = False, local_stack_only: bool = False
484
+ traceback: ExceptionTraceback,
485
+ color: bool = False,
486
+ local_stack_only: bool = False,
487
+ exclude_patterns: typ.Sequence[str] = (),
430
488
  ) -> str:
431
- ctx = _init_entries_context(traceback.stack_frames)
489
+ ctx = _init_entries_context(
490
+ traceback.stack_frames,
491
+ exclude_patterns=exclude_patterns,
492
+ )
432
493
  return _format_traceback(ctx, traceback, color, local_stack_only)
433
494
 
434
495
 
@@ -436,6 +497,7 @@ def format_tracebacks(
436
497
  tracebacks: typ.List[ExceptionTraceback],
437
498
  color: bool = False,
438
499
  local_stack_only: bool = False,
500
+ exclude_patterns: typ.Sequence[str] = (),
439
501
  ) -> str:
440
502
  traceback_strs: typ.List[str] = []
441
503
 
@@ -447,7 +509,12 @@ def format_tracebacks(
447
509
  # traceback_strs.append("vvv happend after ^^^ - ")
448
510
  traceback_strs.append(CONTEXT_HEAD + os.linesep)
449
511
 
450
- traceback_str = format_traceback(tb_tup, color, local_stack_only)
512
+ traceback_str = format_traceback(
513
+ tb_tup,
514
+ color,
515
+ local_stack_only,
516
+ exclude_patterns=exclude_patterns,
517
+ )
451
518
  traceback_strs.append(traceback_str)
452
519
 
453
520
  return os.linesep.join(traceback_strs).strip()
@@ -463,6 +530,7 @@ def exc_to_traceback_str(
463
530
  color: bool = False,
464
531
  local_stack_only: bool = False,
465
532
  exc_msg_override: str | None = None,
533
+ exclude_patterns: typ.Sequence[str] = (),
466
534
  ) -> str:
467
535
  # NOTE (mb 2020-08-13): wrt. cause vs context see
468
536
  # https://www.python.org/dev/peps/pep-3134/#enhanced-reporting
@@ -511,7 +579,12 @@ def exc_to_traceback_str(
511
579
 
512
580
  tracebacks = list(reversed(tracebacks))
513
581
 
514
- return format_tracebacks(tracebacks, color, local_stack_only)
582
+ return format_tracebacks(
583
+ tracebacks,
584
+ color,
585
+ local_stack_only,
586
+ exclude_patterns=exclude_patterns,
587
+ )
515
588
 
516
589
 
517
590
  class LoggingFormatterMixin:
@@ -8,7 +8,11 @@ import colorama
8
8
  from beautiful_traceback import formatting
9
9
 
10
10
 
11
- def init_excepthook(color: bool, local_stack_only: bool) -> typ.Callable:
11
+ def init_excepthook(
12
+ color: bool,
13
+ local_stack_only: bool,
14
+ exclude_patterns: typ.Sequence[str],
15
+ ) -> typ.Callable:
12
16
  def excepthook(
13
17
  exc_type: typ.Type[BaseException],
14
18
  exc_value: BaseException,
@@ -17,7 +21,11 @@ def init_excepthook(color: bool, local_stack_only: bool) -> typ.Callable:
17
21
  # pylint:disable=unused-argument
18
22
  tb_str = (
19
23
  formatting.exc_to_traceback_str(
20
- exc_value, traceback, color, local_stack_only
24
+ exc_value,
25
+ traceback,
26
+ color,
27
+ local_stack_only,
28
+ exclude_patterns=exclude_patterns,
21
29
  )
22
30
  + "\n"
23
31
  )
@@ -39,6 +47,7 @@ def install(
39
47
  only_tty: bool = True,
40
48
  only_hook_if_default_excepthook: bool = True,
41
49
  local_stack_only: bool = False,
50
+ exclude_patterns: typ.Sequence[str] = (),
42
51
  ) -> None:
43
52
  """Hook the current excepthook to the beautiful_traceback.
44
53
 
@@ -68,7 +77,11 @@ def install(
68
77
  if only_hook_if_default_excepthook and not is_default_exepthook:
69
78
  return
70
79
 
71
- sys.excepthook = init_excepthook(color=color, local_stack_only=local_stack_only)
80
+ sys.excepthook = init_excepthook(
81
+ color=color,
82
+ local_stack_only=local_stack_only,
83
+ exclude_patterns=exclude_patterns,
84
+ )
72
85
 
73
86
 
74
87
  def uninstall() -> None:
@@ -46,6 +46,7 @@ def exc_to_json(
46
46
  exc_value: BaseException,
47
47
  traceback: types.TracebackType | None,
48
48
  local_stack_only: bool = False,
49
+ exclude_patterns: typ.Sequence[str] = (),
49
50
  ) -> dict[str, typ.Any]:
50
51
  """Convert an exception to a JSON-serializable dictionary.
51
52
 
@@ -112,7 +113,11 @@ def exc_to_json(
112
113
  "frames": [],
113
114
  }
114
115
  else:
115
- ctx = fmt._init_entries_context(entries, term_width=fmt.DEFAULT_COLUMNS)
116
+ ctx = fmt._init_entries_context(
117
+ entries,
118
+ term_width=fmt.DEFAULT_COLUMNS,
119
+ exclude_patterns=exclude_patterns,
120
+ )
116
121
  result = _format_traceback_json(
117
122
  ctx.rows,
118
123
  main_tb.exc_name,
@@ -132,7 +137,11 @@ def exc_to_json(
132
137
  "frames": [],
133
138
  }
134
139
  else:
135
- ctx = fmt._init_entries_context(entries, term_width=fmt.DEFAULT_COLUMNS)
140
+ ctx = fmt._init_entries_context(
141
+ entries,
142
+ term_width=fmt.DEFAULT_COLUMNS,
143
+ exclude_patterns=exclude_patterns,
144
+ )
136
145
  chain_item = _format_traceback_json(
137
146
  ctx.rows,
138
147
  tb.exc_name,
@@ -0,0 +1,94 @@
1
+ """Helpers for extracting pytest's rewritten assertion details."""
2
+
3
+ import os
4
+
5
+ import pytest
6
+
7
+
8
+ def get_exception_message_override(excinfo: pytest.ExceptionInfo) -> str | None:
9
+ """Return pytest's verbose exception message when rewriting adds detail.
10
+
11
+ The plugin overrides pytest's longrepr rendering, which skips the
12
+ ExceptionInfo repr where pytest stores rewritten assertion diffs. Without
13
+ this, AssertionError messages collapse to str(exc) and omit left/right
14
+ details. Pulling reprcrash.message preserves that verbose message when
15
+ pytest provides one.
16
+ """
17
+ try:
18
+ repr_info = excinfo.getrepr(style="long")
19
+ except Exception:
20
+ return None
21
+
22
+ reprcrash = getattr(repr_info, "reprcrash", None)
23
+ if reprcrash is None:
24
+ return None
25
+
26
+ message = getattr(reprcrash, "message", None)
27
+ if not message:
28
+ return None
29
+
30
+ exc_name = type(excinfo.value).__name__
31
+ prefix = f"{exc_name}: "
32
+ if message.startswith(prefix):
33
+ message = message.removeprefix(prefix)
34
+
35
+ if not message or message == exc_name:
36
+ return None
37
+
38
+ exc_message = str(excinfo.value)
39
+ if message == exc_message:
40
+ return None
41
+
42
+ return message
43
+
44
+
45
+ def get_pytest_assertion_details(excinfo: pytest.ExceptionInfo) -> str | None:
46
+ """Return pytest's rewritten assertion lines for AssertionError.
47
+
48
+ Pytest only provides left/right diffs when a module is rewritten during
49
+ import. For helper modules, call pytest.register_assert_rewrite before
50
+ importing them, otherwise the assertion message is plain and there are no
51
+ diff lines to extract.
52
+ """
53
+ if not isinstance(excinfo.value, AssertionError):
54
+ return None
55
+
56
+ try:
57
+ # pytest stores assertion diffs on its own repr object, not the exception.
58
+ # Reference: https://github.com/pytest-dev/pytest/blob/main/src/_pytest/_code/code.py
59
+ repr_info = excinfo.getrepr(style="long")
60
+ except Exception:
61
+ return None
62
+
63
+ reprtraceback = getattr(repr_info, "reprtraceback", None)
64
+ if reprtraceback is None:
65
+ chain = getattr(repr_info, "chain", None)
66
+ if chain:
67
+ reprtraceback = chain[-1][0]
68
+
69
+ if reprtraceback is None:
70
+ return None
71
+
72
+ reprentries = getattr(reprtraceback, "reprentries", None)
73
+ if not reprentries:
74
+ return None
75
+
76
+ last_entry = reprentries[-1]
77
+ entry_lines = getattr(last_entry, "lines", None)
78
+ if not entry_lines:
79
+ return None
80
+
81
+ lines = []
82
+ for line in entry_lines:
83
+ stripped = line.lstrip()
84
+ if stripped.startswith("E"):
85
+ lines.append(stripped)
86
+ continue
87
+
88
+ if stripped.startswith(">"):
89
+ lines.append(stripped)
90
+
91
+ if not lines:
92
+ return None
93
+
94
+ return os.linesep.join(lines)
@@ -1,11 +1,22 @@
1
+ """Pytest plugin that preserves rewritten assertion details in tracebacks.
2
+
3
+ Pytest rewrites assert statements at import time for test modules (and any
4
+ modules registered via pytest.register_assert_rewrite). The rewritten asserts
5
+ raise AssertionError instances that include rich explanation text and left/right
6
+ diffs inside pytest's repr objects, not on the exception itself. This plugin
7
+ extracts those repr details and appends them to beautiful_traceback output.
8
+ """
9
+
1
10
  import os
2
11
  from typing import Any, Generator
3
12
 
4
- from . import formatting
5
13
  import pytest
6
-
7
14
  from pytest import Config
8
15
 
16
+ from . import formatting
17
+ from .pytest_assertion import get_exception_message_override
18
+ from .pytest_assertion import get_pytest_assertion_details
19
+
9
20
 
10
21
  def _get_option(config: Config, key: str) -> Any:
11
22
  val = None
@@ -22,96 +33,11 @@ def _get_option(config: Config, key: str) -> Any:
22
33
  return val
23
34
 
24
35
 
25
- def _get_exception_message_override(excinfo: pytest.ExceptionInfo) -> str | None:
26
- """Return pytest's verbose exception message when rewriting adds detail.
27
-
28
- The plugin overrides pytest's longrepr rendering, which skips the
29
- ExceptionInfo repr where pytest stores rewritten assertion diffs. Without
30
- this, AssertionError messages collapse to str(exc) and omit left/right
31
- details. Pulling reprcrash.message preserves that verbose message when
32
- pytest provides one.
33
- """
34
- try:
35
- repr_info = excinfo.getrepr(style="long")
36
- except Exception:
37
- return None
38
-
39
- reprcrash = getattr(repr_info, "reprcrash", None)
40
- if reprcrash is None:
41
- return None
42
-
43
- message = getattr(reprcrash, "message", None)
44
- if not message:
45
- return None
46
-
47
- exc_name = type(excinfo.value).__name__
48
- prefix = f"{exc_name}: "
49
- if message.startswith(prefix):
50
- message = message.removeprefix(prefix)
51
-
52
- if not message or message == exc_name:
53
- return None
54
-
55
- exc_message = str(excinfo.value)
56
- if message == exc_message:
57
- return None
58
-
59
- return message
60
-
61
-
62
- def _get_pytest_assertion_details(excinfo: pytest.ExceptionInfo) -> str | None:
63
- """Return the pytest assertion diff lines for AssertionError."""
64
- if not isinstance(excinfo.value, AssertionError):
65
- return None
66
-
67
- try:
68
- # pytest stores assertion diffs on its own repr object, not the exception.
69
- # Reference: https://github.com/pytest-dev/pytest/blob/main/src/_pytest/_code/code.py
70
- repr_info = excinfo.getrepr(style="long")
71
- except Exception:
72
- return None
73
-
74
- reprtraceback = getattr(repr_info, "reprtraceback", None)
75
- if reprtraceback is None:
76
- chain = getattr(repr_info, "chain", None)
77
- if chain:
78
- reprtraceback = chain[-1][0]
79
-
80
- if reprtraceback is None:
81
- return None
82
-
83
- reprentries = getattr(reprtraceback, "reprentries", None)
84
- if not reprentries:
85
- return None
86
-
87
- last_entry = reprentries[-1]
88
- entry_lines = getattr(last_entry, "lines", None)
89
- if not entry_lines:
90
- return None
91
-
92
- # Keep only the assertion diff lines for concise appending.
93
- lines = []
94
- for line in entry_lines:
95
- # Keep pytest's assertion diff lines and the failing expression.
96
- stripped = line.lstrip()
97
- if stripped.startswith("E"):
98
- lines.append(stripped)
99
- continue
100
-
101
- # Include the source line marker when present.
102
- if stripped.startswith(">"):
103
- lines.append(stripped)
104
-
105
- if not lines:
106
- return None
107
-
108
- return os.linesep.join(lines)
109
-
110
-
111
36
  def _format_traceback(excinfo: pytest.ExceptionInfo, config: Config) -> str:
112
- """Format a traceback with beautiful_traceback styling and pytest assertion details."""
113
- message_override = _get_exception_message_override(excinfo)
114
- assertion_details = _get_pytest_assertion_details(excinfo)
37
+ """Format a traceback with beautiful_traceback styling and pytest details."""
38
+ message_override = get_exception_message_override(excinfo)
39
+ assertion_details = get_pytest_assertion_details(excinfo)
40
+ exclude_patterns = _get_option(config, "beautiful_traceback_exclude_patterns")
115
41
 
116
42
  formatted_traceback = formatting.exc_to_traceback_str(
117
43
  excinfo.value,
@@ -121,6 +47,7 @@ def _format_traceback(excinfo: pytest.ExceptionInfo, config: Config) -> str:
121
47
  config, "enable_beautiful_traceback_local_stack_only"
122
48
  ),
123
49
  exc_msg_override=message_override,
50
+ exclude_patterns=exclude_patterns,
124
51
  )
125
52
 
126
53
  if assertion_details:
@@ -144,6 +71,13 @@ def pytest_addoption(parser) -> None:
144
71
  default=True,
145
72
  )
146
73
 
74
+ parser.addini(
75
+ "beautiful_traceback_exclude_patterns",
76
+ "Exclude traceback frames that match regex patterns",
77
+ type="linelist",
78
+ default=[],
79
+ )
80
+
147
81
 
148
82
  @pytest.hookimpl(hookwrapper=True)
149
83
  def pytest_runtest_makereport(item, call) -> Generator[None, None, None]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: beautiful-traceback
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: Beautiful, readable Python tracebacks with colors and formatting
5
5
  Keywords: traceback,error,debugging,formatting
6
6
  Author: Michael Bianco
@@ -142,6 +142,9 @@ Customize the plugin in your `pytest.ini` or `pyproject.toml`:
142
142
  [tool.pytest.ini_options]
143
143
  enable_beautiful_traceback = true # Enable/disable the plugin
144
144
  enable_beautiful_traceback_local_stack_only = true # Show only local code (filter libraries)
145
+ beautiful_traceback_exclude_patterns = [ # Regex patterns to drop frames
146
+ "click/core\\.py",
147
+ ]
145
148
  ```
146
149
 
147
150
  Or in `pytest.ini`:
@@ -150,8 +153,29 @@ Or in `pytest.ini`:
150
153
  [pytest]
151
154
  enable_beautiful_traceback = true
152
155
  enable_beautiful_traceback_local_stack_only = true
156
+ beautiful_traceback_exclude_patterns =
157
+ click/core\.py
153
158
  ```
154
159
 
160
+ Example: filter out pytest, pluggy, and playwright frames from CI tracebacks:
161
+
162
+ ```toml
163
+ [tool.pytest.ini_options]
164
+ beautiful_traceback_exclude_patterns = [
165
+ "^_pytest/",
166
+ "^pluggy/",
167
+ "^playwright/",
168
+ ]
169
+ ```
170
+
171
+ **Pattern Matching:** Patterns are tested against multiple representations of each frame:
172
+ - `_pytest/runner.py` (short module path)
173
+ - `/path/to/site-packages/_pytest/runner.py` (full module path)
174
+ - `<site> _pytest/runner.py:353 from_call result: ...` (formatted line with short path)
175
+ - `<site> /path/to/.../runner.py:353 from_call result: ...` (formatted line with full path)
176
+
177
+ This allows you to write simpler patterns like `^_pytest/` instead of needing to match the full site-packages path.
178
+
155
179
  ## Examples
156
180
 
157
181
  Check out the [examples/](examples/) directory for detailed usage examples including basic usage, exception chaining, logging integration, and more.
@@ -176,6 +200,7 @@ beautiful_traceback.install(
176
200
  only_tty=True, # Only activate for TTY output
177
201
  only_hook_if_default_excepthook=True, # Only install if default hook
178
202
  local_stack_only=False, # Filter to show only local code
203
+ exclude_patterns=["click/core\\.py"], # Regex patterns to drop frames
179
204
  envvar='ENABLE_BEAUTIFUL_TRACEBACK' # Optional environment variable gate
180
205
  )
181
206
  ```
@@ -0,0 +1,14 @@
1
+ beautiful_traceback/__init__.py,sha256=ZDE4AcfeeT420hCntDfdhnp4uhXm1YwCQHsTuvGc0L4,332
2
+ beautiful_traceback/_extension.py,sha256=klyo3XL4q3-Wdy4Lt6JYdh-Cfh_SkRCI7jCcQCwfWTM,311
3
+ beautiful_traceback/cli.py,sha256=M4EWW9SUNiGH2VCpssxRDMNXNjrVpy_8t9T7jf66RKE,2405
4
+ beautiful_traceback/common.py,sha256=Dg6J4rLdX9uKM6LxJaqSwtLkUjggZk-KrR5kpAIA4uo,2208
5
+ beautiful_traceback/formatting.py,sha256=GI6F_l0KyHbMztCwq009oQ2uhAVRX-iDVUklODoQ5rc,17658
6
+ beautiful_traceback/hook.py,sha256=8f4njtVq8tlO8NXBcKV11paNmmfwYz13Nh6eWw6E9JE,2342
7
+ beautiful_traceback/json_formatting.py,sha256=qCGyOsHB4aAlxC2uSv2Lo-DfNAMf0EDeyN7AEAb9_Ts,6313
8
+ beautiful_traceback/parsing.py,sha256=39GvHo6kx5dyzTMfRjUTc8H9tRGIhFT8RcjxUvbZBZY,3094
9
+ beautiful_traceback/pytest_assertion.py,sha256=vyNTOsOzzR70T2oYvzytrvW0R4gCsO7pBkud8d85arw,2784
10
+ beautiful_traceback/pytest_plugin.py,sha256=aVKNoKfI4UmwXvRAV0dPTuYJuLk-rUYOZP_2y_pVteM,3395
11
+ beautiful_traceback-0.5.0.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
12
+ beautiful_traceback-0.5.0.dist-info/entry_points.txt,sha256=EXsu7N89wqDpZPEwLHgYVncZJ3y-lbCFZC5fKBZZDac,138
13
+ beautiful_traceback-0.5.0.dist-info/METADATA,sha256=BJYOONwKSbUybFFSo_QP8UgaAbjfSJyA6tmFmqnj4UE,9501
14
+ beautiful_traceback-0.5.0.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- beautiful_traceback/__init__.py,sha256=ZDE4AcfeeT420hCntDfdhnp4uhXm1YwCQHsTuvGc0L4,332
2
- beautiful_traceback/_extension.py,sha256=klyo3XL4q3-Wdy4Lt6JYdh-Cfh_SkRCI7jCcQCwfWTM,311
3
- beautiful_traceback/cli.py,sha256=M4EWW9SUNiGH2VCpssxRDMNXNjrVpy_8t9T7jf66RKE,2405
4
- beautiful_traceback/common.py,sha256=Dg6J4rLdX9uKM6LxJaqSwtLkUjggZk-KrR5kpAIA4uo,2208
5
- beautiful_traceback/formatting.py,sha256=s4tjdejhYBXBmHlHiUa3bm9j0I6B2hka46JfHNdgKLk,15501
6
- beautiful_traceback/hook.py,sha256=6vYpqA-mD4G32HkX5WSyCMzkvMwB4RXP6wbVEIU6oaY,2078
7
- beautiful_traceback/json_formatting.py,sha256=WcgA6rk6YkFDRLXOTk95yVrK_HxNjvHHUqgr2RlK_O4,6071
8
- beautiful_traceback/parsing.py,sha256=39GvHo6kx5dyzTMfRjUTc8H9tRGIhFT8RcjxUvbZBZY,3094
9
- beautiful_traceback/pytest_plugin.py,sha256=6UtsNuFMMc2Ga5Uvl6ctNGoPNdzGKg9NFAwrz5LzPGk,5132
10
- beautiful_traceback-0.4.0.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
11
- beautiful_traceback-0.4.0.dist-info/entry_points.txt,sha256=EXsu7N89wqDpZPEwLHgYVncZJ3y-lbCFZC5fKBZZDac,138
12
- beautiful_traceback-0.4.0.dist-info/METADATA,sha256=qVYJrlUdgW6lSpelAoujC8OHOy2pJ43COqEiGjXkiww,8558
13
- beautiful_traceback-0.4.0.dist-info/RECORD,,