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 ADDED
@@ -0,0 +1,3 @@
1
+ from backlash.debug import DebuggedApplication
2
+ from backlash.tracing.errors import TraceErrorsMiddleware
3
+ from backlash.tracing.slowrequests import TraceSlowRequestsMiddleware
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