summaryrefslogtreecommitdiff
path: root/src/werkzeug/debug
diff options
context:
space:
mode:
Diffstat (limited to 'src/werkzeug/debug')
-rw-r--r--src/werkzeug/debug/__init__.py265
-rw-r--r--src/werkzeug/debug/console.py61
-rw-r--r--src/werkzeug/debug/repr.py153
-rw-r--r--src/werkzeug/debug/tbtools.py265
4 files changed, 391 insertions, 353 deletions
diff --git a/src/werkzeug/debug/__init__.py b/src/werkzeug/debug/__init__.py
index cb984b03..beb7729e 100644
--- a/src/werkzeug/debug/__init__.py
+++ b/src/werkzeug/debug/__init__.py
@@ -8,33 +8,35 @@
:copyright: 2007 Pallets
:license: BSD-3-Clause
"""
+import getpass
+import hashlib
+import json
+import mimetypes
import os
import pkgutil
import re
import sys
-import uuid
-import json
import time
-import getpass
-import hashlib
-import mimetypes
+import uuid
from itertools import chain
-from os.path import join, basename
-from werkzeug.wrappers import BaseRequest as Request, BaseResponse as Response
-from werkzeug.http import parse_cookie
-from werkzeug.debug.tbtools import get_current_traceback, render_console_html
-from werkzeug.debug.console import Console
-from werkzeug.security import gen_salt
-from werkzeug._internal import _log
-from werkzeug._compat import text_type
+from os.path import basename
+from os.path import join
-
-# DEPRECATED
-from werkzeug.debug.repr import debug_repr as _debug_repr
+from .._compat import text_type
+from .._internal import _log
+from ..http import parse_cookie
+from ..security import gen_salt
+from ..wrappers import BaseRequest as Request
+from ..wrappers import BaseResponse as Response
+from .console import Console
+from .repr import debug_repr as _debug_repr
+from .tbtools import get_current_traceback
+from .tbtools import render_console_html
def debug_repr(*args, **kwargs):
import warnings
+
warnings.warn(
"'debug_repr' has moved to 'werkzeug.debug.repr.debug_repr'"
" as of version 0.7. This old import will be removed in version"
@@ -51,8 +53,8 @@ PIN_TIME = 60 * 60 * 24 * 7
def hash_pin(pin):
if isinstance(pin, text_type):
- pin = pin.encode('utf-8', 'replace')
- return hashlib.md5(pin + b'shittysalt').hexdigest()[:12]
+ pin = pin.encode("utf-8", "replace")
+ return hashlib.md5(pin + b"shittysalt").hexdigest()[:12]
_machine_id = None
@@ -67,9 +69,9 @@ def get_machine_id():
def _generate():
# Potential sources of secret information on linux. The machine-id
# is stable across boots, the boot id is not
- for filename in '/etc/machine-id', '/proc/sys/kernel/random/boot_id':
+ for filename in "/etc/machine-id", "/proc/sys/kernel/random/boot_id":
try:
- with open(filename, 'rb') as f:
+ with open(filename, "rb") as f:
return f.readline().strip()
except IOError:
continue
@@ -81,8 +83,10 @@ def get_machine_id():
# Google App Engine
# See https://github.com/pallets/werkzeug/issues/925
from subprocess import Popen, PIPE
- dump = Popen(['ioreg', '-c', 'IOPlatformExpertDevice', '-d', '2'],
- stdout=PIPE).communicate()[0]
+
+ dump = Popen(
+ ["ioreg", "-c", "IOPlatformExpertDevice", "-d", "2"], stdout=PIPE
+ ).communicate()[0]
match = re.search(b'"serial-number" = <([^>]+)', dump)
if match is not None:
return match.group(1)
@@ -100,12 +104,15 @@ def get_machine_id():
pass
if wr is not None:
try:
- with wr.OpenKey(wr.HKEY_LOCAL_MACHINE,
- 'SOFTWARE\\Microsoft\\Cryptography', 0,
- wr.KEY_READ | wr.KEY_WOW64_64KEY) as rk:
- machineGuid, wrType = wr.QueryValueEx(rk, 'MachineGuid')
+ with wr.OpenKey(
+ wr.HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Cryptography",
+ 0,
+ wr.KEY_READ | wr.KEY_WOW64_64KEY,
+ ) as rk:
+ machineGuid, wrType = wr.QueryValueEx(rk, "MachineGuid")
if wrType == wr.REG_SZ:
- return machineGuid.encode('utf-8')
+ return machineGuid.encode("utf-8")
else:
return machineGuid
except WindowsError:
@@ -116,7 +123,6 @@ def get_machine_id():
class _ConsoleFrame(object):
-
"""Helper class so that we can reuse the frame console code for the
standalone console.
"""
@@ -134,24 +140,23 @@ def get_pin_and_cookie_name(app):
Second item in the resulting tuple is the cookie name for remembering.
"""
- pin = os.environ.get('WERKZEUG_DEBUG_PIN')
+ pin = os.environ.get("WERKZEUG_DEBUG_PIN")
rv = None
num = None
# Pin was explicitly disabled
- if pin == 'off':
+ if pin == "off":
return None, None
# Pin was provided explicitly
- if pin is not None and pin.replace('-', '').isdigit():
+ if pin is not None and pin.replace("-", "").isdigit():
# If there are separators in the pin, return it directly
- if '-' in pin:
+ if "-" in pin:
rv = pin
else:
num = pin
- modname = getattr(app, '__module__',
- getattr(app.__class__, '__module__'))
+ modname = getattr(app, "__module__", getattr(app.__class__, "__module__"))
try:
# `getpass.getuser()` imports the `pwd` module,
@@ -167,42 +172,41 @@ def get_pin_and_cookie_name(app):
probably_public_bits = [
username,
modname,
- getattr(app, '__name__', getattr(app.__class__, '__name__')),
- getattr(mod, '__file__', None),
+ getattr(app, "__name__", getattr(app.__class__, "__name__")),
+ getattr(mod, "__file__", None),
]
# This information is here to make it harder for an attacker to
# guess the cookie name. They are unlikely to be contained anywhere
# within the unauthenticated debug page.
- private_bits = [
- str(uuid.getnode()),
- get_machine_id(),
- ]
+ private_bits = [str(uuid.getnode()), get_machine_id()]
h = hashlib.md5()
for bit in chain(probably_public_bits, private_bits):
if not bit:
continue
if isinstance(bit, text_type):
- bit = bit.encode('utf-8')
+ bit = bit.encode("utf-8")
h.update(bit)
- h.update(b'cookiesalt')
+ h.update(b"cookiesalt")
- cookie_name = '__wzd' + h.hexdigest()[:20]
+ cookie_name = "__wzd" + h.hexdigest()[:20]
# If we need to generate a pin we salt it a bit more so that we don't
# end up with the same value and generate out 9 digits
if num is None:
- h.update(b'pinsalt')
- num = ('%09d' % int(h.hexdigest(), 16))[:9]
+ h.update(b"pinsalt")
+ num = ("%09d" % int(h.hexdigest(), 16))[:9]
# Format the pincode in groups of digits for easier remembering if
# we don't have a result yet.
if rv is None:
for group_size in 5, 4, 3:
if len(num) % group_size == 0:
- rv = '-'.join(num[x:x + group_size].rjust(group_size, '0')
- for x in range(0, len(num), group_size))
+ rv = "-".join(
+ num[x : x + group_size].rjust(group_size, "0")
+ for x in range(0, len(num), group_size)
+ )
break
else:
rv = num
@@ -240,12 +244,21 @@ class DebuggedApplication(object):
:param pin_logging: enables the logging of the pin system.
"""
- def __init__(self, app, evalex=False, request_key='werkzeug.request',
- console_path='/console', console_init_func=None,
- show_hidden_frames=False, lodgeit_url=None,
- pin_security=True, pin_logging=True):
+ def __init__(
+ self,
+ app,
+ evalex=False,
+ request_key="werkzeug.request",
+ console_path="/console",
+ console_init_func=None,
+ show_hidden_frames=False,
+ lodgeit_url=None,
+ pin_security=True,
+ pin_logging=True,
+ ):
if lodgeit_url is not None:
from warnings import warn
+
warn(
"'lodgeit_url' is no longer used as of version 0.9 and"
" will be removed in version 1.0. Werkzeug uses"
@@ -269,19 +282,17 @@ class DebuggedApplication(object):
self.pin_logging = pin_logging
if pin_security:
# Print out the pin for the debugger on standard out.
- if os.environ.get('WERKZEUG_RUN_MAIN') == 'true' and \
- pin_logging:
- _log('warning', ' * Debugger is active!')
+ if os.environ.get("WERKZEUG_RUN_MAIN") == "true" and pin_logging:
+ _log("warning", " * Debugger is active!")
if self.pin is None:
- _log('warning', ' * Debugger PIN disabled. '
- 'DEBUGGER UNSECURED!')
+ _log("warning", " * Debugger PIN disabled. DEBUGGER UNSECURED!")
else:
- _log('info', ' * Debugger PIN: %s' % self.pin)
+ _log("info", " * Debugger PIN: %s" % self.pin)
else:
self.pin = None
def _get_pin(self):
- if not hasattr(self, '_pin'):
+ if not hasattr(self, "_pin"):
self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app)
return self._pin
@@ -294,7 +305,7 @@ class DebuggedApplication(object):
@property
def pin_cookie_name(self):
"""The name of the pin cookie."""
- if not hasattr(self, '_pin_cookie'):
+ if not hasattr(self, "_pin_cookie"):
self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app)
return self._pin_cookie
@@ -305,46 +316,51 @@ class DebuggedApplication(object):
app_iter = self.app(environ, start_response)
for item in app_iter:
yield item
- if hasattr(app_iter, 'close'):
+ if hasattr(app_iter, "close"):
app_iter.close()
except Exception:
- if hasattr(app_iter, 'close'):
+ if hasattr(app_iter, "close"):
app_iter.close()
traceback = get_current_traceback(
- skip=1, show_hidden_frames=self.show_hidden_frames,
- ignore_system_exceptions=True)
+ skip=1,
+ show_hidden_frames=self.show_hidden_frames,
+ ignore_system_exceptions=True,
+ )
for frame in traceback.frames:
self.frames[frame.id] = frame
self.tracebacks[traceback.id] = traceback
try:
- start_response('500 INTERNAL SERVER ERROR', [
- ('Content-Type', 'text/html; charset=utf-8'),
- # Disable Chrome's XSS protection, the debug
- # output can cause false-positives.
- ('X-XSS-Protection', '0'),
- ])
+ start_response(
+ "500 INTERNAL SERVER ERROR",
+ [
+ ("Content-Type", "text/html; charset=utf-8"),
+ # Disable Chrome's XSS protection, the debug
+ # output can cause false-positives.
+ ("X-XSS-Protection", "0"),
+ ],
+ )
except Exception:
# if we end up here there has been output but an error
# occurred. in that situation we can do nothing fancy any
# more, better log something into the error log and fall
# back gracefully.
- environ['wsgi.errors'].write(
- 'Debugging middleware caught exception in streamed '
- 'response at a point where response headers were already '
- 'sent.\n')
+ environ["wsgi.errors"].write(
+ "Debugging middleware caught exception in streamed "
+ "response at a point where response headers were already "
+ "sent.\n"
+ )
else:
is_trusted = bool(self.check_pin_trust(environ))
- yield traceback.render_full(evalex=self.evalex,
- evalex_trusted=is_trusted,
- secret=self.secret) \
- .encode('utf-8', 'replace')
+ yield traceback.render_full(
+ evalex=self.evalex, evalex_trusted=is_trusted, secret=self.secret
+ ).encode("utf-8", "replace")
- traceback.log(environ['wsgi.errors'])
+ traceback.log(environ["wsgi.errors"])
def execute_command(self, request, command, frame):
"""Execute a command in a console."""
- return Response(frame.console.eval(command), mimetype='text/html')
+ return Response(frame.console.eval(command), mimetype="text/html")
def display_console(self, request):
"""Display a standalone shell."""
@@ -353,30 +369,30 @@ class DebuggedApplication(object):
ns = {}
else:
ns = dict(self.console_init_func())
- ns.setdefault('app', self.app)
+ ns.setdefault("app", self.app)
self.frames[0] = _ConsoleFrame(ns)
is_trusted = bool(self.check_pin_trust(request.environ))
- return Response(render_console_html(secret=self.secret,
- evalex_trusted=is_trusted),
- mimetype='text/html')
+ return Response(
+ render_console_html(secret=self.secret, evalex_trusted=is_trusted),
+ mimetype="text/html",
+ )
def paste_traceback(self, request, traceback):
"""Paste the traceback and return a JSON response."""
rv = traceback.paste()
- return Response(json.dumps(rv), mimetype='application/json')
+ return Response(json.dumps(rv), mimetype="application/json")
def get_resource(self, request, filename):
"""Return a static resource from the shared folder."""
- filename = join('shared', basename(filename))
+ filename = join("shared", basename(filename))
try:
data = pkgutil.get_data(__package__, filename)
except OSError:
data = None
if data is not None:
- mimetype = mimetypes.guess_type(filename)[0] \
- or 'application/octet-stream'
+ mimetype = mimetypes.guess_type(filename)[0] or "application/octet-stream"
return Response(data, mimetype=mimetype)
- return Response('Not Found', status=404)
+ return Response("Not Found", status=404)
def check_pin_trust(self, environ):
"""Checks if the request passed the pin test. This returns `True` if the
@@ -387,9 +403,9 @@ class DebuggedApplication(object):
if self.pin is None:
return True
val = parse_cookie(environ).get(self.pin_cookie_name)
- if not val or '|' not in val:
+ if not val or "|" not in val:
return False
- ts, pin_hash = val.split('|', 1)
+ ts, pin_hash = val.split("|", 1)
if not ts.isdigit():
return False
if pin_hash != hash_pin(self.pin):
@@ -426,23 +442,23 @@ class DebuggedApplication(object):
# Otherwise go through pin based authentication
else:
- entered_pin = request.args.get('pin')
- if entered_pin.strip().replace('-', '') == \
- self.pin.replace('-', ''):
+ entered_pin = request.args.get("pin")
+ if entered_pin.strip().replace("-", "") == self.pin.replace("-", ""):
self._failed_pin_auth = 0
auth = True
else:
self._fail_pin_auth()
- rv = Response(json.dumps({
- 'auth': auth,
- 'exhausted': exhausted,
- }), mimetype='application/json')
+ rv = Response(
+ json.dumps({"auth": auth, "exhausted": exhausted}),
+ mimetype="application/json",
+ )
if auth:
- rv.set_cookie(self.pin_cookie_name, '%s|%s' % (
- int(time.time()),
- hash_pin(self.pin)
- ), httponly=True)
+ rv.set_cookie(
+ self.pin_cookie_name,
+ "%s|%s" % (int(time.time()), hash_pin(self.pin)),
+ httponly=True,
+ )
elif bad_cookie:
rv.delete_cookie(self.pin_cookie_name)
return rv
@@ -450,10 +466,11 @@ class DebuggedApplication(object):
def log_pin_request(self):
"""Log the pin if needed."""
if self.pin_logging and self.pin is not None:
- _log('info', ' * To enable the debugger you need to '
- 'enter the security pin:')
- _log('info', ' * Debugger pin code: %s' % self.pin)
- return Response('')
+ _log(
+ "info", " * To enable the debugger you need to enter the security pin:"
+ )
+ _log("info", " * Debugger pin code: %s" % self.pin)
+ return Response("")
def __call__(self, environ, start_response):
"""Dispatch the requests."""
@@ -462,26 +479,32 @@ class DebuggedApplication(object):
# any more!
request = Request(environ)
response = self.debug_application
- if request.args.get('__debugger__') == 'yes':
- cmd = request.args.get('cmd')
- arg = request.args.get('f')
- secret = request.args.get('s')
- traceback = self.tracebacks.get(request.args.get('tb', type=int))
- frame = self.frames.get(request.args.get('frm', type=int))
- if cmd == 'resource' and arg:
+ if request.args.get("__debugger__") == "yes":
+ cmd = request.args.get("cmd")
+ arg = request.args.get("f")
+ secret = request.args.get("s")
+ traceback = self.tracebacks.get(request.args.get("tb", type=int))
+ frame = self.frames.get(request.args.get("frm", type=int))
+ if cmd == "resource" and arg:
response = self.get_resource(request, arg)
- elif cmd == 'paste' and traceback is not None and \
- secret == self.secret:
+ elif cmd == "paste" and traceback is not None and secret == self.secret:
response = self.paste_traceback(request, traceback)
- elif cmd == 'pinauth' and secret == self.secret:
+ elif cmd == "pinauth" and secret == self.secret:
response = self.pin_auth(request)
- elif cmd == 'printpin' and secret == self.secret:
+ elif cmd == "printpin" and secret == self.secret:
response = self.log_pin_request()
- elif self.evalex and cmd is not None and frame is not None \
- and self.secret == secret and \
- self.check_pin_trust(environ):
+ elif (
+ self.evalex
+ and cmd is not None
+ and frame is not None
+ and self.secret == secret
+ and self.check_pin_trust(environ)
+ ):
response = self.execute_command(request, cmd, frame)
- elif self.evalex and self.console_path is not None and \
- request.path == self.console_path:
+ elif (
+ self.evalex
+ and self.console_path is not None
+ and request.path == self.console_path
+ ):
response = self.display_console(request)
return response(environ, start_response)
diff --git a/src/werkzeug/debug/console.py b/src/werkzeug/debug/console.py
index 2915aea1..adbd170b 100644
--- a/src/werkzeug/debug/console.py
+++ b/src/werkzeug/debug/console.py
@@ -8,20 +8,21 @@
:copyright: 2007 Pallets
:license: BSD-3-Clause
"""
-import sys
import code
+import sys
from types import CodeType
-from werkzeug.utils import escape
-from werkzeug.local import Local
-from werkzeug.debug.repr import debug_repr, dump, helper
+from ..local import Local
+from ..utils import escape
+from .repr import debug_repr
+from .repr import dump
+from .repr import helper
_local = Local()
class HTMLStringO(object):
-
"""A StringO version that HTML escapes on write."""
def __init__(self):
@@ -41,46 +42,46 @@ class HTMLStringO(object):
def readline(self):
if len(self._buffer) == 0:
- return ''
+ return ""
ret = self._buffer[0]
del self._buffer[0]
return ret
def reset(self):
- val = ''.join(self._buffer)
+ val = "".join(self._buffer)
del self._buffer[:]
return val
def _write(self, x):
if isinstance(x, bytes):
- x = x.decode('utf-8', 'replace')
+ x = x.decode("utf-8", "replace")
self._buffer.append(x)
def write(self, x):
self._write(escape(x))
def writelines(self, x):
- self._write(escape(''.join(x)))
+ self._write(escape("".join(x)))
class ThreadedStream(object):
-
"""Thread-local wrapper for sys.stdout for the interactive console."""
+ @staticmethod
def push():
if not isinstance(sys.stdout, ThreadedStream):
sys.stdout = ThreadedStream()
_local.stream = HTMLStringO()
- push = staticmethod(push)
+ @staticmethod
def fetch():
try:
stream = _local.stream
except AttributeError:
- return ''
+ return ""
return stream.reset()
- fetch = staticmethod(fetch)
+ @staticmethod
def displayhook(obj):
try:
stream = _local.stream
@@ -89,18 +90,17 @@ class ThreadedStream(object):
# stream._write bypasses escaping as debug_repr is
# already generating HTML for us.
if obj is not None:
- _local._current_ipy.locals['_'] = obj
+ _local._current_ipy.locals["_"] = obj
stream._write(debug_repr(obj))
- displayhook = staticmethod(displayhook)
def __setattr__(self, name, value):
- raise AttributeError('read only attribute %s' % name)
+ raise AttributeError("read only attribute %s" % name)
def __dir__(self):
return dir(sys.__stdout__)
def __getattribute__(self, name):
- if name == '__members__':
+ if name == "__members__":
return dir(sys.__stdout__)
try:
stream = _local.stream
@@ -118,7 +118,6 @@ sys.displayhook = ThreadedStream.displayhook
class _ConsoleLoader(object):
-
def __init__(self):
self._storage = {}
@@ -143,29 +142,30 @@ def _wrap_compiler(console):
code = compile(source, filename, symbol)
console.loader.register(code, source)
return code
+
console.compile = func
class _InteractiveConsole(code.InteractiveInterpreter):
-
def __init__(self, globals, locals):
code.InteractiveInterpreter.__init__(self, locals)
self.globals = dict(globals)
- self.globals['dump'] = dump
- self.globals['help'] = helper
- self.globals['__loader__'] = self.loader = _ConsoleLoader()
+ self.globals["dump"] = dump
+ self.globals["help"] = helper
+ self.globals["__loader__"] = self.loader = _ConsoleLoader()
self.more = False
self.buffer = []
_wrap_compiler(self)
def runsource(self, source):
- source = source.rstrip() + '\n'
+ source = source.rstrip() + "\n"
ThreadedStream.push()
- prompt = '... ' if self.more else '>>> '
+ prompt = "... " if self.more else ">>> "
try:
- source_to_eval = ''.join(self.buffer + [source])
- if code.InteractiveInterpreter.runsource(self,
- source_to_eval, '<debugger>', 'single'):
+ source_to_eval = "".join(self.buffer + [source])
+ if code.InteractiveInterpreter.runsource(
+ self, source_to_eval, "<debugger>", "single"
+ ):
self.more = True
self.buffer.append(source)
else:
@@ -182,12 +182,14 @@ class _InteractiveConsole(code.InteractiveInterpreter):
self.showtraceback()
def showtraceback(self):
- from werkzeug.debug.tbtools import get_current_traceback
+ from .tbtools import get_current_traceback
+
tb = get_current_traceback(skip=1)
sys.stdout._write(tb.render_summary())
def showsyntaxerror(self, filename=None):
- from werkzeug.debug.tbtools import get_current_traceback
+ from .tbtools import get_current_traceback
+
tb = get_current_traceback(skip=4)
sys.stdout._write(tb.render_summary())
@@ -196,7 +198,6 @@ class _InteractiveConsole(code.InteractiveInterpreter):
class Console(object):
-
"""An interactive console."""
def __init__(self, globals=None, locals=None):
diff --git a/src/werkzeug/debug/repr.py b/src/werkzeug/debug/repr.py
index e82d87c4..d7a7285c 100644
--- a/src/werkzeug/debug/repr.py
+++ b/src/werkzeug/debug/repr.py
@@ -13,37 +13,38 @@
:copyright: 2007 Pallets
:license: BSD-3-Clause
"""
-import sys
-import re
import codecs
+import re
+import sys
+from collections import deque
from traceback import format_exception_only
-try:
- from collections import deque
-except ImportError: # pragma: no cover
- deque = None
-from werkzeug.utils import escape
-from werkzeug._compat import iteritems, PY2, text_type, integer_types, \
- string_types
+
+from .._compat import integer_types
+from .._compat import iteritems
+from .._compat import PY2
+from .._compat import string_types
+from .._compat import text_type
+from ..utils import escape
missing = object()
-_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
+_paragraph_re = re.compile(r"(?:\r\n|\r|\n){2,}")
RegexType = type(_paragraph_re)
-HELP_HTML = '''\
+HELP_HTML = """\
<div class=box>
<h3>%(title)s</h3>
<pre class=help>%(text)s</pre>
</div>\
-'''
-OBJECT_DUMP_HTML = '''\
+"""
+OBJECT_DUMP_HTML = """\
<div class=box>
<h3>%(title)s</h3>
%(repr)s
<table>%(items)s</table>
</div>\
-'''
+"""
def debug_repr(obj):
@@ -64,31 +65,31 @@ def dump(obj=missing):
class _Helper(object):
-
"""Displays an HTML version of the normal help, for the interactive
debugger only because it requires a patched sys.stdout.
"""
def __repr__(self):
- return 'Type help(object) for help about object.'
+ return "Type help(object) for help about object."
def __call__(self, topic=None):
if topic is None:
- sys.stdout._write('<span class=help>%s</span>' % repr(self))
+ sys.stdout._write("<span class=help>%s</span>" % repr(self))
return
import pydoc
+
pydoc.help(topic)
rv = sys.stdout.reset()
if isinstance(rv, bytes):
- rv = rv.decode('utf-8', 'ignore')
+ rv = rv.decode("utf-8", "ignore")
paragraphs = _paragraph_re.split(rv)
if len(paragraphs) > 1:
title = paragraphs[0]
- text = '\n\n'.join(paragraphs[1:])
+ text = "\n\n".join(paragraphs[1:])
else: # pragma: no cover
- title = 'Help'
+ title = "Help"
text = paragraphs[0]
- sys.stdout._write(HELP_HTML % {'title': title, 'text': text})
+ sys.stdout._write(HELP_HTML % {"title": title, "text": text})
helper = _Helper()
@@ -101,55 +102,55 @@ def _add_subclass_info(inner, obj, base):
return inner
elif type(obj) is base:
return inner
- module = ''
- if obj.__class__.__module__ not in ('__builtin__', 'exceptions'):
+ module = ""
+ if obj.__class__.__module__ not in ("__builtin__", "exceptions"):
module = '<span class="module">%s.</span>' % obj.__class__.__module__
- return '%s%s(%s)' % (module, obj.__class__.__name__, inner)
+ return "%s%s(%s)" % (module, obj.__class__.__name__, inner)
class DebugReprGenerator(object):
-
def __init__(self):
self._stack = []
- def _sequence_repr_maker(left, right, base=object(), limit=8):
+ def _sequence_repr_maker(left, right, base=object(), limit=8): # noqa: B008, B902
def proxy(self, obj, recursive):
if recursive:
- return _add_subclass_info(left + '...' + right, obj, base)
+ return _add_subclass_info(left + "..." + right, obj, base)
buf = [left]
have_extended_section = False
for idx, item in enumerate(obj):
if idx:
- buf.append(', ')
+ buf.append(", ")
if idx == limit:
buf.append('<span class="extended">')
have_extended_section = True
buf.append(self.repr(item))
if have_extended_section:
- buf.append('</span>')
+ buf.append("</span>")
buf.append(right)
- return _add_subclass_info(u''.join(buf), obj, base)
+ return _add_subclass_info(u"".join(buf), obj, base)
+
return proxy
- list_repr = _sequence_repr_maker('[', ']', list)
- tuple_repr = _sequence_repr_maker('(', ')', tuple)
- set_repr = _sequence_repr_maker('set([', '])', set)
- frozenset_repr = _sequence_repr_maker('frozenset([', '])', frozenset)
- if deque is not None:
- deque_repr = _sequence_repr_maker('<span class="module">collections.'
- '</span>deque([', '])', deque)
+ list_repr = _sequence_repr_maker("[", "]", list)
+ tuple_repr = _sequence_repr_maker("(", ")", tuple)
+ set_repr = _sequence_repr_maker("set([", "])", set)
+ frozenset_repr = _sequence_repr_maker("frozenset([", "])", frozenset)
+ deque_repr = _sequence_repr_maker(
+ '<span class="module">collections.' "</span>deque([", "])", deque
+ )
del _sequence_repr_maker
def regex_repr(self, obj):
pattern = repr(obj.pattern)
if PY2:
- pattern = pattern.decode('string-escape', 'ignore')
+ pattern = pattern.decode("string-escape", "ignore")
else:
- pattern = codecs.decode(pattern, 'unicode-escape', 'ignore')
- if pattern[:1] == 'u':
- pattern = 'ur' + pattern[1:]
+ pattern = codecs.decode(pattern, "unicode-escape", "ignore")
+ if pattern[:1] == "u":
+ pattern = "ur" + pattern[1:]
else:
- pattern = 'r' + pattern
+ pattern = "r" + pattern
return u're.compile(<span class="string regex">%s</span>)' % pattern
def string_repr(self, obj, limit=70):
@@ -158,14 +159,18 @@ class DebugReprGenerator(object):
# shorten the repr when the hidden part would be at least 3 chars
if len(r) - limit > 2:
- buf.extend((
- escape(r[:limit]),
- '<span class="extended">', escape(r[limit:]), '</span>',
- ))
+ buf.extend(
+ (
+ escape(r[:limit]),
+ '<span class="extended">',
+ escape(r[limit:]),
+ "</span>",
+ )
+ )
else:
buf.append(escape(r))
- buf.append('</span>')
+ buf.append("</span>")
out = u"".join(buf)
# if the repr looks like a standard string, add subclass info if needed
@@ -177,27 +182,29 @@ class DebugReprGenerator(object):
def dict_repr(self, d, recursive, limit=5):
if recursive:
- return _add_subclass_info(u'{...}', d, dict)
- buf = ['{']
+ return _add_subclass_info(u"{...}", d, dict)
+ buf = ["{"]
have_extended_section = False
for idx, (key, value) in enumerate(iteritems(d)):
if idx:
- buf.append(', ')
+ buf.append(", ")
if idx == limit - 1:
buf.append('<span class="extended">')
have_extended_section = True
- buf.append('<span class="pair"><span class="key">%s</span>: '
- '<span class="value">%s</span></span>' %
- (self.repr(key), self.repr(value)))
+ buf.append(
+ '<span class="pair"><span class="key">%s</span>: '
+ '<span class="value">%s</span></span>'
+ % (self.repr(key), self.repr(value))
+ )
if have_extended_section:
- buf.append('</span>')
- buf.append('}')
- return _add_subclass_info(u''.join(buf), d, dict)
+ buf.append("</span>")
+ buf.append("}")
+ return _add_subclass_info(u"".join(buf), d, dict)
def object_repr(self, obj):
r = repr(obj)
if PY2:
- r = r.decode('utf-8', 'replace')
+ r = r.decode("utf-8", "replace")
return u'<span class="object">%s</span>' % escape(r)
def dispatch_repr(self, obj, recursive):
@@ -225,13 +232,14 @@ class DebugReprGenerator(object):
def fallback_repr(self):
try:
- info = ''.join(format_exception_only(*sys.exc_info()[:2]))
+ info = "".join(format_exception_only(*sys.exc_info()[:2]))
except Exception: # pragma: no cover
- info = '?'
+ info = "?"
if PY2:
- info = info.decode('utf-8', 'ignore')
- return u'<span class="brokenrepr">&lt;broken repr (%s)&gt;' \
- u'</span>' % escape(info.strip())
+ info = info.decode("utf-8", "ignore")
+ return u'<span class="brokenrepr">&lt;broken repr (%s)&gt;' u"</span>" % escape(
+ info.strip()
+ )
def repr(self, obj):
recursive = False
@@ -251,7 +259,7 @@ class DebugReprGenerator(object):
def dump_object(self, obj):
repr = items = None
if isinstance(obj, dict):
- title = 'Contents of'
+ title = "Contents of"
items = []
for key, value in iteritems(obj):
if not isinstance(key, string_types):
@@ -266,23 +274,24 @@ class DebugReprGenerator(object):
items.append((key, self.repr(getattr(obj, key))))
except Exception:
pass
- title = 'Details for'
- title += ' ' + object.__repr__(obj)[1:-1]
+ title = "Details for"
+ title += " " + object.__repr__(obj)[1:-1]
return self.render_object_dump(items, title, repr)
def dump_locals(self, d):
items = [(key, self.repr(value)) for key, value in d.items()]
- return self.render_object_dump(items, 'Local variables in frame')
+ return self.render_object_dump(items, "Local variables in frame")
def render_object_dump(self, items, title, repr=None):
html_items = []
for key, value in items:
- html_items.append('<tr><th>%s<td><pre class=repr>%s</pre>' %
- (escape(key), value))
+ html_items.append(
+ "<tr><th>%s<td><pre class=repr>%s</pre>" % (escape(key), value)
+ )
if not html_items:
- html_items.append('<tr><td><em>Nothing</em>')
+ html_items.append("<tr><td><em>Nothing</em>")
return OBJECT_DUMP_HTML % {
- 'title': escape(title),
- 'repr': '<pre class=repr>%s</pre>' % repr if repr else '',
- 'items': '\n'.join(html_items)
+ "title": escape(title),
+ "repr": "<pre class=repr>%s</pre>" % repr if repr else "",
+ "items": "\n".join(html_items),
}
diff --git a/src/werkzeug/debug/tbtools.py b/src/werkzeug/debug/tbtools.py
index 9422052f..f6af4e30 100644
--- a/src/werkzeug/debug/tbtools.py
+++ b/src/werkzeug/debug/tbtools.py
@@ -8,30 +8,33 @@
:copyright: 2007 Pallets
:license: BSD-3-Clause
"""
-import re
-
+import codecs
+import inspect
+import json
import os
+import re
import sys
-import json
-import inspect
import sysconfig
import traceback
-import codecs
from tokenize import TokenError
-from werkzeug.utils import cached_property, escape
-from werkzeug.debug.console import Console
-from werkzeug._compat import (
- range_type, PY2, text_type, string_types,
- to_native, to_unicode, reraise,
-)
-from werkzeug.filesystem import get_filesystem_encoding
+from .._compat import PY2
+from .._compat import range_type
+from .._compat import reraise
+from .._compat import string_types
+from .._compat import text_type
+from .._compat import to_native
+from .._compat import to_unicode
+from ..filesystem import get_filesystem_encoding
+from ..utils import cached_property
+from ..utils import escape
+from .console import Console
-_coding_re = re.compile(br'coding[:=]\s*([-\w.]+)')
-_line_re = re.compile(br'^(.*?)$', re.MULTILINE)
-_funcdef_re = re.compile(r'^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)')
-UTF8_COOKIE = b'\xef\xbb\xbf'
+_coding_re = re.compile(br"coding[:=]\s*([-\w.]+)")
+_line_re = re.compile(br"^(.*?)$", re.MULTILINE)
+_funcdef_re = re.compile(r"^(\s*def\s)|(.*(?<!\w)lambda(:|\s))|^(\s*@)")
+UTF8_COOKIE = b"\xef\xbb\xbf"
system_exceptions = (SystemExit, KeyboardInterrupt)
try:
@@ -40,7 +43,7 @@ except NameError:
pass
-HEADER = u'''\
+HEADER = u"""\
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
@@ -65,8 +68,8 @@ HEADER = u'''\
</head>
<body style="background-color: #fff">
<div class="debugger">
-'''
-FOOTER = u'''\
+"""
+FOOTER = u"""\
<div class="footer">
Brought to you by <strong class="arthur">DON'T PANIC</strong>, your
friendly Werkzeug powered traceback interpreter.
@@ -89,9 +92,11 @@ FOOTER = u'''\
</div>
</body>
</html>
-'''
+"""
-PAGE_HTML = HEADER + u'''\
+PAGE_HTML = (
+ HEADER
+ + u"""\
<h1>%(exception_type)s</h1>
<div class="detail">
<p class="errormsg">%(exception)s</p>
@@ -117,61 +122,69 @@ PAGE_HTML = HEADER + u'''\
execution (if the evalex feature is enabled), automatic pasting of the
exceptions and much more.</span>
</div>
-''' + FOOTER + '''
+"""
+ + FOOTER
+ + """
<!--
%(plaintext_cs)s
-->
-'''
+"""
+)
-CONSOLE_HTML = HEADER + u'''\
+CONSOLE_HTML = (
+ HEADER
+ + u"""\
<h1>Interactive Console</h1>
<div class="explanation">
In this console you can execute Python expressions in the context of the
application. The initial namespace was created by the debugger automatically.
</div>
<div class="console"><div class="inner">The Console requires JavaScript.</div></div>
-''' + FOOTER
+"""
+ + FOOTER
+)
-SUMMARY_HTML = u'''\
+SUMMARY_HTML = u"""\
<div class="%(classes)s">
%(title)s
<ul>%(frames)s</ul>
%(description)s
</div>
-'''
+"""
-FRAME_HTML = u'''\
+FRAME_HTML = u"""\
<div class="frame" id="frame-%(id)d">
<h4>File <cite class="filename">"%(filename)s"</cite>,
line <em class="line">%(lineno)s</em>,
in <code class="function">%(function_name)s</code></h4>
<div class="source %(library)s">%(lines)s</div>
</div>
-'''
+"""
-SOURCE_LINE_HTML = u'''\
+SOURCE_LINE_HTML = u"""\
<tr class="%(classes)s">
<td class=lineno>%(lineno)s</td>
<td>%(code)s</td>
</tr>
-'''
+"""
def render_console_html(secret, evalex_trusted=True):
return CONSOLE_HTML % {
- 'evalex': 'true',
- 'evalex_trusted': 'true' if evalex_trusted else 'false',
- 'console': 'true',
- 'title': 'Console',
- 'secret': secret,
- 'traceback_id': -1
+ "evalex": "true",
+ "evalex_trusted": "true" if evalex_trusted else "false",
+ "console": "true",
+ "title": "Console",
+ "secret": secret,
+ "traceback_id": -1,
}
-def get_current_traceback(ignore_system_exceptions=False,
- show_hidden_frames=False, skip=0):
+def get_current_traceback(
+ ignore_system_exceptions=False, show_hidden_frames=False, skip=0
+):
"""Get the current exception info as `Traceback` object. Per default
calling this method will reraise system exceptions such as generator exit,
system exit or others. This behavior can be disabled by passing `False`
@@ -192,7 +205,8 @@ def get_current_traceback(ignore_system_exceptions=False,
class Line(object):
"""Helper for the source renderer."""
- __slots__ = ('lineno', 'code', 'in_frame', 'current')
+
+ __slots__ = ("lineno", "code", "in_frame", "current")
def __init__(self, lineno, code):
self.lineno = lineno
@@ -202,18 +216,18 @@ class Line(object):
@property
def classes(self):
- rv = ['line']
+ rv = ["line"]
if self.in_frame:
- rv.append('in-frame')
+ rv.append("in-frame")
if self.current:
- rv.append('current')
+ rv.append("current")
return rv
def render(self):
return SOURCE_LINE_HTML % {
- 'classes': u' '.join(self.classes),
- 'lineno': self.lineno,
- 'code': escape(self.code)
+ "classes": u" ".join(self.classes),
+ "lineno": self.lineno,
+ "code": escape(self.code),
}
@@ -227,7 +241,7 @@ class Traceback(object):
exception_type = exc_type.__name__
if exc_type.__module__ not in {"builtins", "__builtin__", "exceptions"}:
- exception_type = exc_type.__module__ + '.' + exception_type
+ exception_type = exc_type.__module__ + "." + exception_type
self.exception_type = exception_type
self.groups = []
@@ -264,38 +278,33 @@ class Traceback(object):
"""Log the ASCII traceback into a file object."""
if logfile is None:
logfile = sys.stderr
- tb = self.plaintext.rstrip() + u'\n'
+ tb = self.plaintext.rstrip() + u"\n"
logfile.write(to_native(tb, "utf-8", "replace"))
def paste(self):
"""Create a paste and return the paste id."""
- data = json.dumps({
- 'description': 'Werkzeug Internal Server Error',
- 'public': False,
- 'files': {
- 'traceback.txt': {
- 'content': self.plaintext
- }
+ data = json.dumps(
+ {
+ "description": "Werkzeug Internal Server Error",
+ "public": False,
+ "files": {"traceback.txt": {"content": self.plaintext}},
}
- }).encode('utf-8')
+ ).encode("utf-8")
try:
from urllib2 import urlopen
except ImportError:
from urllib.request import urlopen
- rv = urlopen('https://api.github.com/gists', data=data)
- resp = json.loads(rv.read().decode('utf-8'))
+ rv = urlopen("https://api.github.com/gists", data=data)
+ resp = json.loads(rv.read().decode("utf-8"))
rv.close()
- return {
- 'url': resp['html_url'],
- 'id': resp['id']
- }
+ return {"url": resp["html_url"], "id": resp["id"]}
def render_summary(self, include_title=True):
"""Render the traceback for the interactive console."""
- title = ''
- classes = ['traceback']
+ title = ""
+ classes = ["traceback"]
if not self.frames:
- classes.append('noframe-traceback')
+ classes.append("noframe-traceback")
frames = []
else:
library_frames = sum(frame.is_library for frame in self.frames)
@@ -304,38 +313,37 @@ class Traceback(object):
if include_title:
if self.is_syntax_error:
- title = u'Syntax Error'
+ title = u"Syntax Error"
else:
- title = u'Traceback <em>(most recent call last)</em>:'
+ title = u"Traceback <em>(most recent call last)</em>:"
if self.is_syntax_error:
- description_wrapper = u'<pre class=syntaxerror>%s</pre>'
+ description_wrapper = u"<pre class=syntaxerror>%s</pre>"
else:
- description_wrapper = u'<blockquote>%s</blockquote>'
+ description_wrapper = u"<blockquote>%s</blockquote>"
return SUMMARY_HTML % {
- 'classes': u' '.join(classes),
- 'title': u'<h3>%s</h3>' % title if title else u'',
- 'frames': u'\n'.join(frames),
- 'description': description_wrapper % escape(self.exception)
+ "classes": u" ".join(classes),
+ "title": u"<h3>%s</h3>" % title if title else u"",
+ "frames": u"\n".join(frames),
+ "description": description_wrapper % escape(self.exception),
}
- def render_full(self, evalex=False, secret=None,
- evalex_trusted=True):
+ def render_full(self, evalex=False, secret=None, evalex_trusted=True):
"""Render the Full HTML page with the traceback info."""
exc = escape(self.exception)
return PAGE_HTML % {
- 'evalex': 'true' if evalex else 'false',
- 'evalex_trusted': 'true' if evalex_trusted else 'false',
- 'console': 'false',
- 'title': exc,
- 'exception': exc,
- 'exception_type': escape(self.exception_type),
- 'summary': self.render_summary(include_title=False),
- 'plaintext': escape(self.plaintext),
- 'plaintext_cs': re.sub('-{2,}', '-', self.plaintext),
- 'traceback_id': self.id,
- 'secret': secret
+ "evalex": "true" if evalex else "false",
+ "evalex_trusted": "true" if evalex_trusted else "false",
+ "console": "false",
+ "title": exc,
+ "exception": exc,
+ "exception_type": escape(self.exception_type),
+ "summary": self.render_summary(include_title=False),
+ "plaintext": escape(self.plaintext),
+ "plaintext_cs": re.sub("-{2,}", "-", self.plaintext),
+ "traceback_id": self.id,
+ "secret": secret,
}
@cached_property
@@ -418,10 +426,13 @@ class Group(object):
if self.info is not None:
out.append(u'<li><div class="exc-divider">%s:</div>' % self.info)
for frame in self.frames:
- out.append(u"<li%s>%s" % (
- u' title="%s"' % escape(frame.info) if frame.info else u"",
- frame.render(mark_lib=mark_lib)
- ))
+ out.append(
+ u"<li%s>%s"
+ % (
+ u' title="%s"' % escape(frame.info) if frame.info else u"",
+ frame.render(mark_lib=mark_lib),
+ )
+ )
return u"\n".join(out)
def render_text(self):
@@ -436,7 +447,6 @@ class Group(object):
class Frame(object):
-
"""A single frame in a traceback."""
def __init__(self, exc_type, exc_value, tb):
@@ -446,19 +456,19 @@ class Frame(object):
self.globals = tb.tb_frame.f_globals
fn = inspect.getsourcefile(tb) or inspect.getfile(tb)
- if fn[-4:] in ('.pyo', '.pyc'):
+ if fn[-4:] in (".pyo", ".pyc"):
fn = fn[:-1]
# if it's a file on the file system resolve the real filename.
if os.path.isfile(fn):
fn = os.path.realpath(fn)
self.filename = to_unicode(fn, get_filesystem_encoding())
- self.module = self.globals.get('__name__')
- self.loader = self.globals.get('__loader__')
+ self.module = self.globals.get("__name__")
+ self.loader = self.globals.get("__loader__")
self.code = tb.tb_frame.f_code
# support for paste's traceback extensions
- self.hide = self.locals.get('__traceback_hide__', False)
- info = self.locals.get('__traceback_info__')
+ self.hide = self.locals.get("__traceback_hide__", False)
+ info = self.locals.get("__traceback_info__")
if info is not None:
info = to_unicode(info, "utf-8", "replace")
self.info = info
@@ -466,11 +476,11 @@ class Frame(object):
def render(self, mark_lib=True):
"""Render a single frame in a traceback."""
return FRAME_HTML % {
- 'id': self.id,
- 'filename': escape(self.filename),
- 'lineno': self.lineno,
- 'function_name': escape(self.function_name),
- 'lines': self.render_line_context(),
+ "id": self.id,
+ "filename": escape(self.filename),
+ "lineno": self.lineno,
+ "function_name": escape(self.function_name),
+ "lines": self.render_line_context(),
"library": "library" if mark_lib and self.is_library else "",
}
@@ -485,7 +495,7 @@ class Frame(object):
self.filename,
self.lineno,
self.function_name,
- self.current_line.strip()
+ self.current_line.strip(),
)
def render_line_context(self):
@@ -497,34 +507,34 @@ class Frame(object):
stripped_line = line.strip()
prefix = len(line) - len(stripped_line)
rv.append(
- '<pre class="line %s"><span class="ws">%s</span>%s</pre>' % (
- cls, ' ' * prefix, escape(stripped_line) or ' '))
+ '<pre class="line %s"><span class="ws">%s</span>%s</pre>'
+ % (cls, " " * prefix, escape(stripped_line) or " ")
+ )
for line in before:
- render_line(line, 'before')
- render_line(current, 'current')
+ render_line(line, "before")
+ render_line(current, "current")
for line in after:
- render_line(line, 'after')
+ render_line(line, "after")
- return '\n'.join(rv)
+ return "\n".join(rv)
def get_annotated_lines(self):
"""Helper function that returns lines with extra information."""
lines = [Line(idx + 1, x) for idx, x in enumerate(self.sourcelines)]
# find function definition and mark lines
- if hasattr(self.code, 'co_firstlineno'):
+ if hasattr(self.code, "co_firstlineno"):
lineno = self.code.co_firstlineno - 1
while lineno > 0:
if _funcdef_re.match(lines[lineno].code):
break
lineno -= 1
try:
- offset = len(inspect.getblock([x.code + '\n' for x
- in lines[lineno:]]))
+ offset = len(inspect.getblock([x.code + "\n" for x in lines[lineno:]]))
except TokenError:
offset = 0
- for line in lines[lineno:lineno + offset]:
+ for line in lines[lineno : lineno + offset]:
line.in_frame = True
# mark current line
@@ -535,12 +545,12 @@ class Frame(object):
return lines
- def eval(self, code, mode='single'):
+ def eval(self, code, mode="single"):
"""Evaluate code in the context of the frame."""
if isinstance(code, string_types):
if PY2 and isinstance(code, text_type): # noqa
- code = UTF8_COOKIE + code.encode('utf-8')
- code = compile(code, '<interactive>', mode)
+ code = UTF8_COOKIE + code.encode("utf-8")
+ code = compile(code, "<interactive>", mode)
return eval(code, self.globals, self.locals)
@cached_property
@@ -550,9 +560,9 @@ class Frame(object):
source = None
if self.loader is not None:
try:
- if hasattr(self.loader, 'get_source'):
+ if hasattr(self.loader, "get_source"):
source = self.loader.get_source(self.module)
- elif hasattr(self.loader, 'get_source_by_code'):
+ elif hasattr(self.loader, "get_source_by_code"):
source = self.loader.get_source_by_code(self.code)
except Exception:
# we munch the exception so that we don't cause troubles
@@ -561,8 +571,7 @@ class Frame(object):
if source is None:
try:
- f = open(to_native(self.filename, get_filesystem_encoding()),
- mode='rb')
+ f = open(to_native(self.filename, get_filesystem_encoding()), mode="rb")
except IOError:
return []
try:
@@ -576,7 +585,7 @@ class Frame(object):
# yes. it should be ascii, but we don't want to reject too many
# characters in the debugger if something breaks
- charset = 'utf-8'
+ charset = "utf-8"
if source.startswith(UTF8_COOKIE):
source = source[3:]
else:
@@ -593,25 +602,21 @@ class Frame(object):
try:
codecs.lookup(charset)
except LookupError:
- charset = 'utf-8'
+ charset = "utf-8"
- return source.decode(charset, 'replace').splitlines()
+ return source.decode(charset, "replace").splitlines()
def get_context_lines(self, context=5):
- before = self.sourcelines[self.lineno - context - 1:self.lineno - 1]
- past = self.sourcelines[self.lineno:self.lineno + context]
- return (
- before,
- self.current_line,
- past,
- )
+ before = self.sourcelines[self.lineno - context - 1 : self.lineno - 1]
+ past = self.sourcelines[self.lineno : self.lineno + context]
+ return (before, self.current_line, past)
@property
def current_line(self):
try:
return self.sourcelines[self.lineno - 1]
except IndexError:
- return u''
+ return u""
@cached_property
def console(self):