Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Fix werkzeug 2.1.0 import & dev tools error html rendering. #1995

Merged
merged 3 commits into from
Mar 29, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](https://semver.org/).
### Fixed

- [#1963](https://github.com/plotly/dash/pull/1963) Fix [#1780](https://github.com/plotly/dash/issues/1780) flask shutdown deprecation warning when running dashduo threaded tests.
- [#1995](https://github.com/plotly/dash/pull/1995) Fix [#1992](https://github.com/plotly/dash/issues/1992) ImportError: cannot import name 'get_current_traceback' from 'werkzeug.debug.tbtools'.

## [2.3.0] - 2022-03-13

8 changes: 8 additions & 0 deletions dash/_utils.py
Original file line number Diff line number Diff line change
@@ -8,6 +8,8 @@
import logging
import io
import json
import secrets
import string
from functools import wraps

logger = logging.getLogger()
@@ -206,3 +208,9 @@ def _wrapper(*args, **kwargs):
return _wrapper

return wrapper


def gen_salt(chars):
return "".join(
secrets.choice(string.ascii_letters + string.digits) for _ in range(chars)
)
Original file line number Diff line number Diff line change
@@ -110,7 +110,7 @@ function UnconnectedErrorContent({error, base}) {
)}
{/* Backend Error */}
{typeof error.html !== 'string' ? null : error.html.indexOf(
'<!DOCTYPE HTML'
'<!DOCTYPE'
) === 0 ? (
<div className='dash-be-error__st'>
<div className='dash-backend-error'>
53 changes: 44 additions & 9 deletions dash/dash.py
Original file line number Diff line number Diff line change
@@ -11,11 +11,12 @@
import mimetypes
import hashlib
import base64
import traceback
from urllib.parse import urlparse

import flask
from flask_compress import Compress
from werkzeug.debug.tbtools import get_current_traceback

from pkg_resources import get_distribution, parse_version
from dash import dcc
from dash import html
@@ -48,6 +49,7 @@
patch_collections_abc,
split_callback_id,
to_json,
gen_salt,
)
from . import _callback
from . import _get_paths
@@ -102,6 +104,42 @@
_re_renderer_scripts_id = 'id="_dash-renderer', "new DashRenderer"


def _get_traceback(secret, error: Exception):

try:
# pylint: disable=import-outside-toplevel
from werkzeug.debug import tbtools
except ImportError:
tbtools = None

def _get_skip(text, divider=2):
skip = 0
for i, line in enumerate(text):
if "%% callback invoked %%" in line:
skip = int((i + 1) / divider)
break
return skip

# werkzeug<2.1.0
if hasattr(tbtools, "get_current_traceback"):
tb = tbtools.get_current_traceback()
skip = _get_skip(tb.plaintext.splitlines())
return tbtools.get_current_traceback(skip=skip).render_full()

if hasattr(tbtools, "DebugTraceback"):
tb = tbtools.DebugTraceback(error) # pylint: disable=no-member
skip = _get_skip(tb.render_traceback_text().splitlines())

# pylint: disable=no-member
return tbtools.DebugTraceback(error, skip=skip).render_debugger_html(
True, secret, True
)

tb = traceback.format_exception(type(error), error, error.__traceback__)
skip = _get_skip(tb, 1)
return tb[0] + "".join(tb[skip:])


class _NoUpdate:
# pylint: disable=too-few-public-methods
pass
@@ -1756,19 +1794,16 @@ def enable_dev_tools(

if debug and dev_tools.prune_errors:

secret = gen_salt(20)

@self.server.errorhandler(Exception)
def _wrap_errors(_):
def _wrap_errors(error):
# find the callback invocation, if the error is from a callback
# and skip the traceback up to that point
# if the error didn't come from inside a callback, we won't
# skip anything.
tb = get_current_traceback()
skip = 0
for i, line in enumerate(tb.plaintext.splitlines()):
if "%% callback invoked %%" in line:
skip = int((i + 1) / 2)
break
return get_current_traceback(skip=skip).render_full(), 500
tb = _get_traceback(secret, error)
return tb, 500

if debug and dev_tools.ui: