summaryrefslogtreecommitdiffstats
path: root/sphinx/util/exceptions.py
blob: 577ec734e5948e3f68611925c36415a05ba6d69c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
from __future__ import annotations

import sys
import traceback
from tempfile import NamedTemporaryFile
from typing import TYPE_CHECKING

from sphinx.errors import SphinxParallelError
from sphinx.util.console import strip_escape_sequences

if TYPE_CHECKING:
    from sphinx.application import Sphinx


def save_traceback(app: Sphinx | None, exc: BaseException) -> str:
    """Save the given exception's traceback in a temporary file."""
    import platform

    import docutils
    import jinja2
    import pygments

    import sphinx

    if isinstance(exc, SphinxParallelError):
        exc_format = '(Error in parallel process)\n' + exc.traceback
    else:
        exc_format = traceback.format_exc()

    if app is None:
        last_msgs = exts_list = ''
    else:
        extensions = app.extensions.values()
        last_msgs = '\n'.join(f'#   {strip_escape_sequences(s).strip()}'
                              for s in app.messagelog)
        exts_list = '\n'.join(f'#   {ext.name} ({ext.version})' for ext in extensions
                              if ext.version != 'builtin')

    with NamedTemporaryFile('w', suffix='.log', prefix='sphinx-err-', delete=False) as f:
        f.write(f"""\
# Platform:         {sys.platform}; ({platform.platform()})
# Sphinx version:   {sphinx.__display_version__}
# Python version:   {platform.python_version()} ({platform.python_implementation()})
# Docutils version: {docutils.__version__}
# Jinja2 version:   {jinja2.__version__}
# Pygments version: {pygments.__version__}

# Last messages:
{last_msgs}

# Loaded extensions:
{exts_list}

# Traceback:
{exc_format}
""")
    return f.name


def format_exception_cut_frames(x: int = 1) -> str:
    """Format an exception with traceback, but only the last x frames."""
    typ, val, tb = sys.exc_info()
    # res = ['Traceback (most recent call last):\n']
    res: list[str] = []
    tbres = traceback.format_tb(tb)
    res += tbres[-x:]
    res += traceback.format_exception_only(typ, val)
    return ''.join(res)