Skip to content

Commit 3652e03

Browse files
SilverRainZDeepSeek
andcommitted
feat: Improve error reporting with debug mode and chained exception support
- Add current_exception() method to Report class that supports debug mode (full traceback when debug=True) - Enhance exception() to show chained exceptions with "Caused by:" messages - Use current_exception() in pending_node.resolve() for better error diagnostics - Pass debug=self.template.debug to error reports for template rendering failures Co-authored-by: DeepSeek <service@deepseek.com>
1 parent ebbe2f9 commit 3652e03

2 files changed

Lines changed: 79 additions & 21 deletions

File tree

src/sphinxnotes/render/ctxnodes.py

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -95,10 +95,13 @@ def err_report() -> Report:
9595

9696
if self._ctx_pickle_error is not None:
9797
report = err_report()
98-
report.exception(self._ctx_pickle_error, caption=(
99-
f'UnresolvedContext used by {self.template.phase} phase templates '
100-
'must be picklable:'
101-
))
98+
report.exception(
99+
self._ctx_pickle_error,
100+
caption=(
101+
f'UnresolvedContext used by {self.template.phase} phase templates '
102+
'must be picklable:'
103+
),
104+
)
102105
self += report
103106
return None
104107

@@ -112,9 +115,12 @@ def err_report() -> Report:
112115

113116
try:
114117
ctx = self.ctx = pdata.resolve(host.env)
115-
except Exception as e:
118+
except Exception:
116119
report = err_report()
117-
report.exception(e, caption='Failed to resolve unresolved context:')
120+
report.current_exception(
121+
caption='Failed to resolve unresolved context:',
122+
debug=self.template.debug,
123+
)
118124
self += report
119125
return None
120126
else:
@@ -123,11 +129,23 @@ def err_report() -> Report:
123129
for hook in self._resolved_context_hooks:
124130
hook(self, ctx)
125131

126-
report.code(pformat(ctx), lang='python', caption=f'Resolved context (type: {type(ctx)}):')
127-
report.code(self.template.text, lang='jinja', caption=f'Template (phase: {self.template.phase}):')
132+
report.code(
133+
pformat(ctx),
134+
lang='python',
135+
caption=f'Resolved context (type: {type(ctx)}):',
136+
)
137+
report.code(
138+
self.template.text,
139+
lang='jinja',
140+
caption=f'Template (phase: {self.template.phase}):',
141+
)
128142

129143
extractx_req = ExtraContextRequest(self.template.phase, self, host.env, host)
130-
report.code(pformat(sorted(extra_context_names())), lang='python', caption='Available extra context names:')
144+
report.code(
145+
pformat(sorted(extra_context_names())),
146+
lang='python',
147+
caption='Available extra context names:',
148+
)
131149

132150
# 2. Render the template and context to markup text.
133151
try:
@@ -136,9 +154,11 @@ def err_report() -> Report:
136154
globals={'load_extra': extra_context_loader(extractx_req)},
137155
debug=self.template.debug,
138156
)
139-
except Exception as e:
157+
except Exception:
140158
report = err_report()
141-
report.exception(e, caption='Failed to render Jinja template:')
159+
report.current_exception(
160+
caption='Failed to render Jinja template:', debug=self.template.debug
161+
)
142162
self += report
143163
return
144164

@@ -150,16 +170,23 @@ def err_report() -> Report:
150170
# 3. Render the markup text to doctree nodes.
151171
try:
152172
ns, msgs = MarkupRenderer(host).render(markup, inline=self.inline)
153-
except Exception as e:
173+
except Exception:
154174
report = err_report()
155-
report.exception(e, caption=(
156-
'Failed to render markup text '
157-
f'to {"inline " if self.inline else ""}nodes:'
158-
))
175+
report.current_exception(
176+
caption=(
177+
'Failed to render markup text '
178+
f'to {"inline " if self.inline else ""}nodes:'
179+
),
180+
debug=self.template.debug,
181+
)
159182
self += report
160183
return
161184

162-
report.code('\n\n'.join([n.pformat() for n in ns]), lang='xml', caption=f'Rendered nodes (inline: {self.inline}):')
185+
report.code(
186+
'\n\n'.join([n.pformat() for n in ns]),
187+
lang='xml',
188+
caption=f'Rendered nodes (inline: {self.inline}):',
189+
)
163190
if msgs:
164191
report.text('Systemd messages:')
165192
[report.node(msg) for msg in msgs]

src/sphinxnotes/render/utils/__init__.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22
from dataclasses import dataclass
3+
import sys
34
from typing import TYPE_CHECKING, TypeVar, cast
45
import traceback
56

@@ -123,13 +124,17 @@ def log(self, msg: str) -> None:
123124
def text(self, text: str) -> None:
124125
self.node(nodes.paragraph(text, text))
125126

126-
def code(self, code: str, lang: str | None = None, caption: str | None = None) -> None:
127+
def code(
128+
self, code: str, lang: str | None = None, caption: str | None = None
129+
) -> None:
127130
blk = nodes.literal_block(code, code)
128131
if lang:
129132
blk['language'] = lang
130133
if caption:
131134
# See also: :meth:`sphinx.directives.code.container_wrapper`.
132-
container = nodes.container('', literal_block=True, classes=['literal-block-wrapper'])
135+
container = nodes.container(
136+
'', literal_block=True, classes=['literal-block-wrapper']
137+
)
133138
container += nodes.caption(caption, '', nodes.Text(caption))
134139
container += blk
135140
self.node(container)
@@ -152,9 +157,35 @@ def traceback(self, caption: str | None = None) -> None:
152157
# https://pygments.org/docs/lexers/#pygments.lexers.python.PythonTracebackLexer
153158
self.code(traceback.format_exc(), lang='pytb', caption=caption)
154159

155-
def exception(self, e: Exception, caption: str | None = None) -> None:
160+
def exception(
161+
self, e: Exception | BaseException, caption: str | None = None
162+
) -> None:
163+
# A simplifed traceback.
164+
msg, cause, depth = str(e), e.__cause__, 1
165+
logger.warning('frist: %s', e.__cause__)
166+
while cause:
167+
msg += (
168+
'\n\n'
169+
+ (depth - 1) * 2 * ' '
170+
+ 'Caused by:\n'
171+
+ depth * 2 * ' '
172+
+ str(cause)
173+
)
174+
cause = cause.__cause__
175+
logger.warning('frist: %s', cause)
156176
# https://pygments.org/docs/lexers/#pygments.lexers.python.PythonTracebackLexer
157-
self.code(str(e), lang='pytb', caption=caption)
177+
self.code(msg, lang='pytb', caption=caption)
178+
179+
def current_exception(
180+
self, caption: str | None = None, debug: bool = False
181+
) -> None:
182+
if debug:
183+
self.traceback(caption=caption)
184+
return
185+
_, e, _ = sys.exc_info()
186+
if e is None:
187+
return
188+
self.exception(e, caption=caption)
158189

159190
def is_error(self) -> bool:
160191
return self['type'] == 'ERROR'

0 commit comments

Comments
 (0)