Skip to content

Commit

Permalink
Always print tracebacks in logs when Sphinx encounters an internal er…
Browse files Browse the repository at this point in the history
…ror (#13163)

Co-authored-by: Adam Turner <[email protected]>
  • Loading branch information
kdeldycke and AA-Turner authored Feb 3, 2025
1 parent 1b83e55 commit d486e80
Show file tree
Hide file tree
Showing 2 changed files with 38 additions and 32 deletions.
2 changes: 2 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ Features added
Patch by Walter Dörwald.
* #11995: autodoc: Add support for :confval:`python_display_short_literal_types`.
Patch by Bénédikt Tran and Adam Turner.
* #13163: Always print the full context when Sphinx encounters an internal error.
Patch by Kevin Deldycke and Adam Turner.

Bugs fixed
----------
Expand Down
68 changes: 36 additions & 32 deletions sphinx/_cli/util/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ def full_exception_context(
*,
message_log: Collection[str] = (),
extensions: Collection[Extension] = (),
full_traceback: bool = True,
) -> str:
"""Return a formatted message containing useful debugging context."""
last_msgs = '\n'.join(f'* {strip_escape_sequences(s)}' for s in message_log)
Expand All @@ -66,12 +67,23 @@ def full_exception_context(
for ext in extensions
if ext.version != 'builtin'
)
exc_format = format_traceback(exception)
exc_format = format_traceback(exception, short_traceback=not full_traceback)
return error_info(last_msgs or 'None.', exts_list or 'None.', exc_format)


def format_traceback(exception: BaseException, /) -> str:
def format_traceback(
exception: BaseException, /, *, short_traceback: bool = False
) -> str:
"""Format the given exception's traceback."""
if short_traceback:
from traceback import TracebackException

# format an exception with traceback, but only the last frame.
te = TracebackException.from_exception(exception, limit=-1)
formatted_tb = (
te.stack.format()[-1] + ''.join(te.format_exception_only()).rstrip()
)
return formatted_tb
if isinstance(exception, SphinxParallelError):
return f'(Error in parallel process)\n{exception.traceback}'
else:
Expand Down Expand Up @@ -158,7 +170,6 @@ def handle_exception(
extensions: Collection[Extension] = (),
) -> None:
from bdb import BdbQuit
from traceback import TracebackException

from docutils.utils import SystemMessage

Expand All @@ -175,39 +186,21 @@ def print_red(*values: str) -> None:
print_err(*map(red, values))

print_err()
if print_traceback or use_pdb:
print_err(format_traceback(exception))
print_err()

if use_pdb:
from pdb import post_mortem

print_red(__('Exception occurred, starting debugger:'))
post_mortem(exception.__traceback__)
return

if isinstance(exception, KeyboardInterrupt):
if not use_pdb and isinstance(exception, KeyboardInterrupt):
print_err(__('Interrupted!'))
return

if isinstance(exception, SystemMessage):
print_red(__('reStructuredText markup error:'))
print_err(str(exception))
return
print_red(__('reStructuredText markup error!'))

if isinstance(exception, SphinxError):
print_red(f'{exception.category}:')
print_err(str(exception))
return
print_red(f'{exception.category}!')

if isinstance(exception, UnicodeError):
print_red(__('Encoding error:'))
print_err(str(exception))
return
print_red(__('Encoding error!'))

if isinstance(exception, RecursionError):
print_red(__('Recursion error:'))
print_err(str(exception))
print_red(__('Recursion error!'))
print_err()
print_err(
__(
Expand All @@ -217,14 +210,25 @@ def print_red(*values: str) -> None:
)
)
print_err('\n import sys\n sys.setrecursionlimit(1_500)\n')
return

# format an exception with traceback, but only the last frame.
te = TracebackException.from_exception(exception, limit=-1)
formatted_tb = te.stack.format()[-1] + ''.join(te.format_exception_only()).rstrip()
print_err()
error_context = full_exception_context(
exception,
message_log=message_log,
extensions=extensions,
full_traceback=print_traceback or use_pdb,
)
print_err(error_context)
print_err()

if use_pdb:
from pdb import post_mortem

print_red(__('Starting debugger:'))
post_mortem(exception.__traceback__)
return

print_red(__('Exception occurred:'))
print_err(formatted_tb)
# Save full traceback to log file
traceback_info_path = save_traceback(
exception, message_log=message_log, extensions=extensions
)
Expand Down

0 comments on commit d486e80

Please sign in to comment.