diff options
Diffstat (limited to 'src/werkzeug/debug/tbtools.py')
| -rw-r--r-- | src/werkzeug/debug/tbtools.py | 265 |
1 files changed, 135 insertions, 130 deletions
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): |
