beautiful-traceback 0.3.0__py3-none-any.whl → 0.4.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.
@@ -1,17 +1,7 @@
1
1
  from ._extension import load_ipython_extension # noqa: F401
2
- from .formatting import LoggingFormatter, LoggingFormatterMixin
3
- from .hook import install, uninstall
4
- from .json_formatting import exc_to_json
2
+ from .formatting import LoggingFormatter, LoggingFormatterMixin # noqa: F401
3
+ from .hook import install, uninstall # noqa: F401
4
+ from .json_formatting import exc_to_json # noqa: F401
5
5
 
6
6
  # retain typo for backward compatibility
7
7
  LoggingFormaterMixin = LoggingFormatterMixin
8
-
9
-
10
- __all__ = [
11
- "install",
12
- "uninstall",
13
- "LoggingFormatter",
14
- "LoggingFormatterMixin",
15
- "LoggingFormaterMixin",
16
- "exc_to_json",
17
- ]
@@ -1,10 +1,13 @@
1
+ import os
2
+ from typing import Any, Generator
3
+
1
4
  from . import formatting
2
5
  import pytest
3
6
 
4
7
  from pytest import Config
5
8
 
6
9
 
7
- def _get_option(config: Config, key: str):
10
+ def _get_option(config: Config, key: str) -> Any:
8
11
  val = None
9
12
 
10
13
  # will throw an exception if option is not set
@@ -56,7 +59,77 @@ def _get_exception_message_override(excinfo: pytest.ExceptionInfo) -> str | None
56
59
  return message
57
60
 
58
61
 
59
- def pytest_addoption(parser):
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
+ 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)
115
+
116
+ formatted_traceback = formatting.exc_to_traceback_str(
117
+ excinfo.value,
118
+ excinfo.tb,
119
+ color=True,
120
+ local_stack_only=_get_option(
121
+ config, "enable_beautiful_traceback_local_stack_only"
122
+ ),
123
+ exc_msg_override=message_override,
124
+ )
125
+
126
+ if assertion_details:
127
+ formatted_traceback += os.linesep + assertion_details + os.linesep
128
+
129
+ return formatted_traceback
130
+
131
+
132
+ def pytest_addoption(parser) -> None:
60
133
  parser.addini(
61
134
  "enable_beautiful_traceback",
62
135
  "Enable the beautiful traceback plugin",
@@ -73,54 +146,24 @@ def pytest_addoption(parser):
73
146
 
74
147
 
75
148
  @pytest.hookimpl(hookwrapper=True)
76
- def pytest_runtest_makereport(item, call):
77
- """
78
- Pytest stack traces are challenging to work with by default. This plugin allows beautiful_traceback to be used instead.
149
+ def pytest_runtest_makereport(item, call) -> Generator[None, None, None]:
150
+ """Format test execution tracebacks with beautiful_traceback.
79
151
 
80
- This little piece of code was hard-won:
81
-
82
- https://grok.com/share/bGVnYWN5_951be3b1-6811-4fda-b220-c1dd72dedc31
152
+ This hook runs during the test execution phase and replaces pytest's
153
+ default traceback formatting with beautiful_traceback's output.
83
154
  """
84
- outcome = yield
85
- report = outcome.get_result() # Get the generated TestReport object
155
+ outcome = yield # type: ignore[misc]
156
+ report = outcome.get_result() # type: ignore[attr-defined]
86
157
 
87
- # Check if the report is for the 'call' phase (test execution) and if it failed
88
158
  if _get_option(item.config, "enable_beautiful_traceback") and report.failed:
89
- value = call.excinfo.value
90
- tb = call.excinfo.tb
91
-
92
- message_override = _get_exception_message_override(call.excinfo)
159
+ report.longrepr = _format_traceback(call.excinfo, item.config)
93
160
 
94
- formatted_traceback = formatting.exc_to_traceback_str(
95
- value,
96
- tb,
97
- color=True,
98
- local_stack_only=_get_option(
99
- item.config, "enable_beautiful_traceback_local_stack_only"
100
- ),
101
- exc_msg_override=message_override,
102
- )
103
- report.longrepr = formatted_traceback
104
161
 
162
+ def pytest_exception_interact(node, call, report) -> None:
163
+ """Format collection-phase tracebacks with beautiful_traceback.
105
164
 
106
- def pytest_exception_interact(node, call, report):
107
- """
108
- This can run during collection, not just test execution.
109
-
110
- So, if there's an import or other pre-run error in pytest, this will apply the correct formatting.
165
+ This hook runs during collection (e.g., import errors, fixture errors)
166
+ and ensures those errors also use beautiful_traceback formatting.
111
167
  """
112
- if report.failed:
113
- value = call.excinfo.value
114
- tb = call.excinfo.tb
115
- message_override = _get_exception_message_override(call.excinfo)
116
-
117
- formatted_traceback = formatting.exc_to_traceback_str(
118
- value,
119
- tb,
120
- color=True,
121
- local_stack_only=_get_option(
122
- node.config, "enable_beautiful_traceback_local_stack_only"
123
- ),
124
- exc_msg_override=message_override,
125
- )
126
- report.longrepr = formatted_traceback
168
+ if _get_option(node.config, "enable_beautiful_traceback") and report.failed:
169
+ report.longrepr = _format_traceback(call.excinfo, node.config)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: beautiful-traceback
3
- Version: 0.3.0
3
+ Version: 0.4.0
4
4
  Summary: Beautiful, readable Python tracebacks with colors and formatting
5
5
  Keywords: traceback,error,debugging,formatting
6
6
  Author: Michael Bianco
@@ -1,4 +1,4 @@
1
- beautiful_traceback/__init__.py,sha256=e6zDdmcyc3H2aAmY5_9hAeMtCU_b3xyXwGhqrbOa-qo,438
1
+ beautiful_traceback/__init__.py,sha256=ZDE4AcfeeT420hCntDfdhnp4uhXm1YwCQHsTuvGc0L4,332
2
2
  beautiful_traceback/_extension.py,sha256=klyo3XL4q3-Wdy4Lt6JYdh-Cfh_SkRCI7jCcQCwfWTM,311
3
3
  beautiful_traceback/cli.py,sha256=M4EWW9SUNiGH2VCpssxRDMNXNjrVpy_8t9T7jf66RKE,2405
4
4
  beautiful_traceback/common.py,sha256=Dg6J4rLdX9uKM6LxJaqSwtLkUjggZk-KrR5kpAIA4uo,2208
@@ -6,8 +6,8 @@ beautiful_traceback/formatting.py,sha256=s4tjdejhYBXBmHlHiUa3bm9j0I6B2hka46JfHNd
6
6
  beautiful_traceback/hook.py,sha256=6vYpqA-mD4G32HkX5WSyCMzkvMwB4RXP6wbVEIU6oaY,2078
7
7
  beautiful_traceback/json_formatting.py,sha256=WcgA6rk6YkFDRLXOTk95yVrK_HxNjvHHUqgr2RlK_O4,6071
8
8
  beautiful_traceback/parsing.py,sha256=39GvHo6kx5dyzTMfRjUTc8H9tRGIhFT8RcjxUvbZBZY,3094
9
- beautiful_traceback/pytest_plugin.py,sha256=IwZh5yUUsbLpUWZuVxVqSIGUVPO30_0xIHThc3xor54,3672
10
- beautiful_traceback-0.3.0.dist-info/WHEEL,sha256=XV0cjMrO7zXhVAIyyc8aFf1VjZ33Fen4IiJk5zFlC3g,80
11
- beautiful_traceback-0.3.0.dist-info/entry_points.txt,sha256=EXsu7N89wqDpZPEwLHgYVncZJ3y-lbCFZC5fKBZZDac,138
12
- beautiful_traceback-0.3.0.dist-info/METADATA,sha256=Z5pu8bStps0bEkS8f53AJ5I7krXVHpu2WVdibKPco_s,8558
13
- beautiful_traceback-0.3.0.dist-info/RECORD,,
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,,