|
11 | 11 | import mimetypes
|
12 | 12 | import hashlib
|
13 | 13 | import base64
|
| 14 | +import traceback |
14 | 15 | from urllib.parse import urlparse
|
15 | 16 |
|
16 | 17 | import flask
|
17 | 18 | from flask_compress import Compress
|
18 |
| -from werkzeug.debug.tbtools import get_current_traceback |
| 19 | + |
19 | 20 | from pkg_resources import get_distribution, parse_version
|
20 | 21 | from dash import dcc
|
21 | 22 | from dash import html
|
|
48 | 49 | patch_collections_abc,
|
49 | 50 | split_callback_id,
|
50 | 51 | to_json,
|
| 52 | + gen_salt, |
51 | 53 | )
|
52 | 54 | from . import _callback
|
53 | 55 | from . import _get_paths
|
|
102 | 104 | _re_renderer_scripts_id = 'id="_dash-renderer', "new DashRenderer"
|
103 | 105 |
|
104 | 106 |
|
| 107 | +def _get_traceback(secret, error: Exception): |
| 108 | + |
| 109 | + try: |
| 110 | + # pylint: disable=import-outside-toplevel |
| 111 | + from werkzeug.debug import tbtools |
| 112 | + except ImportError: |
| 113 | + tbtools = None |
| 114 | + |
| 115 | + def _get_skip(text, divider=2): |
| 116 | + skip = 0 |
| 117 | + for i, line in enumerate(text): |
| 118 | + if "%% callback invoked %%" in line: |
| 119 | + skip = int((i + 1) / divider) |
| 120 | + break |
| 121 | + return skip |
| 122 | + |
| 123 | + # werkzeug<2.1.0 |
| 124 | + if hasattr(tbtools, "get_current_traceback"): |
| 125 | + tb = tbtools.get_current_traceback() |
| 126 | + skip = _get_skip(tb.plaintext.splitlines()) |
| 127 | + return tbtools.get_current_traceback(skip=skip).render_full() |
| 128 | + |
| 129 | + if hasattr(tbtools, "DebugTraceback"): |
| 130 | + tb = tbtools.DebugTraceback(error) # pylint: disable=no-member |
| 131 | + skip = _get_skip(tb.render_traceback_text().splitlines()) |
| 132 | + |
| 133 | + # pylint: disable=no-member |
| 134 | + return tbtools.DebugTraceback(error, skip=skip).render_debugger_html( |
| 135 | + True, secret, True |
| 136 | + ) |
| 137 | + |
| 138 | + tb = traceback.format_exception(type(error), error, error.__traceback__) |
| 139 | + skip = _get_skip(tb, 1) |
| 140 | + return tb[0] + "".join(tb[skip:]) |
| 141 | + |
| 142 | + |
105 | 143 | class _NoUpdate:
|
106 | 144 | # pylint: disable=too-few-public-methods
|
107 | 145 | pass
|
@@ -1756,19 +1794,16 @@ def enable_dev_tools(
|
1756 | 1794 |
|
1757 | 1795 | if debug and dev_tools.prune_errors:
|
1758 | 1796 |
|
| 1797 | + secret = gen_salt(20) |
| 1798 | + |
1759 | 1799 | @self.server.errorhandler(Exception)
|
1760 |
| - def _wrap_errors(_): |
| 1800 | + def _wrap_errors(error): |
1761 | 1801 | # find the callback invocation, if the error is from a callback
|
1762 | 1802 | # and skip the traceback up to that point
|
1763 | 1803 | # if the error didn't come from inside a callback, we won't
|
1764 | 1804 | # skip anything.
|
1765 |
| - tb = get_current_traceback() |
1766 |
| - skip = 0 |
1767 |
| - for i, line in enumerate(tb.plaintext.splitlines()): |
1768 |
| - if "%% callback invoked %%" in line: |
1769 |
| - skip = int((i + 1) / 2) |
1770 |
| - break |
1771 |
| - return get_current_traceback(skip=skip).render_full(), 500 |
| 1805 | + tb = _get_traceback(secret, error) |
| 1806 | + return tb, 500 |
1772 | 1807 |
|
1773 | 1808 | if debug and dev_tools.ui:
|
1774 | 1809 |
|
|
0 commit comments