Pi66

merodi
A markdown-based static site generator.
git clone https://git.pi66.xyz/merodi

src/errors.py

 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
69
70
71
72
73
74
75
76
77
78
79
80
81
from os.path import basename
from html import escape
import traceback

from .log import *
from . import settings
from .api import api

def get_user_frame(exc):
    tb = traceback.extract_tb(exc.__traceback__)
    for frame in reversed(tb):
        if "site-packages" not in frame.filename and "<frozen" not in frame.filename:
            return frame
    return tb[-1] if tb else None

def get_error_line(exc):
    from jinja2 import TemplateSyntaxError
    if isinstance(exc, TemplateSyntaxError) and exc.source:
        lines = exc.source.splitlines()
        n = exc.lineno or 1
        if n <= len(lines):
            return lines[n - 1].strip()
    if hasattr(exc, 'source') and exc.source:
        lines = exc.source.splitlines()
        n = getattr(exc, 'lineno', 1) or 1
        if n <= len(lines):
            return lines[n - 1].strip()
    frame = get_user_frame(exc)
    return frame.line.strip() if frame and frame.line else None

def fatal(exc, message):
    frame = get_user_frame(exc)
    source_line = get_error_line(exc)
    detail = str(exc.message) if hasattr(exc, 'message') else str(exc)

    print(f"{RED('!')} {RED(type(exc).__name__)}")
    if frame:
        print(f"{BLUE(frame.name + '()')} {GRAY('—')} {basename(frame.filename)}:{YELLOW(str(frame.lineno))}")
    if detail:
        print(f"{GRAY('│')}  {detail}")
    if source_line:
        print(f"{GRAY('│')}  {source_line}\n")
    die(message)

def html_fatal(exc, message):
    if api.mode == "release":
        fatal(exc, message)

    frame = get_user_frame(exc)
    source_line = get_error_line(exc)
    line = escape(source_line) if source_line else "<i style='color:#666'>line not available</i>"
    detail = escape(str(exc.message)) if hasattr(exc, 'message') else escape(str(exc))

    if frame:
        location = f"""<div style="background: #2a2a2a; border-radius: 6px; overflow: hidden;">
            <div style="background: #333; padding: 0.5rem 1rem; display: flex; justify-content: space-between;">
                <span style="color: #569cd6;">{escape(frame.name)}()</span>
                <span style="color: #888;">{escape(basename(frame.filename))}:{frame.lineno}</span>
            </div>
            <pre style="margin: 0; padding: 1rem; color: #ddd; white-space: pre-wrap; border-left: 3px solid #f44; font-size: 1.1rem;">{line}</pre>
        </div>"""
    else:
        location = """<div style="background: #2a2a2a; border-radius: 6px; overflow: hidden;">
            <div style="background: #333; padding: 0.5rem 1rem;">
                <span style="color: #888;">no source location available</span>
            </div>
        </div>"""

    return f"""<html>
    <head><meta charset="UTF-8"></head>
    <body style="font-family: monospace; margin: 0; background: #1e1e1e; color: #ccc; min-height: 100vh; font-size: 18px;">
        <div style="background: #f44; padding: 0.75rem 2rem;">
            <span style="color: #fff; font-size: 1.2rem; font-weight: bold;">! {type(exc).__name__}</span>
        </div>
        <div style="padding: 2rem;">
            <p style="color: #f90; font-size: 1.3rem; margin: 0 0 0.25rem;">{detail}</p>
            <p style="color: #888; font-size: 1.2rem; margin: 0 0 2rem;">{escape(message)}</p>
            {location}
        </div>
    </body>
    </html>"""