Merge remote-tracking branch 'web2py/master' into translations
This commit is contained in:
@@ -213,7 +213,7 @@ def select():
|
|||||||
|
|
||||||
if is_imap:
|
if is_imap:
|
||||||
step = 3
|
step = 3
|
||||||
|
|
||||||
stop = start + step
|
stop = start + step
|
||||||
|
|
||||||
table = None
|
table = None
|
||||||
@@ -409,7 +409,7 @@ def ccache():
|
|||||||
import copy
|
import copy
|
||||||
import time
|
import time
|
||||||
import math
|
import math
|
||||||
from gluon import portalocker
|
from pydal.contrib import portalocker
|
||||||
|
|
||||||
ram = {
|
ram = {
|
||||||
'entries': 0,
|
'entries': 0,
|
||||||
@@ -421,7 +421,7 @@ def ccache():
|
|||||||
'oldest': time.time(),
|
'oldest': time.time(),
|
||||||
'keys': []
|
'keys': []
|
||||||
}
|
}
|
||||||
|
|
||||||
disk = copy.copy(ram)
|
disk = copy.copy(ram)
|
||||||
total = copy.copy(ram)
|
total = copy.copy(ram)
|
||||||
disk['keys'] = []
|
disk['keys'] = []
|
||||||
@@ -465,8 +465,7 @@ def ccache():
|
|||||||
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
|
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
|
||||||
|
|
||||||
for key in cache.disk.storage:
|
for key in cache.disk.storage:
|
||||||
value = cache.disk.storage[key]
|
if key == 'web2py_cache_statistics' and isinstance(value[1], dict):
|
||||||
if isinstance(value[1], dict):
|
|
||||||
disk['hits'] = value[1]['hit_total'] - value[1]['misses']
|
disk['hits'] = value[1]['hit_total'] - value[1]['misses']
|
||||||
disk['misses'] = value[1]['misses']
|
disk['misses'] = value[1]['misses']
|
||||||
try:
|
try:
|
||||||
@@ -482,7 +481,7 @@ def ccache():
|
|||||||
disk['oldest'] = value[0]
|
disk['oldest'] = value[0]
|
||||||
disk['keys'].append((key, GetInHMS(time.time() - value[0])))
|
disk['keys'].append((key, GetInHMS(time.time() - value[0])))
|
||||||
|
|
||||||
ram_keys = ram.keys() # ['hits', 'objects', 'ratio', 'entries', 'keys', 'oldest', 'bytes', 'misses']
|
ram_keys = list(ram) # ['hits', 'objects', 'ratio', 'entries', 'keys', 'oldest', 'bytes', 'misses']
|
||||||
ram_keys.remove('ratio')
|
ram_keys.remove('ratio')
|
||||||
ram_keys.remove('oldest')
|
ram_keys.remove('oldest')
|
||||||
for key in ram_keys:
|
for key in ram_keys:
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import gluon.dal
|
|||||||
import gluon.html
|
import gluon.html
|
||||||
import gluon.validators
|
import gluon.validators
|
||||||
import code
|
import code
|
||||||
from gluon.debug import communicate, web_debugger, qdb_debugger
|
from gluon.debug import communicate, web_debugger, dbg_debugger
|
||||||
from gluon._compat import thread
|
from gluon._compat import thread
|
||||||
import pydoc
|
import pydoc
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ def reset():
|
|||||||
return 'done'
|
return 'done'
|
||||||
|
|
||||||
|
|
||||||
# new implementation using qdb
|
# new implementation using dbg
|
||||||
|
|
||||||
def interact():
|
def interact():
|
||||||
app = request.args(0) or 'admin'
|
app = request.args(0) or 'admin'
|
||||||
@@ -149,7 +149,7 @@ def breakpoints():
|
|||||||
if form.accepts(request.vars, session):
|
if form.accepts(request.vars, session):
|
||||||
filename = os.path.join(request.env['applications_parent'],
|
filename = os.path.join(request.env['applications_parent'],
|
||||||
'applications', form.vars.filename)
|
'applications', form.vars.filename)
|
||||||
err = qdb_debugger.do_set_breakpoint(filename,
|
err = dbg_debugger.do_set_breakpoint(filename,
|
||||||
form.vars.lineno,
|
form.vars.lineno,
|
||||||
form.vars.temporary,
|
form.vars.temporary,
|
||||||
form.vars.condition)
|
form.vars.condition)
|
||||||
@@ -158,13 +158,13 @@ def breakpoints():
|
|||||||
|
|
||||||
for item in request.vars:
|
for item in request.vars:
|
||||||
if item[:7] == 'delete_':
|
if item[:7] == 'delete_':
|
||||||
qdb_debugger.do_clear(item[7:])
|
dbg_debugger.do_clear(item[7:])
|
||||||
|
|
||||||
breakpoints = [{'number': bp[0], 'filename': os.path.basename(bp[1]),
|
breakpoints = [{'number': bp[0], 'filename': os.path.basename(bp[1]),
|
||||||
'path': bp[1], 'lineno': bp[2],
|
'path': bp[1], 'lineno': bp[2],
|
||||||
'temporary': bp[3], 'enabled': bp[4], 'hits': bp[5],
|
'temporary': bp[3], 'enabled': bp[4], 'hits': bp[5],
|
||||||
'condition': bp[6]}
|
'condition': bp[6]}
|
||||||
for bp in qdb_debugger.do_list_breakpoint()]
|
for bp in dbg_debugger.do_list_breakpoint()]
|
||||||
|
|
||||||
return dict(breakpoints=breakpoints, form=form)
|
return dict(breakpoints=breakpoints, form=form)
|
||||||
|
|
||||||
@@ -193,18 +193,18 @@ def toggle_breakpoint():
|
|||||||
else:
|
else:
|
||||||
lineno = None
|
lineno = None
|
||||||
if lineno is not None:
|
if lineno is not None:
|
||||||
for bp in qdb_debugger.do_list_breakpoint():
|
for bp in dbg_debugger.do_list_breakpoint():
|
||||||
no, bp_filename, bp_lineno, temporary, enabled, hits, cond = bp
|
no, bp_filename, bp_lineno, temporary, enabled, hits, cond = bp
|
||||||
# normalize path name: replace slashes, references, etc...
|
# normalize path name: replace slashes, references, etc...
|
||||||
bp_filename = os.path.normpath(os.path.normcase(bp_filename))
|
bp_filename = os.path.normpath(os.path.normcase(bp_filename))
|
||||||
if filename == bp_filename and lineno == bp_lineno:
|
if filename == bp_filename and lineno == bp_lineno:
|
||||||
err = qdb_debugger.do_clear_breakpoint(filename, lineno)
|
err = dbg_debugger.do_clear_breakpoint(filename, lineno)
|
||||||
response.flash = T("Removed Breakpoint on %s at line %s", (
|
response.flash = T("Removed Breakpoint on %s at line %s", (
|
||||||
filename, lineno))
|
filename, lineno))
|
||||||
ok = False
|
ok = False
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
err = qdb_debugger.do_set_breakpoint(filename, lineno)
|
err = dbg_debugger.do_set_breakpoint(filename, lineno)
|
||||||
response.flash = T("Set Breakpoint on %s at line %s: %s") % (
|
response.flash = T("Set Breakpoint on %s at line %s: %s") % (
|
||||||
filename, lineno, err or T('successful'))
|
filename, lineno, err or T('successful'))
|
||||||
ok = True
|
ok = True
|
||||||
@@ -224,7 +224,7 @@ def list_breakpoints():
|
|||||||
'applications', request.vars.filename)
|
'applications', request.vars.filename)
|
||||||
# normalize path name: replace slashes, references, etc...
|
# normalize path name: replace slashes, references, etc...
|
||||||
filename = os.path.normpath(os.path.normcase(filename))
|
filename = os.path.normpath(os.path.normcase(filename))
|
||||||
for bp in qdb_debugger.do_list_breakpoint():
|
for bp in dbg_debugger.do_list_breakpoint():
|
||||||
no, bp_filename, bp_lineno, temporary, enabled, hits, cond = bp
|
no, bp_filename, bp_lineno, temporary, enabled, hits, cond = bp
|
||||||
# normalize path name: replace slashes, references, etc...
|
# normalize path name: replace slashes, references, etc...
|
||||||
bp_filename = os.path.normpath(os.path.normcase(bp_filename))
|
bp_filename = os.path.normpath(os.path.normcase(bp_filename))
|
||||||
|
|||||||
@@ -1960,7 +1960,7 @@ def git_push():
|
|||||||
|
|
||||||
def plugins():
|
def plugins():
|
||||||
app = request.args(0)
|
app = request.args(0)
|
||||||
from serializers import loads_json
|
from gluon.serializers import loads_json
|
||||||
if not session.plugins:
|
if not session.plugins:
|
||||||
try:
|
try:
|
||||||
rawlist = urlopen("http://www.web2pyslices.com/" +
|
rawlist = urlopen("http://www.web2pyslices.com/" +
|
||||||
|
|||||||
@@ -81,44 +81,44 @@ def install(app_name, filename, data, overwrite=True):
|
|||||||
|
|
||||||
@service.jsonrpc
|
@service.jsonrpc
|
||||||
def attach_debugger(host='localhost', port=6000, authkey='secret password'):
|
def attach_debugger(host='localhost', port=6000, authkey='secret password'):
|
||||||
import gluon.contrib.qdb as qdb
|
import gluon.contrib.dbg as dbg
|
||||||
import gluon.debug
|
import gluon.debug
|
||||||
from multiprocessing.connection import Listener
|
from multiprocessing.connection import Listener
|
||||||
|
|
||||||
if isinstance(authkey, unicode):
|
if isinstance(authkey, unicode):
|
||||||
authkey = authkey.encode('utf8')
|
authkey = authkey.encode('utf8')
|
||||||
|
|
||||||
if not hasattr(gluon.debug, 'qdb_listener'):
|
if not hasattr(gluon.debug, 'dbg_listener'):
|
||||||
# create a remote debugger server and wait for connection
|
# create a remote debugger server and wait for connection
|
||||||
address = (host, port) # family is deduced to be 'AF_INET'
|
address = (host, port) # family is deduced to be 'AF_INET'
|
||||||
gluon.debug.qdb_listener = Listener(address, authkey=authkey)
|
gluon.debug.dbg_listener = Listener(address, authkey=authkey)
|
||||||
gluon.debug.qdb_connection = gluon.debug.qdb_listener.accept()
|
gluon.debug.dbg_connection = gluon.debug.dbg_listener.accept()
|
||||||
# create the backend
|
# create the backend
|
||||||
gluon.debug.qdb_debugger = qdb.Qdb(gluon.debug.qdb_connection)
|
gluon.debug.dbg_debugger = dbg.Qdb(gluon.debug.dbg_connection)
|
||||||
gluon.debug.dbg = gluon.debug.qdb_debugger
|
gluon.debug.dbg = gluon.debug.dbg_debugger
|
||||||
# welcome message (this should be displayed on the frontend)
|
# welcome message (this should be displayed on the frontend)
|
||||||
print('debugger connected to', gluon.debug.qdb_listener.last_accepted)
|
print('debugger connected to', gluon.debug.dbg_listener.last_accepted)
|
||||||
return True # connection successful!
|
return True # connection successful!
|
||||||
|
|
||||||
|
|
||||||
@service.jsonrpc
|
@service.jsonrpc
|
||||||
def detach_debugger():
|
def detach_debugger():
|
||||||
import gluon.contrib.qdb as qdb
|
import gluon.contrib.dbg as dbg
|
||||||
import gluon.debug
|
import gluon.debug
|
||||||
# stop current debugger
|
# stop current debugger
|
||||||
if gluon.debug.qdb_debugger:
|
if gluon.debug.dbg_debugger:
|
||||||
try:
|
try:
|
||||||
gluon.debug.qdb_debugger.do_quit()
|
gluon.debug.dbg_debugger.do_quit()
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
if hasattr(gluon.debug, 'qdb_listener'):
|
if hasattr(gluon.debug, 'dbg_listener'):
|
||||||
if gluon.debug.qdb_connection:
|
if gluon.debug.dbg_connection:
|
||||||
gluon.debug.qdb_connection.close()
|
gluon.debug.dbg_connection.close()
|
||||||
del gluon.debug.qdb_connection
|
del gluon.debug.dbg_connection
|
||||||
if gluon.debug.qdb_listener:
|
if gluon.debug.dbg_listener:
|
||||||
gluon.debug.qdb_listener.close()
|
gluon.debug.dbg_listener.close()
|
||||||
del gluon.debug.qdb_listener
|
del gluon.debug.dbg_listener
|
||||||
gluon.debug.qdb_debugger = None
|
gluon.debug.dbg_debugger = None
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import pickle
|
|||||||
import urllib
|
import urllib
|
||||||
import glob
|
import glob
|
||||||
from gluon.admin import app_create, plugin_install
|
from gluon.admin import app_create, plugin_install
|
||||||
from gluon.fileutils import abspath, read_file, write_file
|
from gluon.fileutils import abspath, read_file, write_file, open_file
|
||||||
|
|
||||||
|
|
||||||
def reset(session):
|
def reset(session):
|
||||||
@@ -67,7 +67,7 @@ def index():
|
|||||||
'..', app, 'wizard.metadata'))
|
'..', app, 'wizard.metadata'))
|
||||||
if os.path.exists(meta):
|
if os.path.exists(meta):
|
||||||
try:
|
try:
|
||||||
metafile = open(meta, 'rb')
|
metafile = open_file(meta, 'rb')
|
||||||
try:
|
try:
|
||||||
session.app = pickle.load(metafile)
|
session.app = pickle.load(metafile)
|
||||||
finally:
|
finally:
|
||||||
@@ -402,7 +402,7 @@ db.auth_user.email.requires = (
|
|||||||
|
|
||||||
def fix_db(filename):
|
def fix_db(filename):
|
||||||
params = dict(session.app['params'])
|
params = dict(session.app['params'])
|
||||||
content = read_file(filename, 'rb')
|
content = read_file(filename, 'r')
|
||||||
if 'auth_user' in session.app['tables']:
|
if 'auth_user' in session.app['tables']:
|
||||||
auth_user = make_table('auth_user', session.app['table_auth_user'])
|
auth_user = make_table('auth_user', session.app['table_auth_user'])
|
||||||
content = content.replace('sqlite://storage.sqlite',
|
content = content.replace('sqlite://storage.sqlite',
|
||||||
@@ -424,7 +424,7 @@ auth.settings.login_form = RPXAccount(request,
|
|||||||
domain = settings.login_config.split(':')[0],
|
domain = settings.login_config.split(':')[0],
|
||||||
url = "http://%s/%s/default/user/login" % (request.env.http_host,request.application))
|
url = "http://%s/%s/default/user/login" % (request.env.http_host,request.application))
|
||||||
"""
|
"""
|
||||||
write_file(filename, content, 'wb')
|
write_file(filename, content, 'w')
|
||||||
|
|
||||||
|
|
||||||
def make_menu(pages):
|
def make_menu(pages):
|
||||||
@@ -493,7 +493,7 @@ def create(options):
|
|||||||
### save metadata in newapp/wizard.metadata
|
### save metadata in newapp/wizard.metadata
|
||||||
try:
|
try:
|
||||||
meta = os.path.join(request.folder, '..', app, 'wizard.metadata')
|
meta = os.path.join(request.folder, '..', app, 'wizard.metadata')
|
||||||
file = open(meta, 'wb')
|
file = open_file(meta, 'wb')
|
||||||
pickle.dump(session.app, file)
|
pickle.dump(session.app, file)
|
||||||
file.close()
|
file.close()
|
||||||
except IOError:
|
except IOError:
|
||||||
@@ -521,7 +521,7 @@ def create(options):
|
|||||||
|
|
||||||
### write configuration file into newapp/models/0.py
|
### write configuration file into newapp/models/0.py
|
||||||
model = os.path.join(request.folder, '..', app, 'models', '0.py')
|
model = os.path.join(request.folder, '..', app, 'models', '0.py')
|
||||||
file = open(model, 'wb')
|
file = open_file(model, 'w')
|
||||||
try:
|
try:
|
||||||
file.write("from gluon.storage import Storage\n")
|
file.write("from gluon.storage import Storage\n")
|
||||||
file.write("settings = Storage()\n\n")
|
file.write("settings = Storage()\n\n")
|
||||||
@@ -534,7 +534,7 @@ def create(options):
|
|||||||
### write configuration file into newapp/models/menu.py
|
### write configuration file into newapp/models/menu.py
|
||||||
if options.generate_menu:
|
if options.generate_menu:
|
||||||
model = os.path.join(request.folder, '..', app, 'models', 'menu.py')
|
model = os.path.join(request.folder, '..', app, 'models', 'menu.py')
|
||||||
file = open(model, 'wb')
|
file = open_file(model, 'w')
|
||||||
try:
|
try:
|
||||||
file.write(make_menu(session.app['pages']))
|
file.write(make_menu(session.app['pages']))
|
||||||
finally:
|
finally:
|
||||||
@@ -548,7 +548,7 @@ def create(options):
|
|||||||
if options.generate_model:
|
if options.generate_model:
|
||||||
model = os.path.join(
|
model = os.path.join(
|
||||||
request.folder, '..', app, 'models', 'db_wizard.py')
|
request.folder, '..', app, 'models', 'db_wizard.py')
|
||||||
file = open(model, 'wb')
|
file = open_file(model, 'w')
|
||||||
try:
|
try:
|
||||||
file.write('### we prepend t_ to tablenames and f_ to fieldnames for disambiguity\n\n')
|
file.write('### we prepend t_ to tablenames and f_ to fieldnames for disambiguity\n\n')
|
||||||
tables = sort_tables(session.app['tables'])
|
tables = sort_tables(session.app['tables'])
|
||||||
@@ -564,7 +564,7 @@ def create(options):
|
|||||||
if os.path.exists(model):
|
if os.path.exists(model):
|
||||||
os.unlink(model)
|
os.unlink(model)
|
||||||
if options.populate_database and session.app['tables']:
|
if options.populate_database and session.app['tables']:
|
||||||
file = open(model, 'wb')
|
file = open_file(model, 'w')
|
||||||
try:
|
try:
|
||||||
file.write(populate(session.app['tables']))
|
file.write(populate(session.app['tables']))
|
||||||
finally:
|
finally:
|
||||||
@@ -574,7 +574,7 @@ def create(options):
|
|||||||
if options.generate_controller:
|
if options.generate_controller:
|
||||||
controller = os.path.join(
|
controller = os.path.join(
|
||||||
request.folder, '..', app, 'controllers', 'default.py')
|
request.folder, '..', app, 'controllers', 'default.py')
|
||||||
file = open(controller, 'wb')
|
file = open_file(controller, 'w')
|
||||||
try:
|
try:
|
||||||
file.write("""# -*- coding: utf-8 -*-
|
file.write("""# -*- coding: utf-8 -*-
|
||||||
### required - do no delete
|
### required - do no delete
|
||||||
@@ -594,7 +594,7 @@ def call(): return service()
|
|||||||
for page in session.app['pages']:
|
for page in session.app['pages']:
|
||||||
view = os.path.join(
|
view = os.path.join(
|
||||||
request.folder, '..', app, 'views', 'default', page + '.html')
|
request.folder, '..', app, 'views', 'default', page + '.html')
|
||||||
file = open(view, 'wb')
|
file = open_file(view, 'w')
|
||||||
try:
|
try:
|
||||||
file.write(
|
file.write(
|
||||||
make_view(page, session.app.get('page_' + page, '')))
|
make_view(page, session.app.get('page_' + page, '')))
|
||||||
|
|||||||
@@ -338,6 +338,7 @@
|
|||||||
web2py.trap_form(action, target);
|
web2py.trap_form(action, target);
|
||||||
web2py.ajax_init('#' + target);
|
web2py.ajax_init('#' + target);
|
||||||
web2py.after_ajax(xhr);
|
web2py.after_ajax(xhr);
|
||||||
|
web2py.fire(element, 'w2p:componentComplete', [xhr, status], target); // Let us know the component is finished loading
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -213,7 +213,7 @@ def select():
|
|||||||
|
|
||||||
if is_imap:
|
if is_imap:
|
||||||
step = 3
|
step = 3
|
||||||
|
|
||||||
stop = start + step
|
stop = start + step
|
||||||
|
|
||||||
table = None
|
table = None
|
||||||
@@ -409,7 +409,7 @@ def ccache():
|
|||||||
import copy
|
import copy
|
||||||
import time
|
import time
|
||||||
import math
|
import math
|
||||||
from gluon import portalocker
|
from pydal.contrib import portalocker
|
||||||
|
|
||||||
ram = {
|
ram = {
|
||||||
'entries': 0,
|
'entries': 0,
|
||||||
@@ -421,7 +421,7 @@ def ccache():
|
|||||||
'oldest': time.time(),
|
'oldest': time.time(),
|
||||||
'keys': []
|
'keys': []
|
||||||
}
|
}
|
||||||
|
|
||||||
disk = copy.copy(ram)
|
disk = copy.copy(ram)
|
||||||
total = copy.copy(ram)
|
total = copy.copy(ram)
|
||||||
disk['keys'] = []
|
disk['keys'] = []
|
||||||
@@ -465,8 +465,7 @@ def ccache():
|
|||||||
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
|
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
|
||||||
|
|
||||||
for key in cache.disk.storage:
|
for key in cache.disk.storage:
|
||||||
value = cache.disk.storage[key]
|
if key == 'web2py_cache_statistics' and isinstance(value[1], dict):
|
||||||
if isinstance(value[1], dict):
|
|
||||||
disk['hits'] = value[1]['hit_total'] - value[1]['misses']
|
disk['hits'] = value[1]['hit_total'] - value[1]['misses']
|
||||||
disk['misses'] = value[1]['misses']
|
disk['misses'] = value[1]['misses']
|
||||||
try:
|
try:
|
||||||
@@ -482,7 +481,7 @@ def ccache():
|
|||||||
disk['oldest'] = value[0]
|
disk['oldest'] = value[0]
|
||||||
disk['keys'].append((key, GetInHMS(time.time() - value[0])))
|
disk['keys'].append((key, GetInHMS(time.time() - value[0])))
|
||||||
|
|
||||||
ram_keys = ram.keys() # ['hits', 'objects', 'ratio', 'entries', 'keys', 'oldest', 'bytes', 'misses']
|
ram_keys = list(ram) # ['hits', 'objects', 'ratio', 'entries', 'keys', 'oldest', 'bytes', 'misses']
|
||||||
ram_keys.remove('ratio')
|
ram_keys.remove('ratio')
|
||||||
ram_keys.remove('oldest')
|
ram_keys.remove('oldest')
|
||||||
for key in ram_keys:
|
for key in ram_keys:
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<html><body><h1>Temporarily down for maintenance</h1></body></html>
|
||||||
@@ -338,6 +338,7 @@
|
|||||||
web2py.trap_form(action, target);
|
web2py.trap_form(action, target);
|
||||||
web2py.ajax_init('#' + target);
|
web2py.ajax_init('#' + target);
|
||||||
web2py.after_ajax(xhr);
|
web2py.after_ajax(xhr);
|
||||||
|
web2py.fire(element, 'w2p:componentComplete', [xhr, status], target); // Let us know the component is finished loading
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -465,8 +465,7 @@ def ccache():
|
|||||||
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
|
ram['keys'].append((key, GetInHMS(time.time() - value[0])))
|
||||||
|
|
||||||
for key in cache.disk.storage:
|
for key in cache.disk.storage:
|
||||||
value = cache.disk.storage[key]
|
if key == 'web2py_cache_statistics' and isinstance(value[1], dict):
|
||||||
if isinstance(value[1], dict):
|
|
||||||
disk['hits'] = value[1]['hit_total'] - value[1]['misses']
|
disk['hits'] = value[1]['hit_total'] - value[1]['misses']
|
||||||
disk['misses'] = value[1]['misses']
|
disk['misses'] = value[1]['misses']
|
||||||
try:
|
try:
|
||||||
@@ -482,7 +481,7 @@ def ccache():
|
|||||||
disk['oldest'] = value[0]
|
disk['oldest'] = value[0]
|
||||||
disk['keys'].append((key, GetInHMS(time.time() - value[0])))
|
disk['keys'].append((key, GetInHMS(time.time() - value[0])))
|
||||||
|
|
||||||
ram_keys = ram.keys() # ['hits', 'objects', 'ratio', 'entries', 'keys', 'oldest', 'bytes', 'misses']
|
ram_keys = list(ram) # ['hits', 'objects', 'ratio', 'entries', 'keys', 'oldest', 'bytes', 'misses']
|
||||||
ram_keys.remove('ratio')
|
ram_keys.remove('ratio')
|
||||||
ram_keys.remove('oldest')
|
ram_keys.remove('oldest')
|
||||||
for key in ram_keys:
|
for key in ram_keys:
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<html><body><h1>Temporarily down for maintenance</h1></body></html>
|
||||||
@@ -338,6 +338,7 @@
|
|||||||
web2py.trap_form(action, target);
|
web2py.trap_form(action, target);
|
||||||
web2py.ajax_init('#' + target);
|
web2py.ajax_init('#' + target);
|
||||||
web2py.after_ajax(xhr);
|
web2py.after_ajax(xhr);
|
||||||
|
web2py.fire(element, 'w2p:componentComplete', [xhr, status], target); // Let us know the component is finished loading
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# coding:utf-8
|
# coding:utf-8
|
||||||
|
|
||||||
"Queues(Pipe)-based independent remote client-server Python Debugger"
|
"Queues(Pipe)-based independent remote client-server Python Debugger (new-py3)"
|
||||||
|
|
||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
|
||||||
__author__ = "Mariano Reingart (reingart@gmail.com)"
|
__author__ = "Mariano Reingart (reingart@gmail.com)"
|
||||||
__copyright__ = "Copyright (C) 2011 Mariano Reingart"
|
__copyright__ = "Copyright (C) 2011 Mariano Reingart"
|
||||||
__license__ = "LGPL 3.0"
|
__license__ = "LGPL 3.0"
|
||||||
__version__ = "1.01b"
|
__version__ = "1.5.2"
|
||||||
|
|
||||||
# remote debugger queue-based (jsonrpc-like interface):
|
# remote debugger queue-based (jsonrpc-like interface):
|
||||||
# - bidirectional communication (request - response calls in both ways)
|
# - bidirectional communication (request - response calls in both ways)
|
||||||
@@ -24,13 +25,19 @@ import traceback
|
|||||||
import cmd
|
import cmd
|
||||||
import pydoc
|
import pydoc
|
||||||
import threading
|
import threading
|
||||||
|
import collections
|
||||||
|
|
||||||
|
|
||||||
|
# Speed Ups: global variables
|
||||||
|
breaks = []
|
||||||
|
|
||||||
|
|
||||||
class Qdb(bdb.Bdb):
|
class Qdb(bdb.Bdb):
|
||||||
"Qdb Debugger Backend"
|
"Qdb Debugger Backend"
|
||||||
|
|
||||||
def __init__(self, pipe, redirect_stdio=True, allow_interruptions=False,
|
def __init__(self, pipe, redirect_stdio=True, allow_interruptions=False,
|
||||||
skip=[__name__]):
|
use_speedups=True, skip=[__name__]):
|
||||||
|
global breaks
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
if sys.version_info > (2, 7):
|
if sys.version_info > (2, 7):
|
||||||
kwargs['skip'] = skip
|
kwargs['skip'] = skip
|
||||||
@@ -38,12 +45,15 @@ class Qdb(bdb.Bdb):
|
|||||||
self.frame = None
|
self.frame = None
|
||||||
self.i = 1 # sequential RPC call id
|
self.i = 1 # sequential RPC call id
|
||||||
self.waiting = False
|
self.waiting = False
|
||||||
self.pipe = pipe # for communication
|
self.pipe = pipe # for communication
|
||||||
self._wait_for_mainpyfile = False
|
self._wait_for_mainpyfile = False
|
||||||
self._wait_for_breakpoint = False
|
self._wait_for_breakpoint = False
|
||||||
self.mainpyfile = ""
|
self.mainpyfile = ""
|
||||||
self._lineno = None # last listed line numbre
|
self._lineno = None # last listed line numbre
|
||||||
|
# ignore filenames (avoid spurious interaction specially on py2)
|
||||||
|
self.ignore_files = [self.canonic(f) for f in (__file__, bdb.__file__)]
|
||||||
# replace system standard input and output (send them thru the pipe)
|
# replace system standard input and output (send them thru the pipe)
|
||||||
|
self.old_stdio = sys.stdin, sys.stdout, sys.stderr
|
||||||
if redirect_stdio:
|
if redirect_stdio:
|
||||||
sys.stdin = self
|
sys.stdin = self
|
||||||
sys.stdout = self
|
sys.stdout = self
|
||||||
@@ -54,6 +64,10 @@ class Qdb(bdb.Bdb):
|
|||||||
self.allow_interruptions = allow_interruptions
|
self.allow_interruptions = allow_interruptions
|
||||||
self.burst = 0 # do not send notifications ("burst" mode)
|
self.burst = 0 # do not send notifications ("burst" mode)
|
||||||
self.params = {} # optional parameters for interaction
|
self.params = {} # optional parameters for interaction
|
||||||
|
|
||||||
|
# flags to reduce overhead (only stop at breakpoint or interrupt)
|
||||||
|
self.use_speedups = use_speedups
|
||||||
|
self.fast_continue = False
|
||||||
|
|
||||||
def pull_actions(self):
|
def pull_actions(self):
|
||||||
# receive a remote procedure call from the frontend:
|
# receive a remote procedure call from the frontend:
|
||||||
@@ -62,14 +76,14 @@ class Qdb(bdb.Bdb):
|
|||||||
request = self.pipe.recv()
|
request = self.pipe.recv()
|
||||||
if request.get("method") == 'run':
|
if request.get("method") == 'run':
|
||||||
return None
|
return None
|
||||||
response = {'version': '1.1', 'id': request.get('id'),
|
response = {'version': '1.1', 'id': request.get('id'),
|
||||||
'result': None,
|
'result': None,
|
||||||
'error': None}
|
'error': None}
|
||||||
try:
|
try:
|
||||||
# dispatch message (JSON RPC like)
|
# dispatch message (JSON RPC like)
|
||||||
method = getattr(self, request['method'])
|
method = getattr(self, request['method'])
|
||||||
response['result'] = method.__call__(*request['args'],
|
response['result'] = method.__call__(*request['args'],
|
||||||
**request.get('kwargs', {}))
|
**request.get('kwargs', {}))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
response['error'] = {'code': 0, 'message': str(e)}
|
response['error'] = {'code': 0, 'message': str(e)}
|
||||||
# send the result for normal method calls, not for notifications
|
# send the result for normal method calls, not for notifications
|
||||||
@@ -83,9 +97,17 @@ class Qdb(bdb.Bdb):
|
|||||||
# check for non-interaction rpc (set_breakpoint, interrupt)
|
# check for non-interaction rpc (set_breakpoint, interrupt)
|
||||||
while self.allow_interruptions and self.pipe.poll():
|
while self.allow_interruptions and self.pipe.poll():
|
||||||
self.pull_actions()
|
self.pull_actions()
|
||||||
|
# check for non-interaction rpc (set_breakpoint, interrupt)
|
||||||
|
while self.pipe.poll():
|
||||||
|
self.pull_actions()
|
||||||
|
if (frame.f_code.co_filename, frame.f_lineno) not in breaks and \
|
||||||
|
self.fast_continue:
|
||||||
|
return self.trace_dispatch
|
||||||
# process the frame (see Bdb.trace_dispatch)
|
# process the frame (see Bdb.trace_dispatch)
|
||||||
|
##if self.fast_continue:
|
||||||
|
## return self.trace_dispatch
|
||||||
if self.quitting:
|
if self.quitting:
|
||||||
return # None
|
return # None
|
||||||
if event == 'line':
|
if event == 'line':
|
||||||
return self.dispatch_line(frame)
|
return self.dispatch_line(frame)
|
||||||
if event == 'call':
|
if event == 'call':
|
||||||
@@ -102,13 +124,13 @@ class Qdb(bdb.Bdb):
|
|||||||
if self._wait_for_mainpyfile or self._wait_for_breakpoint:
|
if self._wait_for_mainpyfile or self._wait_for_breakpoint:
|
||||||
return
|
return
|
||||||
if self.stop_here(frame):
|
if self.stop_here(frame):
|
||||||
self.interaction(frame, None)
|
self.interaction(frame)
|
||||||
|
|
||||||
def user_line(self, frame):
|
def user_line(self, frame):
|
||||||
"""This function is called when we stop or break at this line."""
|
"""This function is called when we stop or break at this line."""
|
||||||
if self._wait_for_mainpyfile:
|
if self._wait_for_mainpyfile:
|
||||||
if (not self.canonic(frame.f_code.co_filename).startswith(self.mainpyfile)
|
if (not self.canonic(frame.f_code.co_filename).startswith(self.mainpyfile)
|
||||||
or frame.f_lineno <= 0):
|
or frame.f_lineno<= 0):
|
||||||
return
|
return
|
||||||
self._wait_for_mainpyfile = 0
|
self._wait_for_mainpyfile = 0
|
||||||
if self._wait_for_breakpoint:
|
if self._wait_for_breakpoint:
|
||||||
@@ -125,14 +147,15 @@ class Qdb(bdb.Bdb):
|
|||||||
extype, exvalue, trace = info
|
extype, exvalue, trace = info
|
||||||
# pre-process stack trace as it isn't pickeable (cannot be sent pure)
|
# pre-process stack trace as it isn't pickeable (cannot be sent pure)
|
||||||
msg = ''.join(traceback.format_exception(extype, exvalue, trace))
|
msg = ''.join(traceback.format_exception(extype, exvalue, trace))
|
||||||
trace = traceback.extract_tb(trace)
|
# in python3.5, convert FrameSummary to tuples (py2.7+ compatibility)
|
||||||
|
tb = [tuple(fs) for fs in traceback.extract_tb(trace)]
|
||||||
title = traceback.format_exception_only(extype, exvalue)[0]
|
title = traceback.format_exception_only(extype, exvalue)[0]
|
||||||
# send an Exception notification
|
# send an Exception notification
|
||||||
msg = {'method': 'exception',
|
msg = {'method': 'exception',
|
||||||
'args': (title, extype.__name__, exvalue, trace, msg),
|
'args': (title, extype.__name__, repr(exvalue), tb, msg),
|
||||||
'id': None}
|
'id': None}
|
||||||
self.pipe.send(msg)
|
self.pipe.send(msg)
|
||||||
self.interaction(frame, info)
|
self.interaction(frame)
|
||||||
|
|
||||||
def run(self, code, interp=None, *args, **kwargs):
|
def run(self, code, interp=None, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
@@ -151,39 +174,58 @@ class Qdb(bdb.Bdb):
|
|||||||
# The script has to run in __main__ namespace (clear it)
|
# The script has to run in __main__ namespace (clear it)
|
||||||
import __main__
|
import __main__
|
||||||
import imp
|
import imp
|
||||||
|
filename = os.path.abspath(filename)
|
||||||
__main__.__dict__.clear()
|
__main__.__dict__.clear()
|
||||||
__main__.__dict__.update({"__name__": "__main__",
|
__main__.__dict__.update({"__name__" : "__main__",
|
||||||
"__file__": filename,
|
"__file__" : filename,
|
||||||
"__builtins__": __builtins__,
|
"__builtins__": __builtins__,
|
||||||
"imp": imp, # need for run
|
"imp" : imp, # need for run
|
||||||
})
|
})
|
||||||
|
|
||||||
# avoid stopping before we reach the main script
|
# avoid stopping before we reach the main script
|
||||||
self._wait_for_mainpyfile = 1
|
self._wait_for_mainpyfile = 1
|
||||||
self.mainpyfile = self.canonic(filename)
|
self.mainpyfile = self.canonic(filename)
|
||||||
self._user_requested_quit = 0
|
self._user_requested_quit = 0
|
||||||
statement = 'imp.load_source("__main__", "%s")' % filename
|
if sys.version_info>(3,0):
|
||||||
# notify and wait frontend to set initial params and breakpoints
|
statement = 'imp.load_source("__main__", "%s")' % filename
|
||||||
self.pipe.send({'method': 'startup', 'args': (__version__, )})
|
else:
|
||||||
|
statement = 'execfile(%r)' % filename
|
||||||
|
self.startup()
|
||||||
|
self.run(statement)
|
||||||
|
|
||||||
|
def startup(self):
|
||||||
|
"Notify and wait frontend to set initial params and breakpoints"
|
||||||
|
# send some useful info to identify session
|
||||||
|
thread = threading.current_thread()
|
||||||
|
# get the caller module filename
|
||||||
|
frame = sys._getframe()
|
||||||
|
fn = self.canonic(frame.f_code.co_filename)
|
||||||
|
while frame.f_back and self.canonic(frame.f_code.co_filename) == fn:
|
||||||
|
frame = frame.f_back
|
||||||
|
args = [__version__, os.getpid(), thread.name, " ".join(sys.argv),
|
||||||
|
frame.f_code.co_filename]
|
||||||
|
self.pipe.send({'method': 'startup', 'args': args})
|
||||||
while self.pull_actions() is not None:
|
while self.pull_actions() is not None:
|
||||||
pass
|
pass
|
||||||
self.run(statement)
|
|
||||||
|
|
||||||
# General interaction function
|
# General interaction function
|
||||||
|
|
||||||
def interaction(self, frame, info=None):
|
def interaction(self, frame):
|
||||||
# chache frame locals to ensure that modifications are not overwritten
|
# chache frame locals to ensure that modifications are not overwritten
|
||||||
self.frame_locals = frame and frame.f_locals or {}
|
self.frame_locals = frame and frame.f_locals or {}
|
||||||
# extract current filename and line number
|
# extract current filename and line number
|
||||||
code, lineno = frame.f_code, frame.f_lineno
|
code, lineno = frame.f_code, frame.f_lineno
|
||||||
filename = code.co_filename
|
filename = self.canonic(code.co_filename)
|
||||||
basename = os.path.basename(filename)
|
basename = os.path.basename(filename)
|
||||||
|
# check if interaction should be ignored (i.e. debug modules internals)
|
||||||
|
if filename in self.ignore_files:
|
||||||
|
return
|
||||||
message = "%s:%s" % (basename, lineno)
|
message = "%s:%s" % (basename, lineno)
|
||||||
if code.co_name != "?":
|
if code.co_name != "?":
|
||||||
message = "%s: %s()" % (message, code.co_name)
|
message = "%s: %s()" % (message, code.co_name)
|
||||||
|
|
||||||
# wait user events
|
# wait user events
|
||||||
self.waiting = True
|
self.waiting = True
|
||||||
self.frame = frame
|
self.frame = frame
|
||||||
try:
|
try:
|
||||||
while self.waiting:
|
while self.waiting:
|
||||||
@@ -202,11 +244,10 @@ class Qdb(bdb.Bdb):
|
|||||||
if self.params.get('environment'):
|
if self.params.get('environment'):
|
||||||
kwargs['environment'] = self.do_environment()
|
kwargs['environment'] = self.do_environment()
|
||||||
self.pipe.send({'method': 'interaction', 'id': None,
|
self.pipe.send({'method': 'interaction', 'id': None,
|
||||||
'args': (filename, self.frame.f_lineno, line),
|
'args': (filename, self.frame.f_lineno, line),
|
||||||
'kwargs': kwargs})
|
'kwargs': kwargs})
|
||||||
|
|
||||||
self.pull_actions()
|
self.pull_actions()
|
||||||
|
|
||||||
finally:
|
finally:
|
||||||
self.waiting = False
|
self.waiting = False
|
||||||
self.frame = None
|
self.frame = None
|
||||||
@@ -228,6 +269,8 @@ class Qdb(bdb.Bdb):
|
|||||||
frame = sys._getframe().f_back
|
frame = sys._getframe().f_back
|
||||||
self._wait_for_mainpyfile = frame.f_code.co_filename
|
self._wait_for_mainpyfile = frame.f_code.co_filename
|
||||||
self._wait_for_breakpoint = 0
|
self._wait_for_breakpoint = 0
|
||||||
|
# reinitialize debugger internal settings
|
||||||
|
self.fast_continue = False
|
||||||
bdb.Bdb.set_trace(self, frame)
|
bdb.Bdb.set_trace(self, frame)
|
||||||
|
|
||||||
# Command definitions, called by interaction()
|
# Command definitions, called by interaction()
|
||||||
@@ -235,34 +278,38 @@ class Qdb(bdb.Bdb):
|
|||||||
def do_continue(self):
|
def do_continue(self):
|
||||||
self.set_continue()
|
self.set_continue()
|
||||||
self.waiting = False
|
self.waiting = False
|
||||||
|
self.fast_continue = self.use_speedups
|
||||||
|
|
||||||
def do_step(self):
|
def do_step(self):
|
||||||
self.set_step()
|
self.set_step()
|
||||||
self.waiting = False
|
self.waiting = False
|
||||||
|
self.fast_continue = False
|
||||||
|
|
||||||
def do_return(self):
|
def do_return(self):
|
||||||
self.set_return(self.frame)
|
self.set_return(self.frame)
|
||||||
self.waiting = False
|
self.waiting = False
|
||||||
|
self.fast_continue = False
|
||||||
|
|
||||||
def do_next(self):
|
def do_next(self):
|
||||||
self.set_next(self.frame)
|
self.set_next(self.frame)
|
||||||
self.waiting = False
|
self.waiting = False
|
||||||
|
self.fast_continue = False
|
||||||
|
|
||||||
def interrupt(self):
|
def interrupt(self):
|
||||||
self.set_step()
|
self.set_trace()
|
||||||
|
self.fast_continue = False
|
||||||
|
|
||||||
def do_quit(self):
|
def do_quit(self):
|
||||||
self.set_quit()
|
self.set_quit()
|
||||||
self.waiting = False
|
self.waiting = False
|
||||||
|
self.fast_continue = False
|
||||||
|
|
||||||
def do_jump(self, lineno):
|
def do_jump(self, lineno):
|
||||||
arg = int(lineno)
|
arg = int(lineno)
|
||||||
try:
|
try:
|
||||||
self.frame.f_lineno = arg
|
self.frame.f_lineno = arg
|
||||||
return arg
|
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
print('*** Jump failed:', e)
|
return str(e)
|
||||||
return False
|
|
||||||
|
|
||||||
def do_list(self, arg):
|
def do_list(self, arg):
|
||||||
last = None
|
last = None
|
||||||
@@ -272,7 +319,7 @@ class Qdb(bdb.Bdb):
|
|||||||
else:
|
else:
|
||||||
first = arg
|
first = arg
|
||||||
elif not self._lineno:
|
elif not self._lineno:
|
||||||
first = max(1, self.frame.f_lineno - 5)
|
first = max(1, self.frame.f_lineno - 5)
|
||||||
else:
|
else:
|
||||||
first = self._lineno + 1
|
first = self._lineno + 1
|
||||||
if last is None:
|
if last is None:
|
||||||
@@ -280,11 +327,11 @@ class Qdb(bdb.Bdb):
|
|||||||
filename = self.frame.f_code.co_filename
|
filename = self.frame.f_code.co_filename
|
||||||
breaklist = self.get_file_breaks(filename)
|
breaklist = self.get_file_breaks(filename)
|
||||||
lines = []
|
lines = []
|
||||||
for lineno in range(first, last + 1):
|
for lineno in range(first, last+1):
|
||||||
line = linecache.getline(filename, lineno,
|
line = linecache.getline(filename, lineno,
|
||||||
self.frame.f_globals)
|
self.frame.f_globals)
|
||||||
if not line:
|
if not line:
|
||||||
lines.append((filename, lineno, '', current, "<EOF>\n"))
|
lines.append((filename, lineno, '', "", "<EOF>\n"))
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
breakpoint = "B" if lineno in breaklist else ""
|
breakpoint = "B" if lineno in breaklist else ""
|
||||||
@@ -297,6 +344,8 @@ class Qdb(bdb.Bdb):
|
|||||||
return open(filename, "Ur").read()
|
return open(filename, "Ur").read()
|
||||||
|
|
||||||
def do_set_breakpoint(self, filename, lineno, temporary=0, cond=None):
|
def do_set_breakpoint(self, filename, lineno, temporary=0, cond=None):
|
||||||
|
global breaks # list for speedups!
|
||||||
|
breaks.append((filename.replace("\\", "/"), int(lineno)))
|
||||||
return self.set_break(filename, int(lineno), temporary, cond)
|
return self.set_break(filename, int(lineno), temporary, cond)
|
||||||
|
|
||||||
def do_list_breakpoint(self):
|
def do_list_breakpoint(self):
|
||||||
@@ -304,8 +353,8 @@ class Qdb(bdb.Bdb):
|
|||||||
if self.breaks: # There's at least one
|
if self.breaks: # There's at least one
|
||||||
for bp in bdb.Breakpoint.bpbynumber:
|
for bp in bdb.Breakpoint.bpbynumber:
|
||||||
if bp:
|
if bp:
|
||||||
breaks.append((bp.number, bp.file, bp.line,
|
breaks.append((bp.number, bp.file, bp.line,
|
||||||
bp.temporary, bp.enabled, bp.hits, bp.cond, ))
|
bp.temporary, bp.enabled, bp.hits, bp.cond, ))
|
||||||
return breaks
|
return breaks
|
||||||
|
|
||||||
def do_clear_breakpoint(self, filename, lineno):
|
def do_clear_breakpoint(self, filename, lineno):
|
||||||
@@ -321,24 +370,33 @@ class Qdb(bdb.Bdb):
|
|||||||
print('*** DO_CLEAR failed', err)
|
print('*** DO_CLEAR failed', err)
|
||||||
|
|
||||||
def do_eval(self, arg, safe=True):
|
def do_eval(self, arg, safe=True):
|
||||||
ret = eval(arg, self.frame.f_globals,
|
if self.frame:
|
||||||
self.frame_locals)
|
ret = eval(arg, self.frame.f_globals,
|
||||||
|
self.frame_locals)
|
||||||
|
else:
|
||||||
|
ret = RPCError("No current frame available to eval")
|
||||||
if safe:
|
if safe:
|
||||||
ret = pydoc.cram(repr(ret), 255)
|
ret = pydoc.cram(repr(ret), 255)
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def do_exec(self, arg):
|
def do_exec(self, arg, safe=True):
|
||||||
locals = self.frame_locals
|
if not self.frame:
|
||||||
globals = self.frame.f_globals
|
ret = RPCError("No current frame available to exec")
|
||||||
code = compile(arg + '\n', '<stdin>', 'single')
|
else:
|
||||||
save_displayhook = sys.displayhook
|
locals = self.frame_locals
|
||||||
self.displayhook_value = None
|
globals = self.frame.f_globals
|
||||||
try:
|
code = compile(arg + '\n', '<stdin>', 'single')
|
||||||
sys.displayhook = self.displayhook
|
save_displayhook = sys.displayhook
|
||||||
exec code in globals, locals
|
self.displayhook_value = None
|
||||||
finally:
|
try:
|
||||||
sys.displayhook = save_displayhook
|
sys.displayhook = self.displayhook
|
||||||
return self.displayhook_value
|
exec(code, globals, locals)
|
||||||
|
ret = self.displayhook_value
|
||||||
|
finally:
|
||||||
|
sys.displayhook = save_displayhook
|
||||||
|
if safe:
|
||||||
|
ret = pydoc.cram(repr(ret), 255)
|
||||||
|
return ret
|
||||||
|
|
||||||
def do_where(self):
|
def do_where(self):
|
||||||
"print_stack_trace"
|
"print_stack_trace"
|
||||||
@@ -355,29 +413,33 @@ class Qdb(bdb.Bdb):
|
|||||||
env = {'locals': {}, 'globals': {}}
|
env = {'locals': {}, 'globals': {}}
|
||||||
# converts the frame global and locals to a short text representation:
|
# converts the frame global and locals to a short text representation:
|
||||||
if self.frame:
|
if self.frame:
|
||||||
for name, value in self.frame_locals.items():
|
for scope, max_length, vars in (
|
||||||
env['locals'][name] = pydoc.cram(repr(
|
("locals", 255, list(self.frame_locals.items())),
|
||||||
value), 255), repr(type(value))
|
("globals", 20, list(self.frame.f_globals.items())), ):
|
||||||
for name, value in self.frame.f_globals.items():
|
for (name, value) in vars:
|
||||||
env['globals'][name] = pydoc.cram(repr(
|
try:
|
||||||
value), 20), repr(type(value))
|
short_repr = pydoc.cram(repr(value), max_length)
|
||||||
|
except Exception as e:
|
||||||
|
# some objects cannot be represented...
|
||||||
|
short_repr = "**exception** %s" % repr(e)
|
||||||
|
env[scope][name] = (short_repr, repr(type(value)))
|
||||||
return env
|
return env
|
||||||
|
|
||||||
def get_autocomplete_list(self, expression):
|
def get_autocomplete_list(self, expression):
|
||||||
"Return list of auto-completion options for expression"
|
"Return list of auto-completion options for expression"
|
||||||
try:
|
try:
|
||||||
obj = self.do_eval(expression)
|
obj = self.do_eval(expression, safe=False)
|
||||||
except:
|
except:
|
||||||
return []
|
return []
|
||||||
else:
|
else:
|
||||||
return dir(obj)
|
return dir(obj)
|
||||||
|
|
||||||
def get_call_tip(self, expression):
|
def get_call_tip(self, expression):
|
||||||
"Return list of auto-completion options for expression"
|
"Return list of auto-completion options for expression"
|
||||||
try:
|
try:
|
||||||
obj = self.do_eval(expression)
|
obj = self.do_eval(expression)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return ('', '', str(e))
|
return ('', '', str(e))
|
||||||
else:
|
else:
|
||||||
name = ''
|
name = ''
|
||||||
try:
|
try:
|
||||||
@@ -405,7 +467,7 @@ class Qdb(bdb.Bdb):
|
|||||||
break
|
break
|
||||||
if f is not None:
|
if f is not None:
|
||||||
drop_self = 1
|
drop_self = 1
|
||||||
elif callable(obj):
|
elif isinstance(obj, collections.Callable):
|
||||||
# use the obj as a function by default
|
# use the obj as a function by default
|
||||||
f = obj
|
f = obj
|
||||||
# Get the __call__ method instead.
|
# Get the __call__ method instead.
|
||||||
@@ -416,7 +478,7 @@ class Qdb(bdb.Bdb):
|
|||||||
if f:
|
if f:
|
||||||
argspec = inspect.formatargspec(*inspect.getargspec(f))
|
argspec = inspect.formatargspec(*inspect.getargspec(f))
|
||||||
doc = ''
|
doc = ''
|
||||||
if callable(obj):
|
if isinstance(obj, collections.Callable):
|
||||||
try:
|
try:
|
||||||
doc = inspect.getdoc(obj)
|
doc = inspect.getdoc(obj)
|
||||||
except:
|
except:
|
||||||
@@ -442,15 +504,21 @@ class Qdb(bdb.Bdb):
|
|||||||
self.waiting = False
|
self.waiting = False
|
||||||
self.frame = None
|
self.frame = None
|
||||||
|
|
||||||
def post_mortem(self, t=None):
|
def post_mortem(self, info=None):
|
||||||
|
"Debug an un-handled python exception"
|
||||||
|
# check if post mortem mode is enabled:
|
||||||
|
if not self.params.get('postmortem', True):
|
||||||
|
return
|
||||||
# handling the default
|
# handling the default
|
||||||
if t is None:
|
if info is None:
|
||||||
# sys.exc_info() returns (type, value, traceback) if an exception is
|
# sys.exc_info() returns (type, value, traceback) if an exception is
|
||||||
# being handled, otherwise it returns None
|
# being handled, otherwise it returns None
|
||||||
t = sys.exc_info()[2]
|
info = sys.exc_info()
|
||||||
if t is None:
|
# extract the traceback object:
|
||||||
raise ValueError("A valid traceback must be passed if no "
|
t = info[2]
|
||||||
"exception is being handled")
|
if t is None:
|
||||||
|
raise ValueError("A valid traceback must be passed if no "
|
||||||
|
"exception is being handled")
|
||||||
self.reset()
|
self.reset()
|
||||||
# get last frame:
|
# get last frame:
|
||||||
while t is not None:
|
while t is not None:
|
||||||
@@ -460,9 +528,24 @@ class Qdb(bdb.Bdb):
|
|||||||
filename = code.co_filename
|
filename = code.co_filename
|
||||||
line = linecache.getline(filename, lineno)
|
line = linecache.getline(filename, lineno)
|
||||||
#(filename, lineno, "", current, line, )}
|
#(filename, lineno, "", current, line, )}
|
||||||
|
# SyntaxError doesn't execute even one line, so avoid mainpyfile check
|
||||||
|
self._wait_for_mainpyfile = False
|
||||||
|
# send exception information & request interaction
|
||||||
|
self.user_exception(frame, info)
|
||||||
|
|
||||||
self.interaction(frame)
|
def ping(self):
|
||||||
|
"Minimal method to test that the pipe (connection) is alive"
|
||||||
|
try:
|
||||||
|
# get some non-trivial data to compare:
|
||||||
|
args = (id(object()), )
|
||||||
|
msg = {'method': 'ping', 'args': args, 'id': None}
|
||||||
|
self.pipe.send(msg)
|
||||||
|
msg = self.pipe.recv()
|
||||||
|
# check if data received is ok (alive and synchonized!)
|
||||||
|
return msg['result'] == args
|
||||||
|
except (IOError, EOFError):
|
||||||
|
return None
|
||||||
|
|
||||||
# console file-like object emulation
|
# console file-like object emulation
|
||||||
def readline(self):
|
def readline(self):
|
||||||
"Replacement for stdin.readline()"
|
"Replacement for stdin.readline()"
|
||||||
@@ -483,9 +566,9 @@ class Qdb(bdb.Bdb):
|
|||||||
"Replacement for stdout.write()"
|
"Replacement for stdout.write()"
|
||||||
msg = {'method': 'write', 'args': (text, ), 'id': None}
|
msg = {'method': 'write', 'args': (text, ), 'id': None}
|
||||||
self.pipe.send(msg)
|
self.pipe.send(msg)
|
||||||
|
|
||||||
def writelines(self, l):
|
def writelines(self, l):
|
||||||
map(self.write, l)
|
list(map(self.write, l))
|
||||||
|
|
||||||
def flush(self):
|
def flush(self):
|
||||||
pass
|
pass
|
||||||
@@ -493,10 +576,25 @@ class Qdb(bdb.Bdb):
|
|||||||
def isatty(self):
|
def isatty(self):
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
def encoding(self):
|
||||||
|
return None # use default, 'utf-8' should be better...
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
# revert redirections and close connection
|
||||||
|
if sys:
|
||||||
|
sys.stdin, sys.stdout, sys.stderr = self.old_stdio
|
||||||
|
try:
|
||||||
|
self.pipe.close()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
|
||||||
class QueuePipe(object):
|
class QueuePipe(object):
|
||||||
"Simulated pipe for threads (using two queues)"
|
"Simulated pipe for threads (using two queues)"
|
||||||
|
|
||||||
def __init__(self, name, in_queue, out_queue):
|
def __init__(self, name, in_queue, out_queue):
|
||||||
self.__name = name
|
self.__name = name
|
||||||
self.in_queue = in_queue
|
self.in_queue = in_queue
|
||||||
@@ -520,12 +618,13 @@ class RPCError(RuntimeError):
|
|||||||
"Remote Error (not user exception)"
|
"Remote Error (not user exception)"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class Frontend(object):
|
class Frontend(object):
|
||||||
"Qdb generic Frontend interface"
|
"Qdb generic Frontend interface"
|
||||||
|
|
||||||
def __init__(self, pipe):
|
def __init__(self, pipe):
|
||||||
self.i = 1
|
self.i = 1
|
||||||
|
self.info = ()
|
||||||
self.pipe = pipe
|
self.pipe = pipe
|
||||||
self.notifies = []
|
self.notifies = []
|
||||||
self.read_lock = threading.RLock()
|
self.read_lock = threading.RLock()
|
||||||
@@ -545,12 +644,13 @@ class Frontend(object):
|
|||||||
finally:
|
finally:
|
||||||
self.write_lock.release()
|
self.write_lock.release()
|
||||||
|
|
||||||
def startup(self):
|
def startup(self, version, pid, thread_name, argv, filename):
|
||||||
|
self.info = (version, pid, thread_name, argv, filename)
|
||||||
self.send({'method': 'run', 'args': (), 'id': None})
|
self.send({'method': 'run', 'args': (), 'id': None})
|
||||||
|
|
||||||
def interaction(self, filename, lineno, line, *kwargs):
|
def interaction(self, filename, lineno, line, *kwargs):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def exception(self, title, extype, exvalue, trace, request):
|
def exception(self, title, extype, exvalue, trace, request):
|
||||||
"Show a user_exception"
|
"Show a user_exception"
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@@ -558,7 +658,7 @@ class Frontend(object):
|
|||||||
def write(self, text):
|
def write(self, text):
|
||||||
"Console output (print)"
|
"Console output (print)"
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
def readline(self, text):
|
def readline(self, text):
|
||||||
"Console input/rawinput"
|
"Console input/rawinput"
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
@@ -570,10 +670,10 @@ class Frontend(object):
|
|||||||
# wait for a message...
|
# wait for a message...
|
||||||
request = self.recv()
|
request = self.recv()
|
||||||
else:
|
else:
|
||||||
# process an asyncronus notification received earlier
|
# process an asyncronus notification received earlier
|
||||||
request = self.notifies.pop(0)
|
request = self.notifies.pop(0)
|
||||||
return self.process_message(request)
|
return self.process_message(request)
|
||||||
|
|
||||||
def process_message(self, request):
|
def process_message(self, request):
|
||||||
if request:
|
if request:
|
||||||
result = None
|
result = None
|
||||||
@@ -584,17 +684,19 @@ class Frontend(object):
|
|||||||
elif request.get('method') == 'interaction':
|
elif request.get('method') == 'interaction':
|
||||||
self.interaction(*request.get("args"), **request.get("kwargs"))
|
self.interaction(*request.get("args"), **request.get("kwargs"))
|
||||||
elif request.get('method') == 'startup':
|
elif request.get('method') == 'startup':
|
||||||
self.startup()
|
self.startup(*request['args'])
|
||||||
elif request.get('method') == 'exception':
|
elif request.get('method') == 'exception':
|
||||||
self.exception(*request['args'])
|
self.exception(*request['args'])
|
||||||
elif request.get('method') == 'write':
|
elif request.get('method') == 'write':
|
||||||
self.write(*request.get("args"))
|
self.write(*request.get("args"))
|
||||||
elif request.get('method') == 'readline':
|
elif request.get('method') == 'readline':
|
||||||
result = self.readline()
|
result = self.readline()
|
||||||
|
elif request.get('method') == 'ping':
|
||||||
|
result = request['args']
|
||||||
if result:
|
if result:
|
||||||
response = {'version': '1.1', 'id': request.get('id'),
|
response = {'version': '1.1', 'id': request.get('id'),
|
||||||
'result': result,
|
'result': result,
|
||||||
'error': None}
|
'error': None}
|
||||||
self.send(response)
|
self.send(response)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
@@ -603,18 +705,17 @@ class Frontend(object):
|
|||||||
req = {'method': method, 'args': args, 'id': self.i}
|
req = {'method': method, 'args': args, 'id': self.i}
|
||||||
self.send(req)
|
self.send(req)
|
||||||
self.i += 1 # increment the id
|
self.i += 1 # increment the id
|
||||||
while True:
|
while 1:
|
||||||
# wait until command acknowledge (response id match the request)
|
# wait until command acknowledge (response id match the request)
|
||||||
res = self.recv()
|
res = self.recv()
|
||||||
if 'id' not in res or not res['id']:
|
if 'id' not in res or not res['id']:
|
||||||
# nested notification received (i.e. write)! process it!
|
# nested notification received (i.e. write)! process it later...
|
||||||
self.process_message(res)
|
self.notifies.append(res)
|
||||||
elif 'result' not in res:
|
elif 'result' not in res:
|
||||||
# nested request received (i.e. readline)! process it!
|
# nested request received (i.e. readline)! process it!
|
||||||
self.process_message(res)
|
self.process_message(res)
|
||||||
elif long(req['id']) != long(res['id']):
|
elif int(req['id']) != int(res['id']):
|
||||||
print("DEBUGGER wrong packet received: expecting id", req[
|
print("DEBUGGER wrong packet received: expecting id", req['id'], res['id'])
|
||||||
'id'], res['id'])
|
|
||||||
# protocol state is unknown
|
# protocol state is unknown
|
||||||
elif 'error' in res and res['error']:
|
elif 'error' in res and res['error']:
|
||||||
raise RPCError(res['error']['message'])
|
raise RPCError(res['error']['message'])
|
||||||
@@ -624,23 +725,23 @@ class Frontend(object):
|
|||||||
def do_step(self, arg=None):
|
def do_step(self, arg=None):
|
||||||
"Execute the current line, stop at the first possible occasion"
|
"Execute the current line, stop at the first possible occasion"
|
||||||
self.call('do_step')
|
self.call('do_step')
|
||||||
|
|
||||||
def do_next(self, arg=None):
|
def do_next(self, arg=None):
|
||||||
"Execute the current line, do not stop at function calls"
|
"Execute the current line, do not stop at function calls"
|
||||||
self.call('do_next')
|
self.call('do_next')
|
||||||
|
|
||||||
def do_continue(self, arg=None):
|
def do_continue(self, arg=None):
|
||||||
"Continue execution, only stop when a breakpoint is encountered."
|
"Continue execution, only stop when a breakpoint is encountered."
|
||||||
self.call('do_continue')
|
self.call('do_continue')
|
||||||
|
|
||||||
def do_return(self, arg=None):
|
def do_return(self, arg=None):
|
||||||
"Continue execution until the current function returns"
|
"Continue execution until the current function returns"
|
||||||
self.call('do_return')
|
self.call('do_return')
|
||||||
|
|
||||||
def do_jump(self, arg):
|
def do_jump(self, arg):
|
||||||
"Set the next line that will be executed."
|
"Set the next line that will be executed (None if sucess or message)"
|
||||||
res = self.call('do_jump', arg)
|
res = self.call('do_jump', arg)
|
||||||
print(res)
|
return res
|
||||||
|
|
||||||
def do_where(self, arg=None):
|
def do_where(self, arg=None):
|
||||||
"Print a stack trace, with the most recent frame at the bottom."
|
"Print a stack trace, with the most recent frame at the bottom."
|
||||||
@@ -649,7 +750,7 @@ class Frontend(object):
|
|||||||
def do_quit(self, arg=None):
|
def do_quit(self, arg=None):
|
||||||
"Quit from the debugger. The program being executed is aborted."
|
"Quit from the debugger. The program being executed is aborted."
|
||||||
self.call('do_quit')
|
self.call('do_quit')
|
||||||
|
|
||||||
def do_eval(self, expr):
|
def do_eval(self, expr):
|
||||||
"Inspect the value of the expression"
|
"Inspect the value of the expression"
|
||||||
return self.call('do_eval', expr)
|
return self.call('do_eval', expr)
|
||||||
@@ -677,11 +778,11 @@ class Frontend(object):
|
|||||||
def do_clear_file_breakpoints(self, filename):
|
def do_clear_file_breakpoints(self, filename):
|
||||||
"Remove all breakpoints at filename"
|
"Remove all breakpoints at filename"
|
||||||
self.call('do_clear_breakpoints', filename, lineno)
|
self.call('do_clear_breakpoints', filename, lineno)
|
||||||
|
|
||||||
def do_list_breakpoint(self):
|
def do_list_breakpoint(self):
|
||||||
"List all breakpoints"
|
"List all breakpoints"
|
||||||
return self.call('do_list_breakpoint')
|
return self.call('do_list_breakpoint')
|
||||||
|
|
||||||
def do_exec(self, statement):
|
def do_exec(self, statement):
|
||||||
return self.call('do_exec', statement)
|
return self.call('do_exec', statement)
|
||||||
|
|
||||||
@@ -690,7 +791,7 @@ class Frontend(object):
|
|||||||
|
|
||||||
def get_call_tip(self, expression):
|
def get_call_tip(self, expression):
|
||||||
return self.call('get_call_tip', expression)
|
return self.call('get_call_tip', expression)
|
||||||
|
|
||||||
def interrupt(self):
|
def interrupt(self):
|
||||||
"Immediately stop at the first possible occasion (outside interaction)"
|
"Immediately stop at the first possible occasion (outside interaction)"
|
||||||
# this is a notification!, do not expect a response
|
# this is a notification!, do not expect a response
|
||||||
@@ -700,7 +801,7 @@ class Frontend(object):
|
|||||||
def set_burst(self, value):
|
def set_burst(self, value):
|
||||||
req = {'method': 'set_burst', 'args': (value, )}
|
req = {'method': 'set_burst', 'args': (value, )}
|
||||||
self.send(req)
|
self.send(req)
|
||||||
|
|
||||||
def set_params(self, params):
|
def set_params(self, params):
|
||||||
req = {'method': 'set_params', 'args': (params, )}
|
req = {'method': 'set_params', 'args': (params, )}
|
||||||
self.send(req)
|
self.send(req)
|
||||||
@@ -708,15 +809,15 @@ class Frontend(object):
|
|||||||
|
|
||||||
class Cli(Frontend, cmd.Cmd):
|
class Cli(Frontend, cmd.Cmd):
|
||||||
"Qdb Front-end command line interface"
|
"Qdb Front-end command line interface"
|
||||||
|
|
||||||
def __init__(self, pipe, completekey='tab', stdin=None, stdout=None, skip=None):
|
def __init__(self, pipe, completekey='tab', stdin=None, stdout=None, skip=None):
|
||||||
cmd.Cmd.__init__(self, completekey, stdin, stdout)
|
cmd.Cmd.__init__(self, completekey, stdin, stdout)
|
||||||
Frontend.__init__(self, pipe)
|
Frontend.__init__(self, pipe)
|
||||||
|
|
||||||
# redefine Frontend methods:
|
# redefine Frontend methods:
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
while True:
|
while 1:
|
||||||
try:
|
try:
|
||||||
Frontend.run(self)
|
Frontend.run(self)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
@@ -736,31 +837,30 @@ class Cli(Frontend, cmd.Cmd):
|
|||||||
|
|
||||||
def write(self, text):
|
def write(self, text):
|
||||||
print(text, end=' ')
|
print(text, end=' ')
|
||||||
|
|
||||||
def readline(self):
|
def readline(self):
|
||||||
return raw_input()
|
return input()
|
||||||
|
|
||||||
def postcmd(self, stop, line):
|
def postcmd(self, stop, line):
|
||||||
return not line.startswith("h") # stop
|
return not line.startswith("h") # stop
|
||||||
|
|
||||||
do_h = cmd.Cmd.do_help
|
do_h = cmd.Cmd.do_help
|
||||||
|
|
||||||
do_s = Frontend.do_step
|
do_s = Frontend.do_step
|
||||||
do_n = Frontend.do_next
|
do_n = Frontend.do_next
|
||||||
do_c = Frontend.do_continue
|
do_c = Frontend.do_continue
|
||||||
do_r = Frontend.do_return
|
do_r = Frontend.do_return
|
||||||
do_j = Frontend.do_jump
|
|
||||||
do_q = Frontend.do_quit
|
do_q = Frontend.do_quit
|
||||||
|
|
||||||
def do_eval(self, args):
|
def do_eval(self, args):
|
||||||
"Inspect the value of the expression"
|
"Inspect the value of the expression"
|
||||||
print(Frontend.do_eval(self, args))
|
print(Frontend.do_eval(self, args))
|
||||||
|
|
||||||
def do_list(self, args):
|
def do_list(self, args):
|
||||||
"List source code for the current file"
|
"List source code for the current file"
|
||||||
lines = Frontend.do_list(self, eval(args, {}, {}) if args else None)
|
lines = Frontend.do_list(self, eval(args, {}, {}) if args else None)
|
||||||
self.print_lines(lines)
|
self.print_lines(lines)
|
||||||
|
|
||||||
def do_where(self, args):
|
def do_where(self, args):
|
||||||
"Print a stack trace, with the most recent frame at the bottom."
|
"Print a stack trace, with the most recent frame at the bottom."
|
||||||
lines = Frontend.do_where(self)
|
lines = Frontend.do_where(self)
|
||||||
@@ -772,7 +872,7 @@ class Cli(Frontend, cmd.Cmd):
|
|||||||
print("=" * 78)
|
print("=" * 78)
|
||||||
print(key.capitalize())
|
print(key.capitalize())
|
||||||
print("-" * 78)
|
print("-" * 78)
|
||||||
for name, value in env[key].items():
|
for name, value in list(env[key].items()):
|
||||||
print("%-12s = %s" % (name, value))
|
print("%-12s = %s" % (name, value))
|
||||||
|
|
||||||
def do_list_breakpoint(self, arg=None):
|
def do_list_breakpoint(self, arg=None):
|
||||||
@@ -794,11 +894,18 @@ class Cli(Frontend, cmd.Cmd):
|
|||||||
else:
|
else:
|
||||||
self.do_list_breakpoint()
|
self.do_list_breakpoint()
|
||||||
|
|
||||||
|
def do_jump(self, args):
|
||||||
|
"Jump to the selected line"
|
||||||
|
ret = Frontend.do_jump(self, args)
|
||||||
|
if ret: # show error message if failed
|
||||||
|
print("cannot jump:", ret)
|
||||||
|
|
||||||
do_b = do_set_breakpoint
|
do_b = do_set_breakpoint
|
||||||
do_l = do_list
|
do_l = do_list
|
||||||
do_p = do_eval
|
do_p = do_eval
|
||||||
do_w = do_where
|
do_w = do_where
|
||||||
do_e = do_environment
|
do_e = do_environment
|
||||||
|
do_j = do_jump
|
||||||
|
|
||||||
def default(self, line):
|
def default(self, line):
|
||||||
"Default command"
|
"Default command"
|
||||||
@@ -813,61 +920,85 @@ class Cli(Frontend, cmd.Cmd):
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
|
|
||||||
|
# WORKAROUND for python3 server using pickle's HIGHEST_PROTOCOL (now 3)
|
||||||
|
# but python2 client using pickles's protocol version 2
|
||||||
|
if sys.version_info[0] > 2:
|
||||||
|
|
||||||
|
import multiprocessing.reduction # forking in py2
|
||||||
|
|
||||||
|
class ForkingPickler2(multiprocessing.reduction.ForkingPickler):
|
||||||
|
def __init__(self, file, protocol=None, fix_imports=True):
|
||||||
|
# downgrade to protocol ver 2
|
||||||
|
protocol = 2
|
||||||
|
super().__init__(file, protocol, fix_imports)
|
||||||
|
|
||||||
|
multiprocessing.reduction.ForkingPickler = ForkingPickler2
|
||||||
|
|
||||||
|
|
||||||
|
def f(pipe):
|
||||||
|
"test function to be debugged"
|
||||||
|
print("creating debugger")
|
||||||
|
qdb_test = Qdb(pipe=pipe, redirect_stdio=False, allow_interruptions=True)
|
||||||
|
print("set trace")
|
||||||
|
|
||||||
|
my_var = "Mariano!"
|
||||||
|
qdb_test.set_trace()
|
||||||
|
print("hello world!")
|
||||||
|
for i in range(100000):
|
||||||
|
pass
|
||||||
|
print("good by!")
|
||||||
|
|
||||||
|
|
||||||
def test():
|
def test():
|
||||||
def f(pipe):
|
"Create a backend/frontend and time it"
|
||||||
print("creating debugger")
|
|
||||||
qdb = Qdb(pipe=pipe, redirect_stdio=False)
|
|
||||||
print("set trace")
|
|
||||||
|
|
||||||
my_var = "Mariano!"
|
|
||||||
qdb.set_trace()
|
|
||||||
print("hello world!")
|
|
||||||
print("good by!")
|
|
||||||
saraza
|
|
||||||
|
|
||||||
if '--process' in sys.argv:
|
if '--process' in sys.argv:
|
||||||
from multiprocessing import Process, Pipe
|
from multiprocessing import Process, Pipe
|
||||||
pipe, child_conn = Pipe()
|
front_conn, child_conn = Pipe()
|
||||||
p = Process(target=f, args=(child_conn,))
|
p = Process(target=f, args=(child_conn,))
|
||||||
else:
|
else:
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from Queue import Queue
|
from queue import Queue
|
||||||
parent_queue, child_queue = Queue(), Queue()
|
parent_queue, child_queue = Queue(), Queue()
|
||||||
front_conn = QueuePipe("parent", parent_queue, child_queue)
|
front_conn = QueuePipe("parent", parent_queue, child_queue)
|
||||||
child_conn = QueuePipe("child", child_queue, parent_queue)
|
child_conn = QueuePipe("child", child_queue, parent_queue)
|
||||||
p = Thread(target=f, args=(child_conn,))
|
p = Thread(target=f, args=(child_conn,))
|
||||||
|
|
||||||
p.start()
|
p.start()
|
||||||
import time
|
import time
|
||||||
|
|
||||||
class Test(Frontend):
|
class Test(Frontend):
|
||||||
def interaction(self, *args):
|
def interaction(self, *args):
|
||||||
print("interaction!", args)
|
print("interaction!", args)
|
||||||
|
##self.do_next()
|
||||||
def exception(self, *args):
|
def exception(self, *args):
|
||||||
print("exception", args)
|
print("exception", args)
|
||||||
#raise RuntimeError("exception %s" % repr(args))
|
|
||||||
|
|
||||||
qdb = Test(front_conn)
|
qdb_test = Test(front_conn)
|
||||||
time.sleep(5)
|
time.sleep(1)
|
||||||
|
t0 = time.time()
|
||||||
while True:
|
|
||||||
print("running...")
|
print("running...")
|
||||||
Frontend.run(qdb)
|
while front_conn.poll():
|
||||||
time.sleep(1)
|
Frontend.run(qdb_test)
|
||||||
print("do_next")
|
qdb_test.do_continue()
|
||||||
qdb.do_next()
|
|
||||||
p.join()
|
p.join()
|
||||||
|
t1 = time.time()
|
||||||
|
print("took", t1 - t0, "seconds")
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
def connect(host="localhost", port=6000, authkey='secret password'):
|
def start(host="localhost", port=6000, authkey='secret password'):
|
||||||
"Connect to a running debugger backend"
|
"Start the CLI server and wait connection from a running debugger backend"
|
||||||
|
|
||||||
address = (host, port)
|
address = (host, port)
|
||||||
from multiprocessing.connection import Client
|
from multiprocessing.connection import Listener
|
||||||
|
address = (host, port) # family is deduced to be 'AF_INET'
|
||||||
print("qdb debugger fronted: waiting for connection to", address)
|
if isinstance(authkey, str):
|
||||||
conn = Client(address, authkey=authkey)
|
authkey = authkey.encode("utf8")
|
||||||
|
listener = Listener(address, authkey=authkey)
|
||||||
|
print("qdb debugger backend: waiting for connection at", address)
|
||||||
|
conn = listener.accept()
|
||||||
|
print('qdb debugger backend: connected to', listener.last_accepted)
|
||||||
try:
|
try:
|
||||||
Cli(conn).run()
|
Cli(conn).run()
|
||||||
except EOFError:
|
except EOFError:
|
||||||
@@ -877,13 +1008,13 @@ def connect(host="localhost", port=6000, authkey='secret password'):
|
|||||||
|
|
||||||
|
|
||||||
def main(host='localhost', port=6000, authkey='secret password'):
|
def main(host='localhost', port=6000, authkey='secret password'):
|
||||||
"Debug a script and accept a remote frontend"
|
"Debug a script (running under the backend) and connect to remote frontend"
|
||||||
|
|
||||||
if not sys.argv[1:] or sys.argv[1] in ("--help", "-h"):
|
if not sys.argv[1:] or sys.argv[1] in ("--help", "-h"):
|
||||||
print("usage: pdb.py scriptfile [arg] ...")
|
print("usage: pdb.py scriptfile [arg] ...")
|
||||||
sys.exit(2)
|
sys.exit(2)
|
||||||
|
|
||||||
mainpyfile = sys.argv[1] # Get script filename
|
mainpyfile = sys.argv[1] # Get script filename
|
||||||
if not os.path.exists(mainpyfile):
|
if not os.path.exists(mainpyfile):
|
||||||
print('Error:', mainpyfile, 'does not exist')
|
print('Error:', mainpyfile, 'does not exist')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@@ -893,15 +1024,8 @@ def main(host='localhost', port=6000, authkey='secret password'):
|
|||||||
# Replace pdb's dir with script's dir in front of module search path.
|
# Replace pdb's dir with script's dir in front of module search path.
|
||||||
sys.path[0] = os.path.dirname(mainpyfile)
|
sys.path[0] = os.path.dirname(mainpyfile)
|
||||||
|
|
||||||
from multiprocessing.connection import Listener
|
|
||||||
address = (host, port) # family is deduced to be 'AF_INET'
|
|
||||||
listener = Listener(address, authkey=authkey)
|
|
||||||
print("qdb debugger backend: waiting for connection at", address)
|
|
||||||
conn = listener.accept()
|
|
||||||
print('qdb debugger backend: connected to', listener.last_accepted)
|
|
||||||
|
|
||||||
# create the backend
|
# create the backend
|
||||||
qdb = Qdb(conn, redirect_stdio=True, allow_interruptions=True)
|
init(host, port, authkey)
|
||||||
try:
|
try:
|
||||||
print("running", mainpyfile)
|
print("running", mainpyfile)
|
||||||
qdb._runscript(mainpyfile)
|
qdb._runscript(mainpyfile)
|
||||||
@@ -911,32 +1035,57 @@ def main(host='localhost', port=6000, authkey='secret password'):
|
|||||||
print("The program exited via sys.exit(). Exit status: ", end=' ')
|
print("The program exited via sys.exit(). Exit status: ", end=' ')
|
||||||
print(sys.exc_info()[1])
|
print(sys.exc_info()[1])
|
||||||
raise
|
raise
|
||||||
except:
|
except Exception:
|
||||||
raise
|
traceback.print_exc()
|
||||||
|
print("Uncaught exception. Entering post mortem debugging")
|
||||||
conn.close()
|
info = sys.exc_info()
|
||||||
listener.close()
|
qdb.post_mortem(info)
|
||||||
|
print("Program terminated!")
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
print("qdb debbuger backend: connection closed")
|
||||||
|
|
||||||
|
|
||||||
|
# "singleton" to store a unique backend per process
|
||||||
qdb = None
|
qdb = None
|
||||||
|
|
||||||
|
|
||||||
def set_trace(host='localhost', port=6000, authkey='secret password'):
|
def init(host='localhost', port=6000, authkey='secret password', redirect=True):
|
||||||
"Simplified interface to debug running programs"
|
"Simplified interface to debug running programs"
|
||||||
global qdb, listener, conn
|
global qdb, listener, conn
|
||||||
|
|
||||||
from multiprocessing.connection import Listener
|
# destroy the debugger if the previous connection is lost (i.e. broken pipe)
|
||||||
|
if qdb and not qdb.ping():
|
||||||
|
qdb.close()
|
||||||
|
qdb = None
|
||||||
|
|
||||||
|
from multiprocessing.connection import Client
|
||||||
# only create it if not currently instantiated
|
# only create it if not currently instantiated
|
||||||
if not qdb:
|
if not qdb:
|
||||||
address = (host, port) # family is deduced to be 'AF_INET'
|
address = (host, port) # family is deduced to be 'AF_INET'
|
||||||
listener = Listener(address, authkey=authkey)
|
print("qdb debugger backend: waiting for connection to", address)
|
||||||
conn = listener.accept()
|
if isinstance(authkey, str):
|
||||||
|
authkey = authkey.encode("utf8")
|
||||||
|
conn = Client(address, authkey=authkey)
|
||||||
|
print('qdb debugger backend: connected to', address)
|
||||||
# create the backend
|
# create the backend
|
||||||
qdb = Qdb(conn)
|
qdb = Qdb(conn, redirect_stdio=redirect, allow_interruptions=True)
|
||||||
|
# initial hanshake
|
||||||
|
qdb.startup()
|
||||||
|
|
||||||
|
|
||||||
|
def set_trace(host='localhost', port=6000, authkey='secret password'):
|
||||||
|
"Simplified interface to start debugging immediately"
|
||||||
|
init(host, port, authkey)
|
||||||
# start debugger backend:
|
# start debugger backend:
|
||||||
qdb.set_trace()
|
qdb.set_trace()
|
||||||
|
|
||||||
|
def debug(host='localhost', port=6000, authkey='secret password'):
|
||||||
|
"Simplified interface to start debugging immediately (no stop)"
|
||||||
|
init(host, port, authkey)
|
||||||
|
# start debugger backend:
|
||||||
|
qdb.do_debug()
|
||||||
|
|
||||||
|
|
||||||
def quit():
|
def quit():
|
||||||
"Remove trace and quit"
|
"Remove trace and quit"
|
||||||
@@ -948,24 +1097,25 @@ def quit():
|
|||||||
conn.close()
|
conn.close()
|
||||||
conn = None
|
conn = None
|
||||||
if listener:
|
if listener:
|
||||||
listener.close()
|
listener.close()
|
||||||
listener = None
|
listener = None
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
# When invoked as main program:
|
# When invoked as main program:
|
||||||
if '--test' in sys.argv:
|
if '--test1' in sys.argv:
|
||||||
test()
|
test()
|
||||||
# Check environment for configuration parameters:
|
# Check environment for configuration parameters:
|
||||||
kwargs = {}
|
kwargs = {}
|
||||||
for param in 'host', 'port', 'authkey':
|
for param in 'host', 'port', 'authkey':
|
||||||
if 'QDB_%s' % param.upper() in os.environ:
|
if 'DBG_%s' % param.upper() in os.environ:
|
||||||
kwargs[param] = os.environ['QDB_%s' % param.upper()]
|
kwargs[param] = os.environ['DBG_%s' % param.upper()]
|
||||||
|
|
||||||
if not sys.argv[1:]:
|
if not sys.argv[1:]:
|
||||||
# connect to a remote debbuger
|
# connect to a remote debbuger
|
||||||
connect(**kwargs)
|
start(**kwargs)
|
||||||
else:
|
else:
|
||||||
# start the debugger on a script
|
# start the debugger on a script
|
||||||
# reimport as global __main__ namespace is destroyed
|
# reimport as global __main__ namespace is destroyed
|
||||||
import qdb
|
import dbg
|
||||||
qdb.main(**kwargs)
|
dbg.main(**kwargs)
|
||||||
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
from __future__ import print_function
|
from __future__ import print_function
|
||||||
|
from gluon._compat import pickle, unicodeT
|
||||||
|
from gluon.fileutils import open_file
|
||||||
import re
|
import re
|
||||||
import cPickle
|
|
||||||
import random
|
import random
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
@@ -32,10 +33,10 @@ class Learner:
|
|||||||
self.db[item][nextitem] += 1
|
self.db[item][nextitem] += 1
|
||||||
|
|
||||||
def save(self, filename):
|
def save(self, filename):
|
||||||
cPickle.dump(self.db, open(filename, 'wb'))
|
pickle.dump(self.db, open_file(filename, 'wb'))
|
||||||
|
|
||||||
def load(self, filename):
|
def load(self, filename):
|
||||||
self.loadd(cPickle.load(open(filename, 'rb')))
|
self.loadd(pickle.load(open_file(filename, 'rb')))
|
||||||
|
|
||||||
def loadd(self, db):
|
def loadd(self, db):
|
||||||
self.db = db
|
self.db = db
|
||||||
@@ -43,7 +44,7 @@ class Learner:
|
|||||||
def generate(self, length=10000, prefix=False):
|
def generate(self, length=10000, prefix=False):
|
||||||
replacements2 = {' ,': ',', ' \.': '.\n', ' :': ':', ' ;':
|
replacements2 = {' ,': ',', ' \.': '.\n', ' :': ':', ' ;':
|
||||||
';', '\n\s+': '\n'}
|
';', '\n\s+': '\n'}
|
||||||
keys = self.db.keys()
|
keys = list(self.db.keys())
|
||||||
key = keys[random.randint(0, len(keys) - 1)]
|
key = keys[random.randint(0, len(keys) - 1)]
|
||||||
words = key
|
words = key
|
||||||
words = words.capitalize()
|
words = words.capitalize()
|
||||||
@@ -130,7 +131,7 @@ def populate_generator(table, default=True, compute=False, contents={}):
|
|||||||
continue # if user supplied it, let it be.
|
continue # if user supplied it, let it be.
|
||||||
|
|
||||||
field = table[fieldname]
|
field = table[fieldname]
|
||||||
if not isinstance(field.type, (str, unicode)):
|
if not isinstance(field.type, (str, unicodeT)):
|
||||||
continue
|
continue
|
||||||
elif field.type == 'id':
|
elif field.type == 'id':
|
||||||
continue
|
continue
|
||||||
|
|||||||
@@ -46,6 +46,17 @@ class WebClient(object):
|
|||||||
self.default_headers = default_headers
|
self.default_headers = default_headers
|
||||||
self.sessions = {}
|
self.sessions = {}
|
||||||
self.session_regex = session_regex and re.compile(session_regex)
|
self.session_regex = session_regex and re.compile(session_regex)
|
||||||
|
self.headers = {}
|
||||||
|
|
||||||
|
def _parse_headers_in_cookies(self):
|
||||||
|
self.cookies = {}
|
||||||
|
if 'set-cookie' in self.headers:
|
||||||
|
for item in self.headers['set-cookie'].split(','):
|
||||||
|
cookie = item[:item.find(';')]
|
||||||
|
pos = cookie.find('=')
|
||||||
|
key = cookie[:pos]
|
||||||
|
value = cookie[pos+1:]
|
||||||
|
self.cookies[key.strip()] = value.strip()
|
||||||
|
|
||||||
def get(self, url, cookies=None, headers=None, auth=None):
|
def get(self, url, cookies=None, headers=None, auth=None):
|
||||||
return self.post(url, data=None, cookies=cookies,
|
return self.post(url, data=None, cookies=cookies,
|
||||||
@@ -149,12 +160,7 @@ class WebClient(object):
|
|||||||
else:
|
else:
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
# parse headers into cookies
|
self._parse_headers_in_cookies()
|
||||||
self.cookies = {}
|
|
||||||
if 'set-cookie' in self.headers:
|
|
||||||
for item in self.headers['set-cookie'].split(','):
|
|
||||||
key, value = item[:item.find(';')].split('=')
|
|
||||||
self.cookies[key.strip()] = value.strip()
|
|
||||||
|
|
||||||
# check is a new session id has been issued, symptom of broken session
|
# check is a new session id has been issued, symptom of broken session
|
||||||
if self.session_regex is not None:
|
if self.session_regex is not None:
|
||||||
|
|||||||
+2
-2
@@ -75,10 +75,10 @@ def _default_validators(db, field):
|
|||||||
if field.unique:
|
if field.unique:
|
||||||
requires.insert(0, validators.IS_NOT_IN_DB(db, field))
|
requires.insert(0, validators.IS_NOT_IN_DB(db, field))
|
||||||
excluded_fields = ['string', 'upload', 'text', 'password', 'boolean']
|
excluded_fields = ['string', 'upload', 'text', 'password', 'boolean']
|
||||||
if (field.notnull or field.unique) and not field_type in excluded_fields:
|
if (field.notnull or field.unique) and field_type not in excluded_fields:
|
||||||
requires.insert(0, validators.IS_NOT_EMPTY())
|
requires.insert(0, validators.IS_NOT_EMPTY())
|
||||||
elif not field.notnull and not field.unique and requires:
|
elif not field.notnull and not field.unique and requires:
|
||||||
requires[0] = validators.IS_EMPTY_OR(requires[0], null='' if field in ('string', 'text', 'password') else None)
|
requires[0] = validators.IS_EMPTY_OR(requires[0], null='' if field.type in ('string', 'text', 'password') else None)
|
||||||
return requires
|
return requires
|
||||||
|
|
||||||
from gluon.serializers import custom_json, xml
|
from gluon.serializers import custom_json, xml
|
||||||
|
|||||||
+16
-16
@@ -87,9 +87,9 @@ def communicate(command=None):
|
|||||||
return ''.join(result)
|
return ''.join(result)
|
||||||
|
|
||||||
|
|
||||||
# New debugger implementation using qdb and a web UI
|
# New debugger implementation using dbg and a web UI
|
||||||
|
|
||||||
import gluon.contrib.qdb as qdb
|
import gluon.contrib.dbg as dbg
|
||||||
from threading import RLock
|
from threading import RLock
|
||||||
|
|
||||||
interact_lock = RLock()
|
interact_lock = RLock()
|
||||||
@@ -109,11 +109,11 @@ def check_interaction(fn):
|
|||||||
return check_fn
|
return check_fn
|
||||||
|
|
||||||
|
|
||||||
class WebDebugger(qdb.Frontend):
|
class WebDebugger(dbg.Frontend):
|
||||||
"""Qdb web2py interface"""
|
"""Qdb web2py interface"""
|
||||||
|
|
||||||
def __init__(self, pipe, completekey='tab', stdin=None, stdout=None):
|
def __init__(self, pipe, completekey='tab', stdin=None, stdout=None):
|
||||||
qdb.Frontend.__init__(self, pipe)
|
dbg.Frontend.__init__(self, pipe)
|
||||||
self.clear_interaction()
|
self.clear_interaction()
|
||||||
|
|
||||||
def clear_interaction(self):
|
def clear_interaction(self):
|
||||||
@@ -128,7 +128,7 @@ class WebDebugger(qdb.Frontend):
|
|||||||
run_lock.acquire()
|
run_lock.acquire()
|
||||||
try:
|
try:
|
||||||
while self.pipe.poll():
|
while self.pipe.poll():
|
||||||
qdb.Frontend.run(self)
|
dbg.Frontend.run(self)
|
||||||
finally:
|
finally:
|
||||||
run_lock.release()
|
run_lock.release()
|
||||||
|
|
||||||
@@ -149,23 +149,23 @@ class WebDebugger(qdb.Frontend):
|
|||||||
|
|
||||||
@check_interaction
|
@check_interaction
|
||||||
def do_continue(self):
|
def do_continue(self):
|
||||||
qdb.Frontend.do_continue(self)
|
dbg.Frontend.do_continue(self)
|
||||||
|
|
||||||
@check_interaction
|
@check_interaction
|
||||||
def do_step(self):
|
def do_step(self):
|
||||||
qdb.Frontend.do_step(self)
|
dbg.Frontend.do_step(self)
|
||||||
|
|
||||||
@check_interaction
|
@check_interaction
|
||||||
def do_return(self):
|
def do_return(self):
|
||||||
qdb.Frontend.do_return(self)
|
dbg.Frontend.do_return(self)
|
||||||
|
|
||||||
@check_interaction
|
@check_interaction
|
||||||
def do_next(self):
|
def do_next(self):
|
||||||
qdb.Frontend.do_next(self)
|
dbg.Frontend.do_next(self)
|
||||||
|
|
||||||
@check_interaction
|
@check_interaction
|
||||||
def do_quit(self):
|
def do_quit(self):
|
||||||
qdb.Frontend.do_quit(self)
|
dbg.Frontend.do_quit(self)
|
||||||
|
|
||||||
def do_exec(self, statement):
|
def do_exec(self, statement):
|
||||||
interact_lock.acquire()
|
interact_lock.acquire()
|
||||||
@@ -175,22 +175,22 @@ class WebDebugger(qdb.Frontend):
|
|||||||
# avoid spurious interaction notifications:
|
# avoid spurious interaction notifications:
|
||||||
self.set_burst(2)
|
self.set_burst(2)
|
||||||
# execute the statement in the remote debugger:
|
# execute the statement in the remote debugger:
|
||||||
return qdb.Frontend.do_exec(self, statement)
|
return dbg.Frontend.do_exec(self, statement)
|
||||||
finally:
|
finally:
|
||||||
interact_lock.release()
|
interact_lock.release()
|
||||||
|
|
||||||
# create the connection between threads:
|
# create the connection between threads:
|
||||||
|
|
||||||
parent_queue, child_queue = Queue.Queue(), Queue.Queue()
|
parent_queue, child_queue = Queue.Queue(), Queue.Queue()
|
||||||
front_conn = qdb.QueuePipe("parent", parent_queue, child_queue)
|
front_conn = dbg.QueuePipe("parent", parent_queue, child_queue)
|
||||||
child_conn = qdb.QueuePipe("child", child_queue, parent_queue)
|
child_conn = dbg.QueuePipe("child", child_queue, parent_queue)
|
||||||
|
|
||||||
web_debugger = WebDebugger(front_conn) # frontend
|
web_debugger = WebDebugger(front_conn) # frontend
|
||||||
qdb_debugger = qdb.Qdb(pipe=child_conn, redirect_stdio=False, skip=None) # backend
|
dbg_debugger = dbg.Qdb(pipe=child_conn, redirect_stdio=False, skip=None) # backend
|
||||||
dbg = qdb_debugger
|
dbg = dbg_debugger
|
||||||
|
|
||||||
# enable getting context (stack, globals/locals) at interaction
|
# enable getting context (stack, globals/locals) at interaction
|
||||||
qdb_debugger.set_params(dict(call_stack=True, environment=True))
|
dbg_debugger.set_params(dict(call_stack=True, environment=True))
|
||||||
|
|
||||||
import gluon.main
|
import gluon.main
|
||||||
gluon.main.global_settings.debugging = True
|
gluon.main.global_settings.debugging = True
|
||||||
|
|||||||
+5
-1
@@ -390,7 +390,11 @@ def wsgibase(environ, responder):
|
|||||||
% 'invalid request',
|
% 'invalid request',
|
||||||
web2py_error='invalid application')
|
web2py_error='invalid application')
|
||||||
elif not request.is_local and exists(disabled):
|
elif not request.is_local and exists(disabled):
|
||||||
raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>")
|
five0three = os.path.join(request.folder,'static','503.html')
|
||||||
|
if os.path.exists(five0three):
|
||||||
|
raise HTTP(503, file(five0three, 'r').read())
|
||||||
|
else:
|
||||||
|
raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>")
|
||||||
|
|
||||||
# ##################################################
|
# ##################################################
|
||||||
# build missing folders
|
# build missing folders
|
||||||
|
|||||||
+3
-2
@@ -11,7 +11,8 @@ import errno
|
|||||||
import socket
|
import socket
|
||||||
import logging
|
import logging
|
||||||
import platform
|
import platform
|
||||||
from gluon._compat import iteritems, to_bytes, StringIO, urllib_unquote
|
from gluon._compat import iteritems, to_bytes, StringIO
|
||||||
|
from gluon._compat import urllib_unquote, to_native
|
||||||
|
|
||||||
# Define Constants
|
# Define Constants
|
||||||
VERSION = '1.2.6'
|
VERSION = '1.2.6'
|
||||||
@@ -1661,7 +1662,7 @@ class WSGIWorker(Worker):
|
|||||||
try:
|
try:
|
||||||
peercert = conn.socket.getpeercert(binary_form=True)
|
peercert = conn.socket.getpeercert(binary_form=True)
|
||||||
environ['SSL_CLIENT_RAW_CERT'] = \
|
environ['SSL_CLIENT_RAW_CERT'] = \
|
||||||
peercert and ssl.DER_cert_to_PEM_cert(peercert)
|
peercert and to_native(ssl.DER_cert_to_PEM_cert(peercert))
|
||||||
except Exception:
|
except Exception:
|
||||||
print(sys.exc_info()[1])
|
print(sys.exc_info()[1])
|
||||||
else:
|
else:
|
||||||
|
|||||||
+1
-1
@@ -254,7 +254,7 @@ def run(
|
|||||||
die(errmsg)
|
die(errmsg)
|
||||||
|
|
||||||
if f:
|
if f:
|
||||||
exec('print %s()' % f, _env)
|
exec('print( %s())' % f, _env)
|
||||||
return
|
return
|
||||||
|
|
||||||
_env.update(exec_pythonrc())
|
_env.update(exec_pythonrc())
|
||||||
|
|||||||
@@ -601,7 +601,8 @@ class testForSchedulerRunnerBase(BaseTestScheduler):
|
|||||||
fdest = os.path.join(current.request.folder, 'models', 'scheduler.py')
|
fdest = os.path.join(current.request.folder, 'models', 'scheduler.py')
|
||||||
os.unlink(fdest)
|
os.unlink(fdest)
|
||||||
additional_files = [
|
additional_files = [
|
||||||
os.path.join(current.request.folder, 'private', 'demo8.pholder')
|
os.path.join(current.request.folder, 'private', 'demo8.pholder'),
|
||||||
|
os.path.join(current.request.folder, 'views', 'issue_1485_2.html'),
|
||||||
]
|
]
|
||||||
for f in additional_files:
|
for f in additional_files:
|
||||||
try:
|
try:
|
||||||
@@ -609,6 +610,12 @@ class testForSchedulerRunnerBase(BaseTestScheduler):
|
|||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def writeview(self, content, dest=None):
|
||||||
|
from gluon import current
|
||||||
|
fdest = os.path.join(current.request.folder, 'views', dest)
|
||||||
|
with open(fdest, 'w') as q:
|
||||||
|
q.write(content)
|
||||||
|
|
||||||
def writefunction(self, content, initlines=None):
|
def writefunction(self, content, initlines=None):
|
||||||
from gluon import current
|
from gluon import current
|
||||||
fdest = os.path.join(current.request.folder, 'models', 'scheduler.py')
|
fdest = os.path.join(current.request.folder, 'models', 'scheduler.py')
|
||||||
@@ -620,6 +627,9 @@ from gluon.scheduler import Scheduler
|
|||||||
db_dal = os.path.abspath(os.path.join(request.folder, '..', '..', 'dummy2.db'))
|
db_dal = os.path.abspath(os.path.join(request.folder, '..', '..', 'dummy2.db'))
|
||||||
sched_dal = DAL('sqlite://%s' % db_dal, folder=os.path.dirname(db_dal))
|
sched_dal = DAL('sqlite://%s' % db_dal, folder=os.path.dirname(db_dal))
|
||||||
sched = Scheduler(sched_dal, max_empty_runs=15, migrate=False, heartbeat=1)
|
sched = Scheduler(sched_dal, max_empty_runs=15, migrate=False, heartbeat=1)
|
||||||
|
def termination():
|
||||||
|
sched.terminate()
|
||||||
|
sched_dal.commit()
|
||||||
"""
|
"""
|
||||||
with open(fdest, 'w') as q:
|
with open(fdest, 'w') as q:
|
||||||
q.write(initlines)
|
q.write(initlines)
|
||||||
@@ -699,10 +709,11 @@ def demo4():
|
|||||||
timeout1 = s.queue_task('demo4', timeout=5)
|
timeout1 = s.queue_task('demo4', timeout=5)
|
||||||
timeout2 = s.queue_task('demo4')
|
timeout2 = s.queue_task('demo4')
|
||||||
progress = s.queue_task('demo6', sync_output=2)
|
progress = s.queue_task('demo6', sync_output=2)
|
||||||
|
termination = s.queue_task('termination')
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
self.writefunction(r"""
|
self.writefunction(r"""
|
||||||
def demo3():
|
def demo3():
|
||||||
time.sleep(15)
|
time.sleep(3)
|
||||||
print(1/0)
|
print(1/0)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@@ -712,7 +723,7 @@ def demo4():
|
|||||||
return dict(a=1, b=2)
|
return dict(a=1, b=2)
|
||||||
|
|
||||||
def demo5():
|
def demo5():
|
||||||
time.sleep(15)
|
time.sleep(3)
|
||||||
print("I'm printing something")
|
print("I'm printing something")
|
||||||
rtn = dict(a=1, b=2)
|
rtn = dict(a=1, b=2)
|
||||||
|
|
||||||
@@ -758,6 +769,7 @@ def demo6():
|
|||||||
immediate = s.queue_task('demo1', ['a', 'b'], dict(c=1, d=2), immediate=True)
|
immediate = s.queue_task('demo1', ['a', 'b'], dict(c=1, d=2), immediate=True)
|
||||||
env = s.queue_task('demo7')
|
env = s.queue_task('demo7')
|
||||||
drift = s.queue_task('demo1', ['a', 'b'], dict(c=1, d=2), period=93, prevent_drift=True)
|
drift = s.queue_task('demo1', ['a', 'b'], dict(c=1, d=2), period=93, prevent_drift=True)
|
||||||
|
termination = s.queue_task('termination')
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
self.writefunction(r"""
|
self.writefunction(r"""
|
||||||
def demo1(*args,**vars):
|
def demo1(*args,**vars):
|
||||||
@@ -844,27 +856,41 @@ def demo8():
|
|||||||
]
|
]
|
||||||
self.exec_asserts(res, 'FAILED_CONSECUTIVE')
|
self.exec_asserts(res, 'FAILED_CONSECUTIVE')
|
||||||
|
|
||||||
def testHugeResult(self):
|
def testRegressions(self):
|
||||||
s = Scheduler(self.db)
|
s = Scheduler(self.db)
|
||||||
huge_result = s.queue_task('demo10', retry_failed=1, period=1)
|
huge_result = s.queue_task('demo10', retry_failed=1, period=1)
|
||||||
|
issue_1485 = s.queue_task('issue_1485')
|
||||||
|
termination = s.queue_task('termination')
|
||||||
self.db.commit()
|
self.db.commit()
|
||||||
self.writefunction(r"""
|
self.writefunction(r"""
|
||||||
def demo10():
|
def demo10():
|
||||||
res = 'a' * 99999
|
res = 'a' * 99999
|
||||||
return dict(res=res)
|
return dict(res=res)
|
||||||
|
|
||||||
|
def issue_1485():
|
||||||
|
return response.render('issue_1485.html', dict(variable='abc'))
|
||||||
""")
|
""")
|
||||||
|
self.writeview(r"""<span>{{=variable}}</span>""", 'issue_1485.html')
|
||||||
ret = self.exec_sched()
|
ret = self.exec_sched()
|
||||||
# process finished just fine
|
# process finished just fine
|
||||||
self.assertEqual(ret, 0)
|
self.assertEqual(ret, 0)
|
||||||
# huge_result - checks
|
# huge_result - checks
|
||||||
task = s.task_status(huge_result.id, output=True)
|
task_huge = s.task_status(huge_result.id, output=True)
|
||||||
res = [
|
res = [
|
||||||
("task status completed", task.scheduler_task.status == 'COMPLETED'),
|
("task status completed", task_huge.scheduler_task.status == 'COMPLETED'),
|
||||||
("task times_run is 1", task.scheduler_task.times_run == 1),
|
("task times_run is 1", task_huge.scheduler_task.times_run == 1),
|
||||||
("result is the correct one", task.result == dict(res='a' * 99999))
|
("result is the correct one", task_huge.result == dict(res='a' * 99999))
|
||||||
]
|
]
|
||||||
self.exec_asserts(res, 'HUGE_RESULT')
|
self.exec_asserts(res, 'HUGE_RESULT')
|
||||||
|
|
||||||
|
task_issue_1485 = s.task_status(issue_1485.id, output=True)
|
||||||
|
res = [
|
||||||
|
("task status completed", task_issue_1485.scheduler_task.status == 'COMPLETED'),
|
||||||
|
("task times_run is 1", task_issue_1485.scheduler_task.times_run == 1),
|
||||||
|
("result is the correct one", task_issue_1485.result == '<span>abc</span>')
|
||||||
|
]
|
||||||
|
self.exec_asserts(res, 'issue_1485')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
+62
-25
@@ -525,28 +525,47 @@ class TestAuth(unittest.TestCase):
|
|||||||
self.auth.define_tables(username=True, signature=False)
|
self.auth.define_tables(username=True, signature=False)
|
||||||
self.db.define_table('t0', Field('tt'), self.auth.signature)
|
self.db.define_table('t0', Field('tt'), self.auth.signature)
|
||||||
self.auth.enable_record_versioning(self.db)
|
self.auth.enable_record_versioning(self.db)
|
||||||
|
self.auth.settings.registration_requires_verification = False
|
||||||
|
self.auth.settings.registration_requires_approval = False
|
||||||
# Create a user
|
# Create a user
|
||||||
self.auth.get_or_create_user(dict(first_name='Bart',
|
# Note: get_or_create_user() doesn't seems to create user properly it better to use register_bare() and
|
||||||
last_name='Simpson',
|
# prevent login_bare() test from succeed. db insert the user manually not properly work either.
|
||||||
username='bart',
|
# Not working
|
||||||
email='bart@simpson.com',
|
# self.auth.get_or_create_user(dict(first_name='Bart',
|
||||||
password='bart_password',
|
# last_name='Simpson',
|
||||||
registration_key='bart',
|
# username='bart',
|
||||||
registration_id=''
|
# email='bart@simpson.com',
|
||||||
),
|
# password='bart_password',
|
||||||
login=False)
|
# # registration_key=None,
|
||||||
self.db.commit()
|
# #registration_id='bart@simpson.com'
|
||||||
self.assertFalse(self.auth.is_logged_in())
|
# ),
|
||||||
# self.auth.settings.registration_requires_verification = False
|
# login=False)
|
||||||
# self.auth.settings.registration_requires_approval = False
|
# Not working
|
||||||
|
# self.db.auth_user.insert(first_name='Bart',
|
||||||
|
# last_name='Simpson',
|
||||||
|
# username='bart',
|
||||||
|
# email='bart@simpson.com',
|
||||||
|
# password='bart_password')
|
||||||
|
# self.db.commit()
|
||||||
|
self.auth.register_bare(first_name='Bart',
|
||||||
|
last_name='Simpson',
|
||||||
|
username='bart',
|
||||||
|
email='bart@simpson.com',
|
||||||
|
password='bart_password')
|
||||||
|
|
||||||
def test_assert_setup(self):
|
def test_assert_setup(self):
|
||||||
self.assertEqual(self.db(self.db.auth_user.username == 'bart').select().first()['username'], 'bart')
|
|
||||||
self.assertTrue('auth_user' in self.db)
|
self.assertTrue('auth_user' in self.db)
|
||||||
self.assertTrue('auth_group' in self.db)
|
self.assertTrue('auth_group' in self.db)
|
||||||
self.assertTrue('auth_membership' in self.db)
|
self.assertTrue('auth_membership' in self.db)
|
||||||
self.assertTrue('auth_permission' in self.db)
|
self.assertTrue('auth_permission' in self.db)
|
||||||
self.assertTrue('auth_event' in self.db)
|
self.assertTrue('auth_event' in self.db)
|
||||||
|
bart_record = self.db(self.db.auth_user.username == 'bart').select().first()
|
||||||
|
self.assertEqual(bart_record['username'], 'bart')
|
||||||
|
self.assertEqual(bart_record['registration_key'], '')
|
||||||
|
bart_id = self.db(self.db.auth_user.username == 'bart').select().first().id
|
||||||
|
bart_group_id = self.db(self.db.auth_group.role == 'user_{0}'.format(bart_id)).select().first().id
|
||||||
|
self.assertTrue(self.db((self.db.auth_membership.group_id == bart_group_id) &
|
||||||
|
(self.db.auth_membership.user_id == bart_id)).select().first())
|
||||||
|
|
||||||
# Just calling many form functions
|
# Just calling many form functions
|
||||||
def test_basic_blank_forms(self):
|
def test_basic_blank_forms(self):
|
||||||
@@ -652,16 +671,31 @@ class TestAuth(unittest.TestCase):
|
|||||||
# TODO: def test_login_user(self):
|
# TODO: def test_login_user(self):
|
||||||
# TODO: def test__get_login_settings(self):
|
# TODO: def test__get_login_settings(self):
|
||||||
|
|
||||||
# login_bare() seems broken see my post on web2py-developpers
|
def test_login_bare(self):
|
||||||
# commented for now
|
self.auth.login_bare(username='bart', password='bart_password')
|
||||||
# def test_login_bare(self):
|
self.assertTrue(self.auth.is_logged_in())
|
||||||
# # The following test case should succeed but failed as I never received the user record but False
|
self.auth.logout_bare()
|
||||||
# self.auth.login_bare(username='bart', password='bart_password')
|
# Failing login because wrong_password
|
||||||
# self.assertTrue(self.auth.is_logged_in())
|
self.assertFalse(self.auth.login_bare(username='bart', password='wrong_password'))
|
||||||
# # Failing login because bad_password
|
# NOTE : The following failed for some reason, but I can't find out why
|
||||||
# self.assertEqual(self.auth.login_bare(username='bart', password='wrong_password'), False)
|
# self.auth = Auth(self.db)
|
||||||
# self.auth.logout_bare()
|
# self.auth.define_tables(username=False, signature=False)
|
||||||
# self.db.auth_user.truncate()
|
# self.auth.settings.registration_requires_verification = False
|
||||||
|
# self.auth.settings.registration_requires_approval = False
|
||||||
|
# self.auth.register_bare(first_name='Omer',
|
||||||
|
# last_name='Simpson',
|
||||||
|
# # no username field passed, failed with :
|
||||||
|
# # ValueError('register_bare: userfield not provided or invalid')
|
||||||
|
# # Or
|
||||||
|
# # username='omer',
|
||||||
|
# # Or
|
||||||
|
# # username='omer@simpson.com',
|
||||||
|
# # In either previous cases, it failed with :
|
||||||
|
# # self.assertTrue(self.auth.is_logged_in()) AssertionError: False is not true
|
||||||
|
# email='omer@simpson.com',
|
||||||
|
# password='omer_password')
|
||||||
|
# self.auth.login_bare(username='omer@sympson.com', password='omer_password')
|
||||||
|
# self.assertTrue(self.auth.is_logged_in())
|
||||||
|
|
||||||
def test_register_bare(self):
|
def test_register_bare(self):
|
||||||
# corner case empty register call register_bare without args
|
# corner case empty register call register_bare without args
|
||||||
@@ -817,7 +851,10 @@ class TestAuth(unittest.TestCase):
|
|||||||
self.myassertRaisesRegex(HTTP, "400*", self.auth.allows_jwt)
|
self.myassertRaisesRegex(HTTP, "400*", self.auth.allows_jwt)
|
||||||
|
|
||||||
# TODO: def test_requires(self):
|
# TODO: def test_requires(self):
|
||||||
# TODO: def test_requires_login(self):
|
|
||||||
|
# def test_login(self):
|
||||||
|
# Basic testing above in "test_basic_blank_forms()" could be refined here
|
||||||
|
|
||||||
# TODO: def test_requires_login_or_token(self):
|
# TODO: def test_requires_login_or_token(self):
|
||||||
# TODO: def test_requires_membership(self):
|
# TODO: def test_requires_membership(self):
|
||||||
# TODO: def test_requires_permission(self):
|
# TODO: def test_requires_permission(self):
|
||||||
|
|||||||
@@ -49,6 +49,24 @@ def stopwebserver():
|
|||||||
webserverprocess.terminate()
|
webserverprocess.terminate()
|
||||||
|
|
||||||
|
|
||||||
|
class Cookie(unittest.TestCase):
|
||||||
|
def testParseMultipleEquals(self):
|
||||||
|
""" Test for issue #1500.
|
||||||
|
Ensure that a cookie containing one or more '=' is correctly parsed
|
||||||
|
"""
|
||||||
|
client = WebClient()
|
||||||
|
client.headers['set-cookie'] = "key = value with one =;"
|
||||||
|
client._parse_headers_in_cookies()
|
||||||
|
self.assertIn("key", client.cookies)
|
||||||
|
self.assertEqual(client.cookies['key'], "value with one =")
|
||||||
|
|
||||||
|
client.headers['set-cookie'] = "key = value with one = and another one =;"
|
||||||
|
client._parse_headers_in_cookies()
|
||||||
|
client._parse_headers_in_cookies()
|
||||||
|
self.assertIn("key", client.cookies)
|
||||||
|
self.assertEqual(client.cookies['key'], "value with one = and another one =")
|
||||||
|
|
||||||
|
|
||||||
class LiveTest(unittest.TestCase):
|
class LiveTest(unittest.TestCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|||||||
Reference in New Issue
Block a user