backlash 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- backlash/__init__.py +3 -0
- backlash/_compat.py +78 -0
- backlash/console.py +213 -0
- backlash/debug.py +193 -0
- backlash/frtools.py +27 -0
- backlash/repr.py +305 -0
- backlash/statics/FONT_LICENSE +202 -0
- backlash/statics/console.png +0 -0
- backlash/statics/debugger.js +202 -0
- backlash/statics/jquery.js +167 -0
- backlash/statics/less.png +0 -0
- backlash/statics/more.png +0 -0
- backlash/statics/opensans.ttf +0 -0
- backlash/statics/source.png +0 -0
- backlash/statics/style.css +113 -0
- backlash/tbtools.py +526 -0
- backlash/trace_errors/__init__.py +8 -0
- backlash/trace_errors/sentry.py +6 -0
- backlash/tracing/__init__.py +1 -0
- backlash/tracing/errors/__init__.py +1 -0
- backlash/tracing/errors/middleware.py +85 -0
- backlash/tracing/reporters/__init__.py +1 -0
- backlash/tracing/reporters/mail.py +126 -0
- backlash/tracing/reporters/sentry.py +40 -0
- backlash/tracing/slowrequests/__init__.py +1 -0
- backlash/tracing/slowrequests/middleware.py +113 -0
- backlash/tracing/slowrequests/timer.py +107 -0
- backlash/utils.py +42 -0
- backlash-0.4.0.dist-info/METADATA +135 -0
- backlash-0.4.0.dist-info/RECORD +34 -0
- backlash-0.4.0.dist-info/WHEEL +5 -0
- backlash-0.4.0.dist-info/licenses/AUTHORS +15 -0
- backlash-0.4.0.dist-info/licenses/LICENSE +7 -0
- backlash-0.4.0.dist-info/top_level.txt +1 -0
backlash/__init__.py
ADDED
backlash/_compat.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import types
|
|
3
|
+
|
|
4
|
+
# True if we are running on Python 3.
|
|
5
|
+
PY3 = sys.version_info[0] == 3
|
|
6
|
+
PY2 = sys.version_info[0] == 2
|
|
7
|
+
|
|
8
|
+
if PY3: # pragma: no cover
|
|
9
|
+
string_types = str,
|
|
10
|
+
integer_types = int,
|
|
11
|
+
class_types = type,
|
|
12
|
+
text_type = str
|
|
13
|
+
binary_type = bytes
|
|
14
|
+
long = int
|
|
15
|
+
else:
|
|
16
|
+
string_types = basestring,
|
|
17
|
+
integer_types = (int, long)
|
|
18
|
+
class_types = (type, types.ClassType)
|
|
19
|
+
text_type = unicode
|
|
20
|
+
binary_type = str
|
|
21
|
+
long = long
|
|
22
|
+
|
|
23
|
+
def text_(s, encoding='utf-8', errors='strict'):
|
|
24
|
+
if isinstance(s, binary_type):
|
|
25
|
+
return s.decode(encoding, errors)
|
|
26
|
+
return s # pragma: no cover
|
|
27
|
+
|
|
28
|
+
def bytes_(s, encoding='utf-8', errors='strict'):
|
|
29
|
+
if isinstance(s, text_type):
|
|
30
|
+
return s.encode(encoding, errors)
|
|
31
|
+
return s
|
|
32
|
+
|
|
33
|
+
if PY3: # pragma: no cover
|
|
34
|
+
def native_(s, encoding='latin-1', errors='strict'):
|
|
35
|
+
if isinstance(s, text_type):
|
|
36
|
+
return s
|
|
37
|
+
return str(s, encoding, errors)
|
|
38
|
+
else:
|
|
39
|
+
def native_(s, encoding='latin-1', errors='strict'):
|
|
40
|
+
if isinstance(s, text_type): # pragma: no cover
|
|
41
|
+
return s.encode(encoding, errors)
|
|
42
|
+
return str(s)
|
|
43
|
+
|
|
44
|
+
if PY3: # pragma: no cover
|
|
45
|
+
def iteritems_(d):
|
|
46
|
+
return d.items()
|
|
47
|
+
else:
|
|
48
|
+
def iteritems_(d):
|
|
49
|
+
return d.iteritems()
|
|
50
|
+
|
|
51
|
+
if PY3: # pragma: no cover
|
|
52
|
+
import builtins
|
|
53
|
+
exec_ = getattr(builtins, "exec")
|
|
54
|
+
def reraise(exc_info):
|
|
55
|
+
etype, exc, tb = exc_info
|
|
56
|
+
if exc.__traceback__ is not tb:
|
|
57
|
+
raise exc.with_traceback(tb)
|
|
58
|
+
raise exc
|
|
59
|
+
else: # pragma: no cover
|
|
60
|
+
def exec_(code, globs=None, locs=None):
|
|
61
|
+
"""Execute code in a namespace."""
|
|
62
|
+
if globs is None:
|
|
63
|
+
frame = sys._getframe(1)
|
|
64
|
+
globs = frame.f_globals
|
|
65
|
+
if locs is None:
|
|
66
|
+
locs = frame.f_locals
|
|
67
|
+
del frame
|
|
68
|
+
elif locs is None:
|
|
69
|
+
locs = globs
|
|
70
|
+
exec("""exec code in globs, locs""")
|
|
71
|
+
exec_("""def reraise(exc_info):
|
|
72
|
+
raise exc_info[0], exc_info[1], exc_info[2]
|
|
73
|
+
""")
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
from urllib2 import urlopen
|
|
77
|
+
except ImportError:
|
|
78
|
+
from urllib.request import urlopen
|
backlash/console.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
werkzeug.debug.console
|
|
4
|
+
~~~~~~~~~~~~~~~~~~~~~~
|
|
5
|
+
|
|
6
|
+
Interactive console support.
|
|
7
|
+
|
|
8
|
+
:copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
|
|
9
|
+
:license: BSD.
|
|
10
|
+
"""
|
|
11
|
+
import sys
|
|
12
|
+
import code
|
|
13
|
+
from types import CodeType
|
|
14
|
+
import threading
|
|
15
|
+
|
|
16
|
+
from backlash._compat import exec_, text_, binary_type
|
|
17
|
+
from backlash.utils import escape
|
|
18
|
+
from backlash.repr import debug_repr, dump, helper
|
|
19
|
+
|
|
20
|
+
_local = threading.local()
|
|
21
|
+
|
|
22
|
+
class HTMLStringO(object):
|
|
23
|
+
"""A StringO version that HTML escapes on write."""
|
|
24
|
+
|
|
25
|
+
def __init__(self):
|
|
26
|
+
self._buffer = []
|
|
27
|
+
|
|
28
|
+
def isatty(self):
|
|
29
|
+
return False
|
|
30
|
+
|
|
31
|
+
def close(self):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
def flush(self):
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
def seek(self, n, mode=0):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
def readline(self):
|
|
41
|
+
if len(self._buffer) == 0:
|
|
42
|
+
return ''
|
|
43
|
+
ret = self._buffer[0]
|
|
44
|
+
del self._buffer[0]
|
|
45
|
+
return ret
|
|
46
|
+
|
|
47
|
+
def reset(self):
|
|
48
|
+
val = ''.join(self._buffer)
|
|
49
|
+
del self._buffer[:]
|
|
50
|
+
return val
|
|
51
|
+
|
|
52
|
+
def _write(self, x):
|
|
53
|
+
if isinstance(x, binary_type):
|
|
54
|
+
x = text_(x, 'utf-8', 'replace')
|
|
55
|
+
self._buffer.append(x)
|
|
56
|
+
|
|
57
|
+
def write(self, x):
|
|
58
|
+
self._write(escape(x))
|
|
59
|
+
|
|
60
|
+
def writelines(self, x):
|
|
61
|
+
self._write(escape(''.join(x)))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ThreadedStream(object):
|
|
65
|
+
"""Thread-local wrapper for sys.stdout for the interactive console."""
|
|
66
|
+
|
|
67
|
+
def push():
|
|
68
|
+
if not isinstance(sys.stdout, ThreadedStream):
|
|
69
|
+
sys.stdout = ThreadedStream()
|
|
70
|
+
_local.stream = HTMLStringO()
|
|
71
|
+
push = staticmethod(push)
|
|
72
|
+
|
|
73
|
+
def fetch():
|
|
74
|
+
try:
|
|
75
|
+
stream = _local.stream
|
|
76
|
+
except AttributeError:
|
|
77
|
+
return ''
|
|
78
|
+
return stream.reset()
|
|
79
|
+
fetch = staticmethod(fetch)
|
|
80
|
+
|
|
81
|
+
def displayhook(obj):
|
|
82
|
+
try:
|
|
83
|
+
stream = _local.stream
|
|
84
|
+
except AttributeError:
|
|
85
|
+
return _displayhook(obj)
|
|
86
|
+
# stream._write bypasses escaping as debug_repr is
|
|
87
|
+
# already generating HTML for us.
|
|
88
|
+
if obj is not None:
|
|
89
|
+
stream._write(debug_repr(obj))
|
|
90
|
+
displayhook = staticmethod(displayhook)
|
|
91
|
+
|
|
92
|
+
def __setattr__(self, name, value):
|
|
93
|
+
raise AttributeError('read only attribute %s' % name)
|
|
94
|
+
|
|
95
|
+
def __dir__(self):
|
|
96
|
+
return dir(sys.__stdout__)
|
|
97
|
+
|
|
98
|
+
def __getattribute__(self, name):
|
|
99
|
+
if name == '__members__':
|
|
100
|
+
return dir(sys.__stdout__)
|
|
101
|
+
try:
|
|
102
|
+
stream = _local.stream
|
|
103
|
+
except AttributeError:
|
|
104
|
+
stream = sys.__stdout__
|
|
105
|
+
return getattr(stream, name)
|
|
106
|
+
|
|
107
|
+
def __repr__(self):
|
|
108
|
+
return repr(sys.__stdout__)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
# add the threaded stream as display hook
|
|
112
|
+
_displayhook = sys.displayhook
|
|
113
|
+
sys.displayhook = ThreadedStream.displayhook
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class _ConsoleLoader(object):
|
|
117
|
+
|
|
118
|
+
def __init__(self):
|
|
119
|
+
self._storage = {}
|
|
120
|
+
|
|
121
|
+
def register(self, code, source):
|
|
122
|
+
self._storage[id(code)] = source
|
|
123
|
+
# register code objects of wrapped functions too.
|
|
124
|
+
for var in code.co_consts:
|
|
125
|
+
if isinstance(var, CodeType):
|
|
126
|
+
self._storage[id(var)] = source
|
|
127
|
+
|
|
128
|
+
def get_source_by_code(self, code):
|
|
129
|
+
try:
|
|
130
|
+
return self._storage[id(code)]
|
|
131
|
+
except KeyError:
|
|
132
|
+
pass
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _wrap_compiler(console):
|
|
136
|
+
compile = console.compile
|
|
137
|
+
def func(source, filename, symbol):
|
|
138
|
+
code = compile(source, filename, symbol)
|
|
139
|
+
console.loader.register(code, source)
|
|
140
|
+
return code
|
|
141
|
+
console.compile = func
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class _InteractiveConsole(code.InteractiveInterpreter):
|
|
145
|
+
|
|
146
|
+
def __init__(self, globals, locals, context):
|
|
147
|
+
code.InteractiveInterpreter.__init__(self, locals)
|
|
148
|
+
self.globals = dict(globals)
|
|
149
|
+
self.globals['dump'] = dump
|
|
150
|
+
self.globals['help'] = helper
|
|
151
|
+
self.globals['ctx'] = context
|
|
152
|
+
self.globals['__loader__'] = self.loader = _ConsoleLoader()
|
|
153
|
+
self.more = False
|
|
154
|
+
self.buffer = []
|
|
155
|
+
_wrap_compiler(self)
|
|
156
|
+
|
|
157
|
+
def runsource(self, source):
|
|
158
|
+
source = source.rstrip() + '\n'
|
|
159
|
+
ThreadedStream.push()
|
|
160
|
+
prompt = self.more and '... ' or '>>> '
|
|
161
|
+
try:
|
|
162
|
+
source_to_eval = ''.join(self.buffer + [source])
|
|
163
|
+
if code.InteractiveInterpreter.runsource(self,
|
|
164
|
+
source_to_eval, '<debugger>', 'single'):
|
|
165
|
+
self.more = True
|
|
166
|
+
self.buffer.append(source)
|
|
167
|
+
else:
|
|
168
|
+
self.more = False
|
|
169
|
+
del self.buffer[:]
|
|
170
|
+
finally:
|
|
171
|
+
output = ThreadedStream.fetch()
|
|
172
|
+
return prompt + source + output
|
|
173
|
+
|
|
174
|
+
def runcode(self, code):
|
|
175
|
+
try:
|
|
176
|
+
exec_(code, self.globals, self.locals)
|
|
177
|
+
except Exception:
|
|
178
|
+
self.showtraceback()
|
|
179
|
+
|
|
180
|
+
def showtraceback(self):
|
|
181
|
+
from backlash.tbtools import get_current_traceback
|
|
182
|
+
tb = get_current_traceback(skip=1)
|
|
183
|
+
sys.stdout._write(tb.render_summary())
|
|
184
|
+
|
|
185
|
+
def showsyntaxerror(self, filename=None):
|
|
186
|
+
from backlash.tbtools import get_current_traceback
|
|
187
|
+
tb = get_current_traceback(skip=4)
|
|
188
|
+
sys.stdout._write(tb.render_summary())
|
|
189
|
+
|
|
190
|
+
def write(self, data):
|
|
191
|
+
sys.stdout.write(data)
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class Console(object):
|
|
195
|
+
"""An interactive console."""
|
|
196
|
+
|
|
197
|
+
def __init__(self, globals=None, locals=None, context=None):
|
|
198
|
+
if locals is None:
|
|
199
|
+
locals = {}
|
|
200
|
+
if globals is None:
|
|
201
|
+
globals = {}
|
|
202
|
+
self._ipy = _InteractiveConsole(globals, locals, context)
|
|
203
|
+
|
|
204
|
+
def eval(self, code):
|
|
205
|
+
return self._ipy.runsource(code)
|
|
206
|
+
|
|
207
|
+
class _ConsoleFrame(object):
|
|
208
|
+
"""Helper class so that we can reuse the frame console code for the
|
|
209
|
+
standalone console.
|
|
210
|
+
"""
|
|
211
|
+
def __init__(self, namespace):
|
|
212
|
+
self.console = Console(namespace)
|
|
213
|
+
self.id = 0
|
backlash/debug.py
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
werkzeug.debug
|
|
4
|
+
~~~~~~~~~~~~~~
|
|
5
|
+
|
|
6
|
+
WSGI application traceback debugger.
|
|
7
|
+
|
|
8
|
+
:copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
|
|
9
|
+
:license: BSD.
|
|
10
|
+
"""
|
|
11
|
+
import mimetypes
|
|
12
|
+
import json
|
|
13
|
+
from os.path import join, dirname, basename, isfile
|
|
14
|
+
|
|
15
|
+
from webob import Request, Response
|
|
16
|
+
|
|
17
|
+
from backlash.tbtools import get_current_traceback, render_console_html
|
|
18
|
+
from backlash.console import Console
|
|
19
|
+
from backlash.utils import gen_salt, RequestContext
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
log = logging.getLogger('backlash')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _ConsoleFrame(object):
|
|
26
|
+
"""Helper class so that we can reuse the frame console code for the
|
|
27
|
+
standalone console.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, namespace):
|
|
31
|
+
self.console = Console(namespace)
|
|
32
|
+
self.id = 0
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class DebuggedApplication(object):
|
|
36
|
+
"""Enables debugging support for a given application::
|
|
37
|
+
|
|
38
|
+
from backlash.debug import DebuggedApplication
|
|
39
|
+
from myapp import app
|
|
40
|
+
app = DebuggedApplication(app, evalex=True)
|
|
41
|
+
|
|
42
|
+
The `evalex` keyword argument allows evaluating expressions in a
|
|
43
|
+
traceback's frame context.
|
|
44
|
+
|
|
45
|
+
:param app: the WSGI application to run debugged.
|
|
46
|
+
:param evalex: enable exception evaluation feature (interactive
|
|
47
|
+
debugging). This requires a non-forking server.
|
|
48
|
+
:param console_path: the URL for a general purpose console.
|
|
49
|
+
:param console_init_func: the function that is executed before starting
|
|
50
|
+
the general purpose console. The return value
|
|
51
|
+
is used as initial namespace.
|
|
52
|
+
:param show_hidden_frames: by default hidden traceback frames are skipped.
|
|
53
|
+
You can show them by setting this parameter
|
|
54
|
+
to `True`.
|
|
55
|
+
"""
|
|
56
|
+
def __init__(self, app, evalex=True, console_path='/__console__',
|
|
57
|
+
console_init_func=None, show_hidden_frames=False,
|
|
58
|
+
lodgeit_url=None, context_injectors=None):
|
|
59
|
+
if not console_init_func:
|
|
60
|
+
console_init_func = dict
|
|
61
|
+
self.app = app
|
|
62
|
+
self.evalex = evalex
|
|
63
|
+
self.frames = {}
|
|
64
|
+
self.tracebacks = {}
|
|
65
|
+
self.console_path = console_path
|
|
66
|
+
self.console_init_func = console_init_func
|
|
67
|
+
self.show_hidden_frames = show_hidden_frames
|
|
68
|
+
self.secret = gen_salt(20)
|
|
69
|
+
self.context_injectors = context_injectors or []
|
|
70
|
+
|
|
71
|
+
if lodgeit_url is not None:
|
|
72
|
+
from warnings import warn
|
|
73
|
+
warn(DeprecationWarning('Backlash now pastes into gists.'))
|
|
74
|
+
|
|
75
|
+
def debug_application(self, environ, start_response):
|
|
76
|
+
"""Run the application and conserve the traceback frames."""
|
|
77
|
+
app_iter = None
|
|
78
|
+
try:
|
|
79
|
+
try:
|
|
80
|
+
app_iter = self.app(environ, start_response)
|
|
81
|
+
for item in app_iter:
|
|
82
|
+
yield item
|
|
83
|
+
finally:
|
|
84
|
+
if hasattr(app_iter, 'close'):
|
|
85
|
+
app_iter.close()
|
|
86
|
+
except Exception:
|
|
87
|
+
context = RequestContext({'environ':dict(environ)})
|
|
88
|
+
for injector in self.context_injectors:
|
|
89
|
+
context.update(injector(environ))
|
|
90
|
+
|
|
91
|
+
traceback = get_current_traceback(skip=1, show_hidden_frames=self.show_hidden_frames,
|
|
92
|
+
context=context)
|
|
93
|
+
for frame in traceback.frames:
|
|
94
|
+
self.frames[frame.id] = frame
|
|
95
|
+
self.tracebacks[traceback.id] = traceback
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
start_response('500 INTERNAL SERVER ERROR', [
|
|
99
|
+
('Content-Type', 'text/html; charset=utf-8'),
|
|
100
|
+
# Disable Chrome's XSS protection, the debug
|
|
101
|
+
# output can cause false-positives.
|
|
102
|
+
('X-XSS-Protection', '0'),
|
|
103
|
+
])
|
|
104
|
+
except Exception:
|
|
105
|
+
# if we end up here there has been output but an error
|
|
106
|
+
# occurred. in that situation we can do nothing fancy any
|
|
107
|
+
# more, better log something into the error log and fall
|
|
108
|
+
# back gracefully.
|
|
109
|
+
environ['wsgi.errors'].write(
|
|
110
|
+
'Debugging middleware caught exception in streamed '
|
|
111
|
+
'response at a point where response headers were already '
|
|
112
|
+
'sent.\n')
|
|
113
|
+
else:
|
|
114
|
+
yield traceback.render_full(
|
|
115
|
+
evalex=self.evalex,
|
|
116
|
+
secret=self.secret
|
|
117
|
+
).encode('utf-8', 'replace')
|
|
118
|
+
|
|
119
|
+
# This will lead to double logging in case backlash logger is set to DEBUG
|
|
120
|
+
# but this is actually wanted as some environments, like WebTest, swallow
|
|
121
|
+
# wsgi.environ making the traceback totally disappear.
|
|
122
|
+
log.debug(traceback.plaintext)
|
|
123
|
+
traceback.log(environ['wsgi.errors'])
|
|
124
|
+
|
|
125
|
+
def execute_command(self, request, command, frame):
|
|
126
|
+
"""Execute a command in a console."""
|
|
127
|
+
return Response(frame.console.eval(command), content_type='text/html')
|
|
128
|
+
|
|
129
|
+
def display_console(self, request):
|
|
130
|
+
"""Display a standalone shell."""
|
|
131
|
+
if 0 not in self.frames:
|
|
132
|
+
self.frames[0] = _ConsoleFrame(self.console_init_func())
|
|
133
|
+
return Response(render_console_html(secret=self.secret),
|
|
134
|
+
content_type='text/html')
|
|
135
|
+
|
|
136
|
+
def paste_traceback(self, request, traceback):
|
|
137
|
+
"""Paste the traceback and return a JSON response."""
|
|
138
|
+
rv = traceback.paste()
|
|
139
|
+
return Response(json.dumps(rv), content_type='application/json')
|
|
140
|
+
|
|
141
|
+
def get_source(self, request, frame):
|
|
142
|
+
"""Render the source viewer."""
|
|
143
|
+
return Response(frame.render_source(), content_type='text/html')
|
|
144
|
+
|
|
145
|
+
def get_resource(self, request, filename):
|
|
146
|
+
"""Return a static resource from the shared folder."""
|
|
147
|
+
filename = join(dirname(__file__), 'statics', basename(filename))
|
|
148
|
+
if isfile(filename):
|
|
149
|
+
mimetype = mimetypes.guess_type(filename)[0]\
|
|
150
|
+
or 'application/octet-stream'
|
|
151
|
+
f = open(filename, 'rb')
|
|
152
|
+
try:
|
|
153
|
+
return Response(f.read(), content_type=mimetype)
|
|
154
|
+
finally:
|
|
155
|
+
f.close()
|
|
156
|
+
return Response('Not Found', status=404)
|
|
157
|
+
|
|
158
|
+
def __call__(self, environ, start_response):
|
|
159
|
+
"""Dispatch the requests."""
|
|
160
|
+
# important: don't ever access a function here that reads the incoming
|
|
161
|
+
# form data! Otherwise the application won't have access to that data
|
|
162
|
+
# any more!
|
|
163
|
+
request = Request(environ)
|
|
164
|
+
response = self.debug_application
|
|
165
|
+
if request.GET.get('__debugger__') == 'yes':
|
|
166
|
+
cmd = request.GET.get('cmd')
|
|
167
|
+
arg = request.GET.get('f')
|
|
168
|
+
secret = request.GET.get('s')
|
|
169
|
+
|
|
170
|
+
tb = request.GET.get('tb')
|
|
171
|
+
if tb is not None:
|
|
172
|
+
tb = int(tb)
|
|
173
|
+
traceback = self.tracebacks.get(tb)
|
|
174
|
+
|
|
175
|
+
frm = request.GET.get('frm')
|
|
176
|
+
if frm is not None:
|
|
177
|
+
frm = int(frm)
|
|
178
|
+
frame = self.frames.get(frm)
|
|
179
|
+
|
|
180
|
+
if cmd == 'resource' and arg:
|
|
181
|
+
response = self.get_resource(request, arg)
|
|
182
|
+
elif cmd == 'paste' and traceback is not None and\
|
|
183
|
+
secret == self.secret:
|
|
184
|
+
response = self.paste_traceback(request, traceback)
|
|
185
|
+
elif cmd == 'source' and frame and self.secret == secret:
|
|
186
|
+
response = self.get_source(request, frame)
|
|
187
|
+
elif self.evalex and cmd is not None and frame is not None and\
|
|
188
|
+
self.secret == secret:
|
|
189
|
+
response = self.execute_command(request, cmd, frame)
|
|
190
|
+
elif self.evalex and self.console_path is not None and\
|
|
191
|
+
request.path == self.console_path:
|
|
192
|
+
response = self.display_console(request)
|
|
193
|
+
return response(environ, start_response)
|
backlash/frtools.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import sys, inspect
|
|
2
|
+
from .tbtools import Traceback, Frame
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DumpThread(Exception):
|
|
6
|
+
backlash_event = True
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_thread_stack(thread_id, description='', error_type=DumpThread, context=None):
|
|
10
|
+
if isinstance(error_type, str):
|
|
11
|
+
error_type = type(error_type, (DumpThread,), {})
|
|
12
|
+
# Hack to prevent traceback module from printing
|
|
13
|
+
# backlash.frtools.ExceptionName instead of just ExceptionName
|
|
14
|
+
error_type.__module__ = '__main__'
|
|
15
|
+
|
|
16
|
+
e = error_type(description)
|
|
17
|
+
tb = Traceback(error_type, e, [], context=context)
|
|
18
|
+
|
|
19
|
+
f = sys._current_frames()[thread_id]
|
|
20
|
+
n = 0
|
|
21
|
+
while f is not None:
|
|
22
|
+
if inspect.isframe(f):
|
|
23
|
+
tb.frames.insert(0, Frame(error_type, e, f, context))
|
|
24
|
+
f = f.f_back
|
|
25
|
+
n += 1
|
|
26
|
+
|
|
27
|
+
return tb
|