started work on backend
This commit is contained in:
1 parent
a1a8bca34b
commit
9f9a7e4974
4032 files changed
+745079
No files matched your search
@@ -0,0 +1,470 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
werkzeug.debug
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
WSGI application traceback debugger.
|
||||
|
||||
:copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
|
||||
:license: BSD, see LICENSE for more details.
|
||||
"""
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import uuid
|
||||
import json
|
||||
import time
|
||||
import getpass
|
||||
import hashlib
|
||||
import mimetypes
|
||||
from itertools import chain
|
||||
from os.path import join, dirname, basename, isfile
|
||||
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
|
||||
|
||||
|
||||
# DEPRECATED
|
||||
#: import this here because it once was documented as being available
|
||||
#: from this module. In case there are users left ...
|
||||
from werkzeug.debug.repr import debug_repr # noqa
|
||||
|
||||
|
||||
# A week
|
||||
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]
|
||||
|
||||
|
||||
_machine_id = None
|
||||
|
||||
|
||||
def get_machine_id():
|
||||
global _machine_id
|
||||
rv = _machine_id
|
||||
if rv is not None:
|
||||
return rv
|
||||
|
||||
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':
|
||||
try:
|
||||
with open(filename, 'rb') as f:
|
||||
return f.readline().strip()
|
||||
except IOError:
|
||||
continue
|
||||
|
||||
# On OS X we can use the computer's serial number assuming that
|
||||
# ioreg exists and can spit out that information.
|
||||
try:
|
||||
# Also catch import errors: subprocess may not be available, e.g.
|
||||
# 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]
|
||||
match = re.search(b'"serial-number" = <([^>]+)', dump)
|
||||
if match is not None:
|
||||
return match.group(1)
|
||||
except (OSError, ImportError):
|
||||
pass
|
||||
|
||||
# On Windows we can use winreg to get the machine guid
|
||||
wr = None
|
||||
try:
|
||||
import winreg as wr
|
||||
except ImportError:
|
||||
try:
|
||||
import _winreg as wr
|
||||
except ImportError:
|
||||
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')
|
||||
if (wrType == wr.REG_SZ):
|
||||
return machineGuid.encode('utf-8')
|
||||
else:
|
||||
return machineGuid
|
||||
except WindowsError:
|
||||
pass
|
||||
|
||||
_machine_id = rv = _generate()
|
||||
return rv
|
||||
|
||||
|
||||
class _ConsoleFrame(object):
|
||||
|
||||
"""Helper class so that we can reuse the frame console code for the
|
||||
standalone console.
|
||||
"""
|
||||
|
||||
def __init__(self, namespace):
|
||||
self.console = Console(namespace)
|
||||
self.id = 0
|
||||
|
||||
|
||||
def get_pin_and_cookie_name(app):
|
||||
"""Given an application object this returns a semi-stable 9 digit pin
|
||||
code and a random key. The hope is that this is stable between
|
||||
restarts to not make debugging particularly frustrating. If the pin
|
||||
was forcefully disabled this returns `None`.
|
||||
|
||||
Second item in the resulting tuple is the cookie name for remembering.
|
||||
"""
|
||||
pin = os.environ.get('WERKZEUG_DEBUG_PIN')
|
||||
rv = None
|
||||
num = None
|
||||
|
||||
# Pin was explicitly disabled
|
||||
if pin == 'off':
|
||||
return None, None
|
||||
|
||||
# Pin was provided explicitly
|
||||
if pin is not None and pin.replace('-', '').isdigit():
|
||||
# If there are separators in the pin, return it directly
|
||||
if '-' in pin:
|
||||
rv = pin
|
||||
else:
|
||||
num = pin
|
||||
|
||||
modname = getattr(app, '__module__',
|
||||
getattr(app.__class__, '__module__'))
|
||||
|
||||
try:
|
||||
# `getpass.getuser()` imports the `pwd` module,
|
||||
# which does not exist in the Google App Engine sandbox.
|
||||
username = getpass.getuser()
|
||||
except ImportError:
|
||||
username = None
|
||||
|
||||
mod = sys.modules.get(modname)
|
||||
|
||||
# This information only exists to make the cookie unique on the
|
||||
# computer, not as a security feature.
|
||||
probably_public_bits = [
|
||||
username,
|
||||
modname,
|
||||
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(),
|
||||
]
|
||||
|
||||
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')
|
||||
h.update(bit)
|
||||
h.update(b'cookiesalt')
|
||||
|
||||
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]
|
||||
|
||||
# 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))
|
||||
break
|
||||
else:
|
||||
rv = num
|
||||
|
||||
return rv, cookie_name
|
||||
|
||||
|
||||
class DebuggedApplication(object):
|
||||
"""Enables debugging support for a given application::
|
||||
|
||||
from werkzeug.debug import DebuggedApplication
|
||||
from myapp import app
|
||||
app = DebuggedApplication(app, evalex=True)
|
||||
|
||||
The `evalex` keyword argument allows evaluating expressions in a
|
||||
traceback's frame context.
|
||||
|
||||
.. versionadded:: 0.9
|
||||
The `lodgeit_url` parameter was deprecated.
|
||||
|
||||
:param app: the WSGI application to run debugged.
|
||||
:param evalex: enable exception evaluation feature (interactive
|
||||
debugging). This requires a non-forking server.
|
||||
:param request_key: The key that points to the request object in ths
|
||||
environment. This parameter is ignored in current
|
||||
versions.
|
||||
:param console_path: the URL for a general purpose console.
|
||||
:param console_init_func: the function that is executed before starting
|
||||
the general purpose console. The return value
|
||||
is used as initial namespace.
|
||||
:param show_hidden_frames: by default hidden traceback frames are skipped.
|
||||
You can show them by setting this parameter
|
||||
to `True`.
|
||||
:param pin_security: can be used to disable the pin based security system.
|
||||
: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):
|
||||
if lodgeit_url is not None:
|
||||
from warnings import warn
|
||||
warn(DeprecationWarning('Werkzeug now pastes into gists.'))
|
||||
if not console_init_func:
|
||||
console_init_func = None
|
||||
self.app = app
|
||||
self.evalex = evalex
|
||||
self.frames = {}
|
||||
self.tracebacks = {}
|
||||
self.request_key = request_key
|
||||
self.console_path = console_path
|
||||
self.console_init_func = console_init_func
|
||||
self.show_hidden_frames = show_hidden_frames
|
||||
self.secret = gen_salt(20)
|
||||
self._failed_pin_auth = 0
|
||||
|
||||
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 self.pin is None:
|
||||
_log('warning', ' * Debugger PIN disabled. '
|
||||
'DEBUGGER UNSECURED!')
|
||||
else:
|
||||
_log('info', ' * Debugger PIN: %s' % self.pin)
|
||||
else:
|
||||
self.pin = None
|
||||
|
||||
def _get_pin(self):
|
||||
if not hasattr(self, '_pin'):
|
||||
self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app)
|
||||
return self._pin
|
||||
|
||||
def _set_pin(self, value):
|
||||
self._pin = value
|
||||
|
||||
pin = property(_get_pin, _set_pin)
|
||||
del _get_pin, _set_pin
|
||||
|
||||
@property
|
||||
def pin_cookie_name(self):
|
||||
"""The name of the pin cookie."""
|
||||
if not hasattr(self, '_pin_cookie'):
|
||||
self._pin, self._pin_cookie = get_pin_and_cookie_name(self.app)
|
||||
return self._pin_cookie
|
||||
|
||||
def debug_application(self, environ, start_response):
|
||||
"""Run the application and conserve the traceback frames."""
|
||||
app_iter = None
|
||||
try:
|
||||
app_iter = self.app(environ, start_response)
|
||||
for item in app_iter:
|
||||
yield item
|
||||
if hasattr(app_iter, 'close'):
|
||||
app_iter.close()
|
||||
except Exception:
|
||||
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)
|
||||
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'),
|
||||
])
|
||||
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')
|
||||
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')
|
||||
|
||||
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')
|
||||
|
||||
def display_console(self, request):
|
||||
"""Display a standalone shell."""
|
||||
if 0 not in self.frames:
|
||||
if self.console_init_func is None:
|
||||
ns = {}
|
||||
else:
|
||||
ns = dict(self.console_init_func())
|
||||
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')
|
||||
|
||||
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')
|
||||
|
||||
def get_resource(self, request, filename):
|
||||
"""Return a static resource from the shared folder."""
|
||||
filename = join(dirname(__file__), 'shared', basename(filename))
|
||||
if isfile(filename):
|
||||
mimetype = mimetypes.guess_type(filename)[0] \
|
||||
or 'application/octet-stream'
|
||||
f = open(filename, 'rb')
|
||||
try:
|
||||
return Response(f.read(), mimetype=mimetype)
|
||||
finally:
|
||||
f.close()
|
||||
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
|
||||
request is trusted on a pin/cookie basis and returns `False` if not.
|
||||
Additionally if the cookie's stored pin hash is wrong it will return
|
||||
`None` so that appropriate action can be taken.
|
||||
"""
|
||||
if self.pin is None:
|
||||
return True
|
||||
val = parse_cookie(environ).get(self.pin_cookie_name)
|
||||
if not val or '|' not in val:
|
||||
return False
|
||||
ts, pin_hash = val.split('|', 1)
|
||||
if not ts.isdigit():
|
||||
return False
|
||||
if pin_hash != hash_pin(self.pin):
|
||||
return None
|
||||
return (time.time() - PIN_TIME) < int(ts)
|
||||
|
||||
def _fail_pin_auth(self):
|
||||
time.sleep(self._failed_pin_auth > 5 and 5.0 or 0.5)
|
||||
self._failed_pin_auth += 1
|
||||
|
||||
def pin_auth(self, request):
|
||||
"""Authenticates with the pin."""
|
||||
exhausted = False
|
||||
auth = False
|
||||
trust = self.check_pin_trust(request.environ)
|
||||
|
||||
# If the trust return value is `None` it means that the cookie is
|
||||
# set but the stored pin hash value is bad. This means that the
|
||||
# pin was changed. In this case we count a bad auth and unset the
|
||||
# cookie. This way it becomes harder to guess the cookie name
|
||||
# instead of the pin as we still count up failures.
|
||||
bad_cookie = False
|
||||
if trust is None:
|
||||
self._fail_pin_auth()
|
||||
bad_cookie = True
|
||||
|
||||
# If we're trusted, we're authenticated.
|
||||
elif trust:
|
||||
auth = True
|
||||
|
||||
# If we failed too many times, then we're locked out.
|
||||
elif self._failed_pin_auth > 10:
|
||||
exhausted = True
|
||||
|
||||
# Otherwise go through pin based authentication
|
||||
else:
|
||||
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')
|
||||
if auth:
|
||||
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
|
||||
|
||||
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('')
|
||||
|
||||
def __call__(self, environ, start_response):
|
||||
"""Dispatch the requests."""
|
||||
# important: don't ever access a function here that reads the incoming
|
||||
# form data! Otherwise the application won't have access to that data
|
||||
# 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:
|
||||
response = self.get_resource(request, arg)
|
||||
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:
|
||||
response = self.pin_auth(request)
|
||||
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):
|
||||
response = self.execute_command(request, cmd, frame)
|
||||
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)
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -0,0 +1,215 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
werkzeug.debug.console
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Interactive console support.
|
||||
|
||||
:copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
|
||||
:license: BSD.
|
||||
"""
|
||||
import sys
|
||||
import code
|
||||
from types import CodeType
|
||||
|
||||
from werkzeug.utils import escape
|
||||
from werkzeug.local import Local
|
||||
from werkzeug.debug.repr import debug_repr, dump, helper
|
||||
|
||||
|
||||
_local = Local()
|
||||
|
||||
|
||||
class HTMLStringO(object):
|
||||
|
||||
"""A StringO version that HTML escapes on write."""
|
||||
|
||||
def __init__(self):
|
||||
self._buffer = []
|
||||
|
||||
def isatty(self):
|
||||
return False
|
||||
|
||||
def close(self):
|
||||
pass
|
||||
|
||||
def flush(self):
|
||||
pass
|
||||
|
||||
def seek(self, n, mode=0):
|
||||
pass
|
||||
|
||||
def readline(self):
|
||||
if len(self._buffer) == 0:
|
||||
return ''
|
||||
ret = self._buffer[0]
|
||||
del self._buffer[0]
|
||||
return ret
|
||||
|
||||
def reset(self):
|
||||
val = ''.join(self._buffer)
|
||||
del self._buffer[:]
|
||||
return val
|
||||
|
||||
def _write(self, x):
|
||||
if isinstance(x, bytes):
|
||||
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)))
|
||||
|
||||
|
||||
class ThreadedStream(object):
|
||||
|
||||
"""Thread-local wrapper for sys.stdout for the interactive console."""
|
||||
|
||||
def push():
|
||||
if not isinstance(sys.stdout, ThreadedStream):
|
||||
sys.stdout = ThreadedStream()
|
||||
_local.stream = HTMLStringO()
|
||||
push = staticmethod(push)
|
||||
|
||||
def fetch():
|
||||
try:
|
||||
stream = _local.stream
|
||||
except AttributeError:
|
||||
return ''
|
||||
return stream.reset()
|
||||
fetch = staticmethod(fetch)
|
||||
|
||||
def displayhook(obj):
|
||||
try:
|
||||
stream = _local.stream
|
||||
except AttributeError:
|
||||
return _displayhook(obj)
|
||||
# stream._write bypasses escaping as debug_repr is
|
||||
# already generating HTML for us.
|
||||
if obj is not None:
|
||||
_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)
|
||||
|
||||
def __dir__(self):
|
||||
return dir(sys.__stdout__)
|
||||
|
||||
def __getattribute__(self, name):
|
||||
if name == '__members__':
|
||||
return dir(sys.__stdout__)
|
||||
try:
|
||||
stream = _local.stream
|
||||
except AttributeError:
|
||||
stream = sys.__stdout__
|
||||
return getattr(stream, name)
|
||||
|
||||
def __repr__(self):
|
||||
return repr(sys.__stdout__)
|
||||
|
||||
|
||||
# add the threaded stream as display hook
|
||||
_displayhook = sys.displayhook
|
||||
sys.displayhook = ThreadedStream.displayhook
|
||||
|
||||
|
||||
class _ConsoleLoader(object):
|
||||
|
||||
def __init__(self):
|
||||
self._storage = {}
|
||||
|
||||
def register(self, code, source):
|
||||
self._storage[id(code)] = source
|
||||
# register code objects of wrapped functions too.
|
||||
for var in code.co_consts:
|
||||
if isinstance(var, CodeType):
|
||||
self._storage[id(var)] = source
|
||||
|
||||
def get_source_by_code(self, code):
|
||||
try:
|
||||
return self._storage[id(code)]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
|
||||
def _wrap_compiler(console):
|
||||
compile = console.compile
|
||||
|
||||
def func(source, filename, symbol):
|
||||
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.more = False
|
||||
self.buffer = []
|
||||
_wrap_compiler(self)
|
||||
|
||||
def runsource(self, source):
|
||||
source = source.rstrip() + '\n'
|
||||
ThreadedStream.push()
|
||||
prompt = self.more and '... ' or '>>> '
|
||||
try:
|
||||
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:
|
||||
self.more = False
|
||||
del self.buffer[:]
|
||||
finally:
|
||||
output = ThreadedStream.fetch()
|
||||
return prompt + escape(source) + output
|
||||
|
||||
def runcode(self, code):
|
||||
try:
|
||||
eval(code, self.globals, self.locals)
|
||||
except Exception:
|
||||
self.showtraceback()
|
||||
|
||||
def showtraceback(self):
|
||||
from werkzeug.debug.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
|
||||
tb = get_current_traceback(skip=4)
|
||||
sys.stdout._write(tb.render_summary())
|
||||
|
||||
def write(self, data):
|
||||
sys.stdout.write(data)
|
||||
|
||||
|
||||
class Console(object):
|
||||
|
||||
"""An interactive console."""
|
||||
|
||||
def __init__(self, globals=None, locals=None):
|
||||
if locals is None:
|
||||
locals = {}
|
||||
if globals is None:
|
||||
globals = {}
|
||||
self._ipy = _InteractiveConsole(globals, locals)
|
||||
|
||||
def eval(self, code):
|
||||
_local._current_ipy = self._ipy
|
||||
old_sys_stdout = sys.stdout
|
||||
try:
|
||||
return self._ipy.runsource(code)
|
||||
finally:
|
||||
sys.stdout = old_sys_stdout
|
||||
@@ -0,0 +1,280 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
werkzeug.debug.repr
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module implements object representations for debugging purposes.
|
||||
Unlike the default repr these reprs expose a lot more information and
|
||||
produce HTML instead of ASCII.
|
||||
|
||||
Together with the CSS and JavaScript files of the debugger this gives
|
||||
a colorful and more compact output.
|
||||
|
||||
:copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
|
||||
:license: BSD.
|
||||
"""
|
||||
import sys
|
||||
import re
|
||||
import codecs
|
||||
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
|
||||
|
||||
|
||||
missing = object()
|
||||
_paragraph_re = re.compile(r'(?:\r\n|\r|\n){2,}')
|
||||
RegexType = type(_paragraph_re)
|
||||
|
||||
|
||||
HELP_HTML = '''\
|
||||
<div class=box>
|
||||
<h3>%(title)s</h3>
|
||||
<pre class=help>%(text)s</pre>
|
||||
</div>\
|
||||
'''
|
||||
OBJECT_DUMP_HTML = '''\
|
||||
<div class=box>
|
||||
<h3>%(title)s</h3>
|
||||
%(repr)s
|
||||
<table>%(items)s</table>
|
||||
</div>\
|
||||
'''
|
||||
|
||||
|
||||
def debug_repr(obj):
|
||||
"""Creates a debug repr of an object as HTML unicode string."""
|
||||
return DebugReprGenerator().repr(obj)
|
||||
|
||||
|
||||
def dump(obj=missing):
|
||||
"""Print the object details to stdout._write (for the interactive
|
||||
console of the web debugger.
|
||||
"""
|
||||
gen = DebugReprGenerator()
|
||||
if obj is missing:
|
||||
rv = gen.dump_locals(sys._getframe(1).f_locals)
|
||||
else:
|
||||
rv = gen.dump_object(obj)
|
||||
sys.stdout._write(rv)
|
||||
|
||||
|
||||
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.'
|
||||
|
||||
def __call__(self, topic=None):
|
||||
if topic is None:
|
||||
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')
|
||||
paragraphs = _paragraph_re.split(rv)
|
||||
if len(paragraphs) > 1:
|
||||
title = paragraphs[0]
|
||||
text = '\n\n'.join(paragraphs[1:])
|
||||
else: # pragma: no cover
|
||||
title = 'Help'
|
||||
text = paragraphs[0]
|
||||
sys.stdout._write(HELP_HTML % {'title': title, 'text': text})
|
||||
|
||||
|
||||
helper = _Helper()
|
||||
|
||||
|
||||
def _add_subclass_info(inner, obj, base):
|
||||
if isinstance(base, tuple):
|
||||
for base in base:
|
||||
if type(obj) is base:
|
||||
return inner
|
||||
elif type(obj) is base:
|
||||
return inner
|
||||
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)
|
||||
|
||||
|
||||
class DebugReprGenerator(object):
|
||||
|
||||
def __init__(self):
|
||||
self._stack = []
|
||||
|
||||
def _sequence_repr_maker(left, right, base=object(), limit=8):
|
||||
def proxy(self, obj, recursive):
|
||||
if recursive:
|
||||
return _add_subclass_info(left + '...' + right, obj, base)
|
||||
buf = [left]
|
||||
have_extended_section = False
|
||||
for idx, item in enumerate(obj):
|
||||
if idx:
|
||||
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(right)
|
||||
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)
|
||||
del _sequence_repr_maker
|
||||
|
||||
def regex_repr(self, obj):
|
||||
pattern = repr(obj.pattern)
|
||||
if PY2:
|
||||
pattern = pattern.decode('string-escape', 'ignore')
|
||||
else:
|
||||
pattern = codecs.decode(pattern, 'unicode-escape', 'ignore')
|
||||
if pattern[:1] == 'u':
|
||||
pattern = 'ur' + pattern[1:]
|
||||
else:
|
||||
pattern = 'r' + pattern
|
||||
return u're.compile(<span class="string regex">%s</span>)' % pattern
|
||||
|
||||
def string_repr(self, obj, limit=70):
|
||||
buf = ['<span class="string">']
|
||||
a = repr(obj[:limit])
|
||||
b = repr(obj[limit:])
|
||||
if isinstance(obj, text_type) and PY2:
|
||||
buf.append('u')
|
||||
a = a[1:]
|
||||
b = b[1:]
|
||||
if b != "''":
|
||||
buf.extend((escape(a[:-1]), '<span class="extended">', escape(b[1:]), '</span>'))
|
||||
else:
|
||||
buf.append(escape(a))
|
||||
buf.append('</span>')
|
||||
return _add_subclass_info(u''.join(buf), obj, (bytes, text_type))
|
||||
|
||||
def dict_repr(self, d, recursive, limit=5):
|
||||
if recursive:
|
||||
return _add_subclass_info(u'{...}', d, dict)
|
||||
buf = ['{']
|
||||
have_extended_section = False
|
||||
for idx, (key, value) in enumerate(iteritems(d)):
|
||||
if idx:
|
||||
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)))
|
||||
if have_extended_section:
|
||||
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')
|
||||
return u'<span class="object">%s</span>' % escape(r)
|
||||
|
||||
def dispatch_repr(self, obj, recursive):
|
||||
if obj is helper:
|
||||
return u'<span class="help">%r</span>' % helper
|
||||
if isinstance(obj, (integer_types, float, complex)):
|
||||
return u'<span class="number">%r</span>' % obj
|
||||
if isinstance(obj, string_types):
|
||||
return self.string_repr(obj)
|
||||
if isinstance(obj, RegexType):
|
||||
return self.regex_repr(obj)
|
||||
if isinstance(obj, list):
|
||||
return self.list_repr(obj, recursive)
|
||||
if isinstance(obj, tuple):
|
||||
return self.tuple_repr(obj, recursive)
|
||||
if isinstance(obj, set):
|
||||
return self.set_repr(obj, recursive)
|
||||
if isinstance(obj, frozenset):
|
||||
return self.frozenset_repr(obj, recursive)
|
||||
if isinstance(obj, dict):
|
||||
return self.dict_repr(obj, recursive)
|
||||
if deque is not None and isinstance(obj, deque):
|
||||
return self.deque_repr(obj, recursive)
|
||||
return self.object_repr(obj)
|
||||
|
||||
def fallback_repr(self):
|
||||
try:
|
||||
info = ''.join(format_exception_only(*sys.exc_info()[:2]))
|
||||
except Exception: # pragma: no cover
|
||||
info = '?'
|
||||
if PY2:
|
||||
info = info.decode('utf-8', 'ignore')
|
||||
return u'<span class="brokenrepr"><broken repr (%s)>' \
|
||||
u'</span>' % escape(info.strip())
|
||||
|
||||
def repr(self, obj):
|
||||
recursive = False
|
||||
for item in self._stack:
|
||||
if item is obj:
|
||||
recursive = True
|
||||
break
|
||||
self._stack.append(obj)
|
||||
try:
|
||||
try:
|
||||
return self.dispatch_repr(obj, recursive)
|
||||
except Exception:
|
||||
return self.fallback_repr()
|
||||
finally:
|
||||
self._stack.pop()
|
||||
|
||||
def dump_object(self, obj):
|
||||
repr = items = None
|
||||
if isinstance(obj, dict):
|
||||
title = 'Contents of'
|
||||
items = []
|
||||
for key, value in iteritems(obj):
|
||||
if not isinstance(key, string_types):
|
||||
items = None
|
||||
break
|
||||
items.append((key, self.repr(value)))
|
||||
if items is None:
|
||||
items = []
|
||||
repr = self.repr(obj)
|
||||
for key in dir(obj):
|
||||
try:
|
||||
items.append((key, self.repr(getattr(obj, key))))
|
||||
except Exception:
|
||||
pass
|
||||
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')
|
||||
|
||||
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))
|
||||
if not html_items:
|
||||
html_items.append('<tr><td><em>Nothing</em>')
|
||||
return OBJECT_DUMP_HTML % {
|
||||
'title': escape(title),
|
||||
'repr': repr and '<pre class=repr>%s</pre>' % repr or '',
|
||||
'items': '\n'.join(html_items)
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
-------------------------------
|
||||
UBUNTU FONT LICENCE Version 1.0
|
||||
-------------------------------
|
||||
|
||||
PREAMBLE
|
||||
This licence allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely. The fonts, including any derivative works, can be
|
||||
bundled, embedded, and redistributed provided the terms of this licence
|
||||
are met. The fonts and derivatives, however, cannot be released under
|
||||
any other licence. The requirement for fonts to remain under this
|
||||
licence does not require any document created using the fonts or their
|
||||
derivatives to be published under this licence, as long as the primary
|
||||
purpose of the document is not to be a vehicle for the distribution of
|
||||
the fonts.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this licence and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Original Version" refers to the collection of Font Software components
|
||||
as received under this licence.
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to
|
||||
a new environment.
|
||||
|
||||
"Copyright Holder(s)" refers to all individuals and companies who have a
|
||||
copyright ownership of the Font Software.
|
||||
|
||||
"Substantially Changed" refers to Modified Versions which can be easily
|
||||
identified as dissimilar to the Font Software by users of the Font
|
||||
Software comparing the Original Version with the Modified Version.
|
||||
|
||||
To "Propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification and with or without charging
|
||||
a redistribution fee), making available to the public, and in some
|
||||
countries other activities as well.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
This licence does not grant any rights under trademark law and all such
|
||||
rights are reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of the Font Software, to propagate the Font Software, subject to
|
||||
the below conditions:
|
||||
|
||||
1) Each copy of the Font Software must contain the above copyright
|
||||
notice and this licence. These can be included either as stand-alone
|
||||
text files, human-readable headers or in the appropriate machine-
|
||||
readable metadata fields within text or binary files as long as those
|
||||
fields can be easily viewed by the user.
|
||||
|
||||
2) The font name complies with the following:
|
||||
(a) The Original Version must retain its name, unmodified.
|
||||
(b) Modified Versions which are Substantially Changed must be renamed to
|
||||
avoid use of the name of the Original Version or similar names entirely.
|
||||
(c) Modified Versions which are not Substantially Changed must be
|
||||
renamed to both (i) retain the name of the Original Version and (ii) add
|
||||
additional naming elements to distinguish the Modified Version from the
|
||||
Original Version. The name of such Modified Versions must be the name of
|
||||
the Original Version, with "derivative X" where X represents the name of
|
||||
the new work, appended to that name.
|
||||
|
||||
3) The name(s) of the Copyright Holder(s) and any contributor to the
|
||||
Font Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except (i) as required by this licence, (ii) to
|
||||
acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
|
||||
their explicit written permission.
|
||||
|
||||
4) The Font Software, modified or unmodified, in part or in whole, must
|
||||
be distributed entirely under this licence, and must not be distributed
|
||||
under any other licence. The requirement for fonts to remain under this
|
||||
licence does not affect any document created using the Font Software,
|
||||
except any version of the Font Software extracted from a document
|
||||
created using the Font Software may only be distributed under this
|
||||
licence.
|
||||
|
||||
TERMINATION
|
||||
This licence becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
|
||||
COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
|
||||
DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 507 B |
@@ -0,0 +1,205 @@
|
||||
$(function() {
|
||||
if (!EVALEX_TRUSTED) {
|
||||
initPinBox();
|
||||
}
|
||||
|
||||
/**
|
||||
* if we are in console mode, show the console.
|
||||
*/
|
||||
if (CONSOLE_MODE && EVALEX) {
|
||||
openShell(null, $('div.console div.inner').empty(), 0);
|
||||
}
|
||||
|
||||
$('div.traceback div.frame').each(function() {
|
||||
var
|
||||
target = $('pre', this),
|
||||
consoleNode = null,
|
||||
frameID = this.id.substring(6);
|
||||
|
||||
target.click(function() {
|
||||
$(this).parent().toggleClass('expanded');
|
||||
});
|
||||
|
||||
/**
|
||||
* Add an interactive console to the frames
|
||||
*/
|
||||
if (EVALEX && target.is('.current')) {
|
||||
$('<img src="?__debugger__=yes&cmd=resource&f=console.png">')
|
||||
.attr('title', 'Open an interactive python shell in this frame')
|
||||
.click(function() {
|
||||
consoleNode = openShell(consoleNode, target, frameID);
|
||||
return false;
|
||||
})
|
||||
.prependTo(target);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* toggle traceback types on click.
|
||||
*/
|
||||
$('h2.traceback').click(function() {
|
||||
$(this).next().slideToggle('fast');
|
||||
$('div.plain').slideToggle('fast');
|
||||
}).css('cursor', 'pointer');
|
||||
$('div.plain').hide();
|
||||
|
||||
/**
|
||||
* Add extra info (this is here so that only users with JavaScript
|
||||
* enabled see it.)
|
||||
*/
|
||||
$('span.nojavascript')
|
||||
.removeClass('nojavascript')
|
||||
.html('<p>To switch between the interactive traceback and the plaintext ' +
|
||||
'one, you can click on the "Traceback" headline. From the text ' +
|
||||
'traceback you can also create a paste of it. ' + (!EVALEX ? '' :
|
||||
'For code execution mouse-over the frame you want to debug and ' +
|
||||
'click on the console icon on the right side.' +
|
||||
'<p>You can execute arbitrary Python code in the stack frames and ' +
|
||||
'there are some extra helpers available for introspection:' +
|
||||
'<ul><li><code>dump()</code> shows all variables in the frame' +
|
||||
'<li><code>dump(obj)</code> dumps all that\'s known about the object</ul>'));
|
||||
|
||||
/**
|
||||
* Add the pastebin feature
|
||||
*/
|
||||
$('div.plain form')
|
||||
.submit(function() {
|
||||
var label = $('input[type="submit"]', this);
|
||||
var old_val = label.val();
|
||||
label.val('submitting...');
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url: document.location.pathname,
|
||||
data: {__debugger__: 'yes', tb: TRACEBACK, cmd: 'paste',
|
||||
s: SECRET},
|
||||
success: function(data) {
|
||||
$('div.plain span.pastemessage')
|
||||
.removeClass('pastemessage')
|
||||
.text('Paste created: ')
|
||||
.append($('<a>#' + data.id + '</a>').attr('href', data.url));
|
||||
},
|
||||
error: function() {
|
||||
alert('Error: Could not submit paste. No network connection?');
|
||||
label.val(old_val);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
// if we have javascript we submit by ajax anyways, so no need for the
|
||||
// not scaling textarea.
|
||||
var plainTraceback = $('div.plain textarea');
|
||||
plainTraceback.replaceWith($('<pre>').text(plainTraceback.text()));
|
||||
});
|
||||
|
||||
function initPinBox() {
|
||||
$('.pin-prompt form').submit(function(evt) {
|
||||
evt.preventDefault();
|
||||
var pin = this.pin.value;
|
||||
var btn = this.btn;
|
||||
btn.disabled = true;
|
||||
$.ajax({
|
||||
dataType: 'json',
|
||||
url: document.location.pathname,
|
||||
data: {__debugger__: 'yes', cmd: 'pinauth', pin: pin,
|
||||
s: SECRET},
|
||||
success: function(data) {
|
||||
btn.disabled = false;
|
||||
if (data.auth) {
|
||||
EVALEX_TRUSTED = true;
|
||||
$('.pin-prompt').fadeOut();
|
||||
} else {
|
||||
if (data.exhausted) {
|
||||
alert('Error: too many attempts. Restart server to retry.');
|
||||
} else {
|
||||
alert('Error: incorrect pin');
|
||||
}
|
||||
}
|
||||
console.log(data);
|
||||
},
|
||||
error: function() {
|
||||
btn.disabled = false;
|
||||
alert('Error: Could not verify PIN. Network error?');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function promptForPin() {
|
||||
if (!EVALEX_TRUSTED) {
|
||||
$.ajax({
|
||||
url: document.location.pathname,
|
||||
data: {__debugger__: 'yes', cmd: 'printpin', s: SECRET}
|
||||
});
|
||||
$('.pin-prompt').fadeIn(function() {
|
||||
$('.pin-prompt input[name="pin"]').focus();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function for shell initialization
|
||||
*/
|
||||
function openShell(consoleNode, target, frameID) {
|
||||
promptForPin();
|
||||
if (consoleNode)
|
||||
return consoleNode.slideToggle('fast');
|
||||
consoleNode = $('<pre class="console">')
|
||||
.appendTo(target.parent())
|
||||
.hide()
|
||||
var historyPos = 0, history = [''];
|
||||
var output = $('<div class="output">[console ready]</div>')
|
||||
.appendTo(consoleNode);
|
||||
var form = $('<form>>>> </form>')
|
||||
.submit(function() {
|
||||
var cmd = command.val();
|
||||
$.get('', {
|
||||
__debugger__: 'yes', cmd: cmd, frm: frameID, s: SECRET}, function(data) {
|
||||
var tmp = $('<div>').html(data);
|
||||
$('span.extended', tmp).each(function() {
|
||||
var hidden = $(this).wrap('<span>').hide();
|
||||
hidden
|
||||
.parent()
|
||||
.append($('<a href="#" class="toggle"> </a>')
|
||||
.click(function() {
|
||||
hidden.toggle();
|
||||
$(this).toggleClass('open')
|
||||
return false;
|
||||
}));
|
||||
});
|
||||
output.append(tmp);
|
||||
command.focus();
|
||||
consoleNode.scrollTop(consoleNode.get(0).scrollHeight);
|
||||
var old = history.pop();
|
||||
history.push(cmd);
|
||||
if (typeof old != 'undefined')
|
||||
history.push(old);
|
||||
historyPos = history.length - 1;
|
||||
});
|
||||
command.val('');
|
||||
return false;
|
||||
}).
|
||||
appendTo(consoleNode);
|
||||
|
||||
var command = $('<input type="text" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">')
|
||||
.appendTo(form)
|
||||
.keydown(function(e) {
|
||||
if (e.charCode == 100 && e.ctrlKey) {
|
||||
output.text('--- screen cleared ---');
|
||||
return false;
|
||||
}
|
||||
else if (e.charCode == 0 && (e.keyCode == 38 || e.keyCode == 40)) {
|
||||
if (e.keyCode == 38 && historyPos > 0)
|
||||
historyPos--;
|
||||
else if (e.keyCode == 40 && historyPos < history.length)
|
||||
historyPos++;
|
||||
command.val(history[historyPos]);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return consoleNode.slideDown('fast', function() {
|
||||
command.focus();
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/*! jQuery v1.11.3 | (c) 2005, 2015 jQuery Foundation, Inc. | jquery.org/license */
|
||||
!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.3",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)+1>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b="length"in a&&a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C=1<<31,D={}.hasOwnProperty,E=[],F=E.pop,G=E.push,H=E.push,I=E.slice,J=function(a,b){for(var c=0,d=a.length;d>c;c++)if(a[c]===b)return c;return-1},K="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",L="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",N=M.replace("w","w#"),O="\\["+L+"*("+M+")(?:"+L+"*([*^$|!~]?=)"+L+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^Line truncated
|
||||
|
||||
return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h;if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length<c?m.queue(this[0],a):void 0===b?this:this.each(function(){var c=m.queue(this,a,b);m._queueHooks(this,a),"fx"===a&&"inprogress"!==c[0]&&m.dequeue(this,a)})},dequeue:function(a){return this.each(function(){m.dequeue(this,a)})},clearQueue:function(a){return this.queue(a||"fx",[])},promise:function(a,b){var c,d=1,e=m.Deferred(),f=this,g=this.length,h=function(){--d||e.resolveWith(f,[f])};"string"!=typeof a&&(b=a,a=void 0),a=a||"fx";while(g--)c=m._data(f[g],a+"queueHooks"),c&&c.empty&&(d++,c.empty.add(h));return h(),e.promise(b)}});var S=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,T=["Top","Right","Bottom","Left"],U=function(a,b){return a=b||a,"none"===m.css(a,"display")||!m.contains(a.ownerDocument,a)},V=m.access=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===m.type(c)){e=!0;for(h in c)m.access(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,m.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(m(a),c)})),b))for(;i>h;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML=" <link/><table></table><a href='/a'>a</a><input type='checkbox'/>",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav></:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="<textarea>x</textarea>",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="<input type='radio' checked='checked' name='t'/>",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function aa(){return!0}function ba(){return!1}function ca(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var
Line truncated
|
||||
return new Za.prototype.init(a,b,c,d,e)}m.Tween=Za,Za.prototype={constructor:Za,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||"swing",this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(m.cssNumber[c]?"":"px")},cur:function(){var a=Za.propHooks[this.prop];return a&&a.get?a.get(this):Za.propHooks._default.get(this)},run:function(a){var b,c=Za.propHooks[this.prop];return this.options.duration?this.pos=b=m.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):Za.propHooks._default.set(this),this}},Za.prototype.init.prototype=Za.prototype,Za.propHooks={_default:{get:function(a){var b;return null==a.elem[a.prop]||a.elem.style&&null!=a.elem.style[a.prop]?(b=m.css(a.elem,a.prop,""),b&&"auto"!==b?b:0):a.elem[a.prop]},set:function(a){m.fx.step[a.prop]?m.fx.step[a.prop](a):a.elem.style&&(null!=a.elem.style[m.cssProps[a.prop]]||m.cssHooks[a.prop])?m.style(a.elem,a.prop,a.now+a.unit):a.elem[a.prop]=a.now}}},Za.propHooks.scrollTop=Za.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},m.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2}},m.fx=Za.prototype.init,m.fx.step={};var $a,_a,ab=/^(?:toggle|show|hide)$/,bb=new RegExp("^(?:([+-])=|)("+S+")([a-z%]*)$","i"),cb=/queueHooks$/,db=[ib],eb={"*":[function(a,b){var c=this.createTween(a,b),d=c.cur(),e=bb.exec(b),f=e&&e[3]||(m.cssNumber[a]?"":"px"),g=(m.cssNumber[a]||"px"!==f&&+d)&&bb.exec(m.css(c.elem,a)),h=1,i=20;if(g&&g[3]!==f){f=f||g[3],e=e||[],g=+d||1;do h=h||".5",g/=h,m.style(c.elem,a,g+f);while(h!==(h=c.cur()/d)&&1!==h&&--i)}return e&&(g=c.start=+g||+d||0,c.unit=f,c.end=e[1]?g+(e[1]+1)*e[2]:+e[2]),c}]};function fb(){return setTimeout(function(){$a=void 0}),$a=m.now()}function gb(a,b){var c,d={height:a},e=0;for(b=b?1:0;4>e;e+=2-b)c=T[e],d["margin"+c]=d["padding"+c]=a;return b&&(d.opacity=d.width=a),d}function hb(a,b,c){for(var d,e=(eb[b]||[]).concat(eb["*"]),f=0,g=e.length;g>f;f++)if(d=e[f].call(c,b,a))return d}function ib(a,b,c){var d,e,f,g,h,i,j,l,n=this,o={},p=a.style,q=a.nodeType&&U(a),r=m._data(a,"fxshow");c.queue||(h=m._queueHooks(a,"fx"),null==h.unqueued&&(h.unqueued=0,i=h.empty.fire,h.empty.fire=function(){h.unqueued||i()}),h.unqueued++,n.always(function(){n.always(function(){h.unqueued--,m.queue(a,"fx").length||h.empty.fire()})})),1===a.nodeType&&("height"in b||"width"in b)&&(c.overflow=[p.overflow,p.overflowX,p.overflowY],j=m.css(a,"display"),l="none"===j?m._data(a,"olddisplay")||Fa(a.nodeName):j,"inline"===l&&"none"===m.css(a,"float")&&(k.inlineBlockNeedsLayout&&"inline"!==Fa(a.nodeName)?p.zoom=1:p.display="inline-block")),c.overflow&&(p.overflow="hidden",k.shrinkWrapBlocks()||n.always(function(){p.overflow=c.overflow[0],p.overflowX=c.overflow[1],p.overflowY=c.overflow[2]}));for(d in b)if(e=b[d],ab.exec(e)){if(delete b[d],f=f||"toggle"===e,e===(q?"hide":"show")){if("show"!==e||!r||void 0===r[d])continue;q=!0}o[d]=r&&r[d]||m.style(a,d)}else j=void 0;if(m.isEmptyObject(o))"inline"===("none"===j?Fa(a.nodeName):j)&&(p.display=j);else{r?"hidden"in r&&(q=r.hidden):r=m._data(a,"fxshow",{}),f&&(r.hidden=!q),q?m(a).show():n.done(function(){m(a).hide()}),n.done(function(){var b;m._removeData(a,"fxshow");for(b in o)m.style(a,b,o[b])});for(d in o)g=hb(q?r[d]:0,d,n),d in r||(r[d]=g.start,q&&(g.end=g.start,g.start="width"===d||"height"===d?1:0))}}function jb(a,b){var c,d,e,f,g;for(c in a)if(d=m.camelCase(c),e=b[d],f=a[c],m.isArray(f)&&(e=f[1],f=a[c]=f[0]),c!==d&&(a[d]=f,delete a[c]),g=m.cssHooks[d],g&&"expand"in g){f=g.expand(f),delete a[d];for(c in f)c in a||(a[c]=f[c],b[c]=e)}else b[d]=e}function kb(a,b,c){var d,e,f=0,g=db.length,h=m.Deferred().always(function(){delete i.elem}),i=function(){if(e)return!1;for(var b=$a||fb(),c=Math.max(0,j.startTime+j.duration-b),d=c/j.duration||0,f=1-d,g=0,i=j.tweens.length;i>g;g++)j.tweens[g].run(f);return h.notifyWith(a,[j,f,c]),1>f&&i?c:(h.resolveWith(a,[j]),!1)},j=h.promise({elem:a,props:m.extend({},b),opts:m.extend(!0,{specialEasing:{}},c),originalProperties:b,originalOptions:c,startTime:$a||fb(),duration:c.duration,tweens:[],createTween:function(b,c){var d=m.Tween(a,j.opts,b,c,j.opts.specialEasing[b]||j.opts.easing);return j.tweens.push(d),d},stop:function(b){var c=0,d=b?j.tweens.length:0;if(e)return this;for(e=!0;d>c;c++)j.tweens[c].run(1);return b?h.resolveWith(a,[j,b]):h.rejectWith(a,[j,b]),this}}),k=j.props;for(jb(k,j.opts.specialEasing);g>f;f++)if(d=db[f].call(j,a,k,j.opts))return d;return m.map(k,hb,j),m.isFunction(j.opts.start)&&j.opts.start.call(a,j),m.fx.timer(m.extend(i,{elem:a,anim:j,queue:j.opts.queue})),j.progress(j.opts.progress).done(j.opts.done,j.opts.complete).fail(j.opts.fail).always(j.opts.always)}m.Animation=m.extend(kb,{tweener:function(a,b){m.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");for(var c,d=0,e=a.length;e>d;d++)c=a[d],eb[cLine truncated
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 191 B |
Binary file not shown.
|
After Width: | Height: | Size: 200 B |
Binary file not shown.
|
After Width: | Height: | Size: 818 B |
@@ -0,0 +1,143 @@
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
src: local('Ubuntu'), local('Ubuntu-Regular'),
|
||||
url('?__debugger__=yes&cmd=resource&f=ubuntu.ttf') format('truetype');
|
||||
}
|
||||
|
||||
body, input { font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
|
||||
'Verdana', sans-serif; color: #000; text-align: center;
|
||||
margin: 1em; padding: 0; font-size: 15px; }
|
||||
h1, h2, h3 { font-family: 'Ubuntu', 'Lucida Grande', 'Lucida Sans Unicode',
|
||||
'Geneva', 'Verdana', sans-serif; font-weight: normal; }
|
||||
|
||||
input { background-color: #fff; margin: 0; text-align: left;
|
||||
outline: none !important; }
|
||||
input[type="submit"] { padding: 3px 6px; }
|
||||
a { color: #11557C; }
|
||||
a:hover { color: #177199; }
|
||||
pre, code,
|
||||
textarea { font-family: 'Consolas', 'Monaco', 'Bitstream Vera Sans Mono',
|
||||
monospace; font-size: 14px; }
|
||||
|
||||
div.debugger { text-align: left; padding: 12px; margin: auto;
|
||||
background-color: white; }
|
||||
h1 { font-size: 36px; margin: 0 0 0.3em 0; }
|
||||
div.detail p { margin: 0 0 8px 13px; font-size: 14px; white-space: pre-wrap;
|
||||
font-family: monospace; }
|
||||
div.explanation { margin: 20px 13px; font-size: 15px; color: #555; }
|
||||
div.footer { font-size: 13px; text-align: right; margin: 30px 0;
|
||||
color: #86989B; }
|
||||
|
||||
h2 { font-size: 16px; margin: 1.3em 0 0.0 0; padding: 9px;
|
||||
background-color: #11557C; color: white; }
|
||||
h2 em, h3 em { font-style: normal; color: #A5D6D9; font-weight: normal; }
|
||||
|
||||
div.traceback, div.plain { border: 1px solid #ddd; margin: 0 0 1em 0; padding: 10px; }
|
||||
div.plain p { margin: 0; }
|
||||
div.plain textarea,
|
||||
div.plain pre { margin: 10px 0 0 0; padding: 4px;
|
||||
background-color: #E8EFF0; border: 1px solid #D3E7E9; }
|
||||
div.plain textarea { width: 99%; height: 300px; }
|
||||
div.traceback h3 { font-size: 1em; margin: 0 0 0.8em 0; }
|
||||
div.traceback ul { list-style: none; margin: 0; padding: 0 0 0 1em; }
|
||||
div.traceback h4 { font-size: 13px; font-weight: normal; margin: 0.7em 0 0.1em 0; }
|
||||
div.traceback pre { margin: 0; padding: 5px 0 3px 15px;
|
||||
background-color: #E8EFF0; border: 1px solid #D3E7E9; }
|
||||
div.traceback pre:hover { background-color: #DDECEE; color: black; cursor: pointer; }
|
||||
div.traceback div.source.expanded pre + pre { border-top: none; }
|
||||
|
||||
div.traceback span.ws { display: none; }
|
||||
div.traceback pre.before, div.traceback pre.after { display: none; background: white; }
|
||||
div.traceback div.source.expanded pre.before,
|
||||
div.traceback div.source.expanded pre.after {
|
||||
display: block;
|
||||
}
|
||||
|
||||
div.traceback div.source.expanded span.ws {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.traceback blockquote { margin: 1em 0 0 0; padding: 0; }
|
||||
div.traceback img { float: right; padding: 2px; margin: -3px 2px 0 0; display: none; }
|
||||
div.traceback img:hover { background-color: #ddd; cursor: pointer;
|
||||
border-color: #BFDDE0; }
|
||||
div.traceback pre:hover img { display: block; }
|
||||
div.traceback cite.filename { font-style: normal; color: #3B666B; }
|
||||
|
||||
pre.console { border: 1px solid #ccc; background: white!important;
|
||||
color: black; padding: 5px!important;
|
||||
margin: 3px 0 0 0!important; cursor: default!important;
|
||||
max-height: 400px; overflow: auto; }
|
||||
pre.console form { color: #555; }
|
||||
pre.console input { background-color: transparent; color: #555;
|
||||
width: 90%; font-family: 'Consolas', 'Deja Vu Sans Mono',
|
||||
'Bitstream Vera Sans Mono', monospace; font-size: 14px;
|
||||
border: none!important; }
|
||||
|
||||
span.string { color: #30799B; }
|
||||
span.number { color: #9C1A1C; }
|
||||
span.help { color: #3A7734; }
|
||||
span.object { color: #485F6E; }
|
||||
span.extended { opacity: 0.5; }
|
||||
span.extended:hover { opacity: 1; }
|
||||
a.toggle { text-decoration: none; background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-image: url(?__debugger__=yes&cmd=resource&f=more.png); }
|
||||
a.toggle:hover { background-color: #444; }
|
||||
a.open { background-image: url(?__debugger__=yes&cmd=resource&f=less.png); }
|
||||
|
||||
pre.console div.traceback,
|
||||
pre.console div.box { margin: 5px 10px; white-space: normal;
|
||||
border: 1px solid #11557C; padding: 10px;
|
||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva',
|
||||
'Verdana', sans-serif; }
|
||||
pre.console div.box h3,
|
||||
pre.console div.traceback h3 { margin: -10px -10px 10px -10px; padding: 5px;
|
||||
background: #11557C; color: white; }
|
||||
|
||||
pre.console div.traceback pre:hover { cursor: default; background: #E8EFF0; }
|
||||
pre.console div.traceback pre.syntaxerror { background: inherit; border: none;
|
||||
margin: 20px -10px -10px -10px;
|
||||
padding: 10px; border-top: 1px solid #BFDDE0;
|
||||
background: #E8EFF0; }
|
||||
pre.console div.noframe-traceback pre.syntaxerror { margin-top: -10px; border: none; }
|
||||
|
||||
pre.console div.box pre.repr { padding: 0; margin: 0; background-color: white; border: none; }
|
||||
pre.console div.box table { margin-top: 6px; }
|
||||
pre.console div.box pre { border: none; }
|
||||
pre.console div.box pre.help { background-color: white; }
|
||||
pre.console div.box pre.help:hover { cursor: default; }
|
||||
pre.console table tr { vertical-align: top; }
|
||||
div.console { border: 1px solid #ccc; padding: 4px; background-color: #fafafa; }
|
||||
|
||||
div.traceback pre, div.console pre {
|
||||
white-space: pre-wrap; /* css-3 should we be so lucky... */
|
||||
white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
|
||||
white-space: -pre-wrap; /* Opera 4-6 ?? */
|
||||
white-space: -o-pre-wrap; /* Opera 7 ?? */
|
||||
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
||||
_white-space: pre; /* IE only hack to re-specify in
|
||||
addition to word-wrap */
|
||||
}
|
||||
|
||||
|
||||
div.pin-prompt {
|
||||
position: absolute;
|
||||
display: none;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
div.pin-prompt .inner {
|
||||
background: #eee;
|
||||
padding: 10px 50px;
|
||||
width: 350px;
|
||||
margin: 10% auto 0 auto;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 2px;
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,556 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
werkzeug.debug.tbtools
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This module provides various traceback related utility functions.
|
||||
|
||||
:copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
|
||||
:license: BSD.
|
||||
"""
|
||||
import re
|
||||
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import inspect
|
||||
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
|
||||
from werkzeug.filesystem import get_filesystem_encoding
|
||||
|
||||
|
||||
_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:
|
||||
system_exceptions += (GeneratorExit,)
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
|
||||
HEADER = u'''\
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
|
||||
"http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<title>%(title)s // Werkzeug Debugger</title>
|
||||
<link rel="stylesheet" href="?__debugger__=yes&cmd=resource&f=style.css"
|
||||
type="text/css">
|
||||
<!-- We need to make sure this has a favicon so that the debugger does
|
||||
not by accident trigger a request to /favicon.ico which might
|
||||
change the application state. -->
|
||||
<link rel="shortcut icon"
|
||||
href="?__debugger__=yes&cmd=resource&f=console.png">
|
||||
<script src="?__debugger__=yes&cmd=resource&f=jquery.js"></script>
|
||||
<script src="?__debugger__=yes&cmd=resource&f=debugger.js"></script>
|
||||
<script type="text/javascript">
|
||||
var TRACEBACK = %(traceback_id)d,
|
||||
CONSOLE_MODE = %(console)s,
|
||||
EVALEX = %(evalex)s,
|
||||
EVALEX_TRUSTED = %(evalex_trusted)s,
|
||||
SECRET = "%(secret)s";
|
||||
</script>
|
||||
</head>
|
||||
<body style="background-color: #fff">
|
||||
<div class="debugger">
|
||||
'''
|
||||
FOOTER = u'''\
|
||||
<div class="footer">
|
||||
Brought to you by <strong class="arthur">DON'T PANIC</strong>, your
|
||||
friendly Werkzeug powered traceback interpreter.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="pin-prompt">
|
||||
<div class="inner">
|
||||
<h3>Console Locked</h3>
|
||||
<p>
|
||||
The console is locked and needs to be unlocked by entering the PIN.
|
||||
You can find the PIN printed out on the standard output of your
|
||||
shell that runs the server.
|
||||
<form>
|
||||
<p>PIN:
|
||||
<input type=text name=pin size=14>
|
||||
<input type=submit name=btn value="Confirm Pin">
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
PAGE_HTML = HEADER + u'''\
|
||||
<h1>%(exception_type)s</h1>
|
||||
<div class="detail">
|
||||
<p class="errormsg">%(exception)s</p>
|
||||
</div>
|
||||
<h2 class="traceback">Traceback <em>(most recent call last)</em></h2>
|
||||
%(summary)s
|
||||
<div class="plain">
|
||||
<form action="/?__debugger__=yes&cmd=paste" method="post">
|
||||
<p>
|
||||
<input type="hidden" name="language" value="pytb">
|
||||
This is the Copy/Paste friendly version of the traceback. <span
|
||||
class="pastemessage">You can also paste this traceback into
|
||||
a <a href="https://gist.github.com/">gist</a>:
|
||||
<input type="submit" value="create paste"></span>
|
||||
</p>
|
||||
<textarea cols="50" rows="10" name="code" readonly>%(plaintext)s</textarea>
|
||||
</form>
|
||||
</div>
|
||||
<div class="explanation">
|
||||
The debugger caught an exception in your WSGI application. You can now
|
||||
look at the traceback which led to the error. <span class="nojavascript">
|
||||
If you enable JavaScript you can also use additional features such as code
|
||||
execution (if the evalex feature is enabled), automatic pasting of the
|
||||
exceptions and much more.</span>
|
||||
</div>
|
||||
''' + FOOTER + '''
|
||||
<!--
|
||||
|
||||
%(plaintext_cs)s
|
||||
|
||||
-->
|
||||
'''
|
||||
|
||||
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
|
||||
|
||||
SUMMARY_HTML = u'''\
|
||||
<div class="%(classes)s">
|
||||
%(title)s
|
||||
<ul>%(frames)s</ul>
|
||||
%(description)s
|
||||
</div>
|
||||
'''
|
||||
|
||||
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">%(lines)s</div>
|
||||
</div>
|
||||
'''
|
||||
|
||||
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': evalex_trusted and 'true' or 'false',
|
||||
'console': 'true',
|
||||
'title': 'Console',
|
||||
'secret': secret,
|
||||
'traceback_id': -1
|
||||
}
|
||||
|
||||
|
||||
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`
|
||||
to the function as first parameter.
|
||||
"""
|
||||
exc_type, exc_value, tb = sys.exc_info()
|
||||
if ignore_system_exceptions and exc_type in system_exceptions:
|
||||
raise
|
||||
for x in range_type(skip):
|
||||
if tb.tb_next is None:
|
||||
break
|
||||
tb = tb.tb_next
|
||||
tb = Traceback(exc_type, exc_value, tb)
|
||||
if not show_hidden_frames:
|
||||
tb.filter_hidden_frames()
|
||||
return tb
|
||||
|
||||
|
||||
class Line(object):
|
||||
"""Helper for the source renderer."""
|
||||
__slots__ = ('lineno', 'code', 'in_frame', 'current')
|
||||
|
||||
def __init__(self, lineno, code):
|
||||
self.lineno = lineno
|
||||
self.code = code
|
||||
self.in_frame = False
|
||||
self.current = False
|
||||
|
||||
def classes(self):
|
||||
rv = ['line']
|
||||
if self.in_frame:
|
||||
rv.append('in-frame')
|
||||
if self.current:
|
||||
rv.append('current')
|
||||
return rv
|
||||
classes = property(classes)
|
||||
|
||||
def render(self):
|
||||
return SOURCE_LINE_HTML % {
|
||||
'classes': u' '.join(self.classes),
|
||||
'lineno': self.lineno,
|
||||
'code': escape(self.code)
|
||||
}
|
||||
|
||||
|
||||
class Traceback(object):
|
||||
"""Wraps a traceback."""
|
||||
|
||||
def __init__(self, exc_type, exc_value, tb):
|
||||
self.exc_type = exc_type
|
||||
self.exc_value = exc_value
|
||||
if not isinstance(exc_type, str):
|
||||
exception_type = exc_type.__name__
|
||||
if exc_type.__module__ not in ('__builtin__', 'exceptions'):
|
||||
exception_type = exc_type.__module__ + '.' + exception_type
|
||||
else:
|
||||
exception_type = exc_type
|
||||
self.exception_type = exception_type
|
||||
|
||||
# we only add frames to the list that are not hidden. This follows
|
||||
# the the magic variables as defined by paste.exceptions.collector
|
||||
self.frames = []
|
||||
while tb:
|
||||
self.frames.append(Frame(exc_type, exc_value, tb))
|
||||
tb = tb.tb_next
|
||||
|
||||
def filter_hidden_frames(self):
|
||||
"""Remove the frames according to the paste spec."""
|
||||
if not self.frames:
|
||||
return
|
||||
|
||||
new_frames = []
|
||||
hidden = False
|
||||
for frame in self.frames:
|
||||
hide = frame.hide
|
||||
if hide in ('before', 'before_and_this'):
|
||||
new_frames = []
|
||||
hidden = False
|
||||
if hide == 'before_and_this':
|
||||
continue
|
||||
elif hide in ('reset', 'reset_and_this'):
|
||||
hidden = False
|
||||
if hide == 'reset_and_this':
|
||||
continue
|
||||
elif hide in ('after', 'after_and_this'):
|
||||
hidden = True
|
||||
if hide == 'after_and_this':
|
||||
continue
|
||||
elif hide or hidden:
|
||||
continue
|
||||
new_frames.append(frame)
|
||||
|
||||
# if we only have one frame and that frame is from the codeop
|
||||
# module, remove it.
|
||||
if len(new_frames) == 1 and self.frames[0].module == 'codeop':
|
||||
del self.frames[:]
|
||||
|
||||
# if the last frame is missing something went terrible wrong :(
|
||||
elif self.frames[-1] in new_frames:
|
||||
self.frames[:] = new_frames
|
||||
|
||||
def is_syntax_error(self):
|
||||
"""Is it a syntax error?"""
|
||||
return isinstance(self.exc_value, SyntaxError)
|
||||
is_syntax_error = property(is_syntax_error)
|
||||
|
||||
def exception(self):
|
||||
"""String representation of the exception."""
|
||||
buf = traceback.format_exception_only(self.exc_type, self.exc_value)
|
||||
rv = ''.join(buf).strip()
|
||||
return rv.decode('utf-8', 'replace') if PY2 else rv
|
||||
exception = property(exception)
|
||||
|
||||
def log(self, logfile=None):
|
||||
"""Log the ASCII traceback into a file object."""
|
||||
if logfile is None:
|
||||
logfile = sys.stderr
|
||||
tb = self.plaintext.rstrip() + u'\n'
|
||||
if PY2:
|
||||
tb = tb.encode('utf-8', 'replace')
|
||||
logfile.write(tb)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}).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.close()
|
||||
return {
|
||||
'url': resp['html_url'],
|
||||
'id': resp['id']
|
||||
}
|
||||
|
||||
def render_summary(self, include_title=True):
|
||||
"""Render the traceback for the interactive console."""
|
||||
title = ''
|
||||
frames = []
|
||||
classes = ['traceback']
|
||||
if not self.frames:
|
||||
classes.append('noframe-traceback')
|
||||
|
||||
if include_title:
|
||||
if self.is_syntax_error:
|
||||
title = u'Syntax Error'
|
||||
else:
|
||||
title = u'Traceback <em>(most recent call last)</em>:'
|
||||
|
||||
for frame in self.frames:
|
||||
frames.append(u'<li%s>%s' % (
|
||||
frame.info and u' title="%s"' % escape(frame.info) or u'',
|
||||
frame.render()
|
||||
))
|
||||
|
||||
if self.is_syntax_error:
|
||||
description_wrapper = u'<pre class=syntaxerror>%s</pre>'
|
||||
else:
|
||||
description_wrapper = u'<blockquote>%s</blockquote>'
|
||||
|
||||
return SUMMARY_HTML % {
|
||||
'classes': u' '.join(classes),
|
||||
'title': title and u'<h3>%s</h3>' % title or u'',
|
||||
'frames': u'\n'.join(frames),
|
||||
'description': description_wrapper % escape(self.exception)
|
||||
}
|
||||
|
||||
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': evalex and 'true' or 'false',
|
||||
'evalex_trusted': evalex_trusted and 'true' or '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
|
||||
}
|
||||
|
||||
def generate_plaintext_traceback(self):
|
||||
"""Like the plaintext attribute but returns a generator"""
|
||||
yield u'Traceback (most recent call last):'
|
||||
for frame in self.frames:
|
||||
yield u' File "%s", line %s, in %s' % (
|
||||
frame.filename,
|
||||
frame.lineno,
|
||||
frame.function_name
|
||||
)
|
||||
yield u' ' + frame.current_line.strip()
|
||||
yield self.exception
|
||||
|
||||
def plaintext(self):
|
||||
return u'\n'.join(self.generate_plaintext_traceback())
|
||||
plaintext = cached_property(plaintext)
|
||||
|
||||
id = property(lambda x: id(x))
|
||||
|
||||
|
||||
class Frame(object):
|
||||
|
||||
"""A single frame in a traceback."""
|
||||
|
||||
def __init__(self, exc_type, exc_value, tb):
|
||||
self.lineno = tb.tb_lineno
|
||||
self.function_name = tb.tb_frame.f_code.co_name
|
||||
self.locals = tb.tb_frame.f_locals
|
||||
self.globals = tb.tb_frame.f_globals
|
||||
|
||||
fn = inspect.getsourcefile(tb) or inspect.getfile(tb)
|
||||
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.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__')
|
||||
if info is not None:
|
||||
try:
|
||||
info = text_type(info)
|
||||
except UnicodeError:
|
||||
info = str(info).decode('utf-8', 'replace')
|
||||
self.info = info
|
||||
|
||||
def render(self):
|
||||
"""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(),
|
||||
}
|
||||
|
||||
def render_line_context(self):
|
||||
before, current, after = self.get_context_lines()
|
||||
rv = []
|
||||
|
||||
def render_line(line, cls):
|
||||
line = line.expandtabs().rstrip()
|
||||
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 ' '))
|
||||
|
||||
for line in before:
|
||||
render_line(line, 'before')
|
||||
render_line(current, 'current')
|
||||
for line in after:
|
||||
render_line(line, 'after')
|
||||
|
||||
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'):
|
||||
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:]]))
|
||||
except TokenError:
|
||||
offset = 0
|
||||
for line in lines[lineno:lineno + offset]:
|
||||
line.in_frame = True
|
||||
|
||||
# mark current line
|
||||
try:
|
||||
lines[self.lineno - 1].current = True
|
||||
except IndexError:
|
||||
pass
|
||||
|
||||
return lines
|
||||
|
||||
def eval(self, code, mode='single'):
|
||||
"""Evaluate code in the context of the frame."""
|
||||
if isinstance(code, string_types):
|
||||
if PY2 and isinstance(code, unicode): # noqa
|
||||
code = UTF8_COOKIE + code.encode('utf-8')
|
||||
code = compile(code, '<interactive>', mode)
|
||||
return eval(code, self.globals, self.locals)
|
||||
|
||||
@cached_property
|
||||
def sourcelines(self):
|
||||
"""The sourcecode of the file as list of unicode strings."""
|
||||
# get sourcecode from loader or file
|
||||
source = None
|
||||
if self.loader is not None:
|
||||
try:
|
||||
if hasattr(self.loader, 'get_source'):
|
||||
source = self.loader.get_source(self.module)
|
||||
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
|
||||
# if the loader is broken.
|
||||
pass
|
||||
|
||||
if source is None:
|
||||
try:
|
||||
f = open(to_native(self.filename, get_filesystem_encoding()),
|
||||
mode='rb')
|
||||
except IOError:
|
||||
return []
|
||||
try:
|
||||
source = f.read()
|
||||
finally:
|
||||
f.close()
|
||||
|
||||
# already unicode? return right away
|
||||
if isinstance(source, text_type):
|
||||
return source.splitlines()
|
||||
|
||||
# yes. it should be ascii, but we don't want to reject too many
|
||||
# characters in the debugger if something breaks
|
||||
charset = 'utf-8'
|
||||
if source.startswith(UTF8_COOKIE):
|
||||
source = source[3:]
|
||||
else:
|
||||
for idx, match in enumerate(_line_re.finditer(source)):
|
||||
match = _coding_re.search(match.group())
|
||||
if match is not None:
|
||||
charset = match.group(1)
|
||||
break
|
||||
if idx > 1:
|
||||
break
|
||||
|
||||
# on broken cookies we fall back to utf-8 too
|
||||
charset = to_native(charset)
|
||||
try:
|
||||
codecs.lookup(charset)
|
||||
except LookupError:
|
||||
charset = 'utf-8'
|
||||
|
||||
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,
|
||||
)
|
||||
|
||||
@property
|
||||
def current_line(self):
|
||||
try:
|
||||
return self.sourcelines[self.lineno - 1]
|
||||
except IndexError:
|
||||
return u''
|
||||
|
||||
@cached_property
|
||||
def console(self):
|
||||
return Console(self.globals, self.locals)
|
||||
|
||||
id = property(lambda x: id(x))
|
||||
Reference in new issue
Block a user