Merge branch 'master' into pydal-pip

Conflicts:
	gluon/__init__.py
	gluon/dal/_load.py
	gluon/dal/adapters/base.py
	gluon/dal/adapters/google.py
	gluon/dal/adapters/postgres.py
	gluon/dal/adapters/teradata.py
	gluon/dal/base.py
	gluon/dal/objects.py
	gluon/tests/__init__.py
	gluon/tests/test_dal_nosql.py
This commit is contained in:
gi0baro
2015-01-17 13:32:06 +01:00
55 changed files with 2715 additions and 1907 deletions
+693 -660
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -127,7 +127,7 @@ class mybuiltin(object):
def LOAD(c=None, f='index', args=None, vars=None,
extension=None, target=None, ajax=False, ajax_trap=False,
url=None, user_signature=False, timeout=None, times=1,
content='loading...', **attr):
content='loading...', post_vars=Storage(), **attr):
""" LOADs a component into the action's document
Args:
@@ -202,7 +202,7 @@ def LOAD(c=None, f='index', args=None, vars=None,
other_request.args = List(args)
other_request.vars = vars
other_request.get_vars = vars
other_request.post_vars = Storage()
other_request.post_vars = post_vars
other_response = Response()
other_request.env.path_info = '/' + \
'/'.join([request.application, c, f] +
+123
View File
@@ -0,0 +1,123 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Read from configuration files easily without hurting performances
USAGE:
During development you can load a config file either in .ini or .json
format (by default app/private/appconfig.ini or app/private/appconfig.json)
The result is a dict holding the configured values. Passing reload=True
is meant only for development: in production, leave reload to False and all
values will be cached
from gluon.contrib.appconfig import AppConfig
myconfig = AppConfig(path_to_configfile, reload=False)
print myconfig['db']['uri']
The returned dict can walk with "dot notation" an arbitrarely nested dict
print myconfig.take('db.uri')
You can even pass a cast function, i.e.
print myconfig.take('auth.expiration', cast=int)
Once the value has been fetched (and casted) it won't change until the process
is restarted (or reload=True is passed).
"""
import thread
import os
from ConfigParser import SafeConfigParser
from gluon import current
from gluon.serializers import json_parser
locker = thread.allocate_lock()
def AppConfig(*args, **vars):
locker.acquire()
reload_ = vars.pop('reload', False)
try:
instance_name = 'AppConfig_' + current.request.application
if reload_ or not hasattr(AppConfig, instance_name):
setattr(AppConfig, instance_name, AppConfigLoader(*args, **vars))
return getattr(AppConfig, instance_name).settings
finally:
locker.release()
class AppConfigDict(dict):
"""
dict that has a .take() method to fetch nested values and puts
them into cache
"""
def __init__(self, *args, **kwargs):
dict.__init__(self, *args, **kwargs)
self.int_cache = {}
def take(self, path, cast=None):
parts = path.split('.')
if path in self.int_cache:
return self.int_cache[path]
value = self
walking = []
for part in parts:
if part not in value:
raise BaseException("%s not in config [%s]" %
(part, '-->'.join(walking)))
value = value[part]
walking.append(part)
if cast is None:
self.int_cache[path] = value
else:
try:
value = cast(value)
self.int_cache[path] = value
except (ValueError, TypeError):
raise BaseException("%s can't be converted to %s" %
(value, cast))
return value
class AppConfigLoader(object):
def __init__(self, configfile=None):
if not configfile:
priv_folder = os.path.join(current.request.folder, 'private')
configfile = os.path.join(priv_folder, 'appconfig.ini')
if not os.path.isfile(configfile):
configfile = os.path.join(priv_folder, 'appconfig.json')
if not os.path.isfile(configfile):
configfile = None
if not configfile or not os.path.isfile(configfile):
raise BaseException("Config file not found")
self.file = configfile
self.ctype = os.path.splitext(configfile)[1][1:]
self.settings = None
self.read_config()
def read_config_ini(self):
config = SafeConfigParser()
config.read(self.file)
settings = {}
for section in config.sections():
settings[section] = {}
for option in config.options(section):
settings[section][option] = config.get(section, option)
self.settings = AppConfigDict(settings)
def read_config_json(self):
with open(self.file, 'r') as c:
self.settings = AppConfigDict(json_parser.load(c))
def read_config(self):
if self.settings is None:
try:
getattr(self, 'read_config_' + self.ctype)()
except AttributeError:
raise BaseException("Unsupported config file format")
return self.settings
+10 -11
View File
@@ -47,7 +47,7 @@ class Collection(object):
if self.compact:
for fieldname in (self.table_policy.get('fields',table.fields)):
field = table[fieldname]
if not ((field.type=='text' and text==False) or
if not ((field.type=='text' and text==False) or
field.type=='blob' or
field.type.startswith('reference ') or
field.type.startswith('list:reference ')) and field.name in row:
@@ -56,7 +56,7 @@ class Collection(object):
for fieldname in (self.table_policy.get('fields',table.fields)):
field = table[fieldname]
if not ((field.type=='text' and text==False) or
field.type=='blob' or
field.type=='blob' or
field.type.startswith('reference ') or
field.type.startswith('list:reference ')) and field.name in row:
data.append({'name':field.name,'value':row[field.name],
@@ -128,10 +128,10 @@ class Collection(object):
for key,value in vars.items():
if key=='_offset':
limitby[0] = int(value) # MAY FAIL
elif key == '_limit':
elif key == '_limit':
limitby[1] = int(value)+1 # MAY FAIL
elif key=='_orderby':
orderby = value
orderby = value
elif key in fieldnames:
queries.append(table[key] == value)
elif key.endswith('.eq') and key[:-3] in fieldnames: # for completeness (useless)
@@ -156,14 +156,14 @@ class Collection(object):
if filter_query:
queries.append(filter_query)
query = reduce(lambda a,b:a&b,queries[1:]) if len(queries)>1 else queries[0]
orderby = [table[f] if f[0]!='~' else ~table[f[1:]] for f in orderby.split(',')]
orderby = [table[f] if f[0]!='~' else ~table[f[1:]] for f in orderby.split(',')]
return (query, limitby, orderby)
def table2queries(self,table, href):
""" generates a set of collection.queries examples for the table """
data = []
for fieldname in (self.table_policy.get('fields', table.fields)):
data.append({'name':fieldname,'value':''})
data.append({'name':fieldname,'value':''})
if self.extensions:
data.append({'name':fieldname+'.ne','value':''}) # NEW !!!
data.append({'name':fieldname+'.lt','value':''})
@@ -192,7 +192,7 @@ class Collection(object):
if not tablename:
r['href'] = URL(scheme=True),
# https://github.com/collection-json/extensions/blob/master/model.md
r['links'] = [{'rel' : t, 'href' : URL(args=t,scheme=True), 'model':t}
r['links'] = [{'rel' : t, 'href' : URL(args=t,scheme=True), 'model':t}
for t in tablenames]
response.headers['Content-Type'] = 'application/vnd.collection+json'
return response.json({'collection':r})
@@ -207,7 +207,7 @@ class Collection(object):
# process GET
if request.env.request_method=='GET':
table = db[tablename]
r['href'] = URL(args=tablename)
r['href'] = URL(args=tablename)
r['items'] = items = []
try:
(query, limitby, orderby) = self.request2query(table,request.get_vars)
@@ -258,7 +258,7 @@ class Collection(object):
return response.json({'collection':r})
# process DELETE
elif request.env.request_method=='DELETE':
table = db[tablename]
table = db[tablename]
if not request.get_vars:
return self.error(400, "BAD REQUEST", "Nothing to delete")
else:
@@ -312,7 +312,7 @@ class Collection(object):
request, response = self.request, self.response
r = OrderedDict({
"version" : self.VERSION,
"href" : URL(args=request.args,vars=request.vars),
"href" : URL(args=request.args,vars=request.vars),
"error" : {
"title" : title,
"code" : code,
@@ -340,4 +340,3 @@ example_policies = {
'DELETE':{'query':None},
},
}
@@ -73,7 +73,7 @@ class BrowserID(object):
auth_info_json = fetch(self.verify_url, data=verify_data)
j = json.loads(auth_info_json)
epoch_time = int(time.time() * 1000) # we need 13 digit epoch time
if j["status"] == "okay" and j["audience"] == audience and j['issuer'] == issuer and j['expires'] >= epoch_time:
if j["status"] == "okay" and j["audience"] == audience and j['issuer'].endswith(issuer) and j['expires'] >= epoch_time:
return dict(email=j['email'])
elif self.on_login_failure:
#print "status: ", j["status"]=="okay", j["status"]
+6 -6
View File
@@ -254,12 +254,12 @@ class Table(DALStorage):
self._db(self.id > 0).delete()
def insert(self, **fields):
# Checks 3 times that the id is new. 3 times is enough!
for i in range(3):
id = self._create_id()
if self.get(id) is None and self.update(id, **fields):
return long(id)
def insert(self, **fields):
# Checks 3 times that the id is new. 3 times is enough!
for i in range(3):
id = self._create_id()
if self.get(id) is None and self.update(id, **fields):
return long(id)
else:
raise RuntimeError("Too many ID conflicts")
+3 -4
View File
@@ -141,7 +141,7 @@ class Connection(object):
parts = "complete"
return ("OK", (("%s " % message_id, message[parts]), message["flags"]))
def _get_messages(self, query):
if query.strip().isdigit():
return [self.spam[self._mailbox][int(query.strip()) - 1],]
@@ -151,7 +151,7 @@ class Connection(object):
for item in self.spam[self._mailbox]:
if item["uid"] == query[1:-1].replace("UID", "").strip():
return [item,]
messages = []
messages = []
try:
for m in self.results[self._mailbox][query]:
try:
@@ -169,7 +169,7 @@ class Connection(object):
return messages
except KeyError:
raise ValueError("The client issued an unexpected query: %s" % query)
def setup(self, spam={}, results={}):
"""adds custom message and query databases or sets
the values to the module defaults.
@@ -252,4 +252,3 @@ class IMAP4(object):
return Connection()
IMAP4_SSL = IMAP4
+4 -4
View File
@@ -11,7 +11,7 @@
Note: This module is intended as a plugin replacement of pbkdf2.py
by Armin Ronacher.
Git repository:
Git repository:
$ git clone https://github.com/michele-comitini/pbkdf2_ctypes.git
:copyright: Copyright (c) 2013: Michele Comitini <mcm@glisco.it>
@@ -86,7 +86,7 @@ def _openssl_hashlib_to_crypto_map_get(hashfunc):
crypto_hashfunc.restype = ctypes.c_void_p
return crypto_hashfunc()
def _openssl_pbkdf2(data, salt, iterations, digest, keylen):
"""OpenSSL compatibile wrapper
"""
@@ -99,7 +99,7 @@ def _openssl_pbkdf2(data, salt, iterations, digest, keylen):
c_iter = ctypes.c_int(iterations)
c_keylen = ctypes.c_int(keylen)
c_buff = ctypes.create_string_buffer(keylen)
# PKCS5_PBKDF2_HMAC(const char *pass, int passlen,
# const unsigned char *salt, int saltlen, int iter,
# const EVP_MD *digest,
@@ -109,7 +109,7 @@ def _openssl_pbkdf2(data, salt, iterations, digest, keylen):
ctypes.c_char_p, ctypes.c_int,
ctypes.c_int, ctypes.c_void_p,
ctypes.c_int, ctypes.c_char_p]
crypto.PKCS5_PBKDF2_HMAC.restype = ctypes.c_int
err = crypto.PKCS5_PBKDF2_HMAC(c_pass, c_passlen,
c_salt, c_saltlen,
+408 -408
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -235,7 +235,7 @@ class SoapDispatcher(object):
body.marshall("%s:Fault" % soap_ns, fault, ns=False)
else:
# return normal value
res = body.add_child("%sResponse" % name, ns=prefix)
res = body.add_child("%sResponse" % name, ns=self.namespace)
if not prefix:
res['xmlns'] = self.namespace # add target namespace
+22 -22
View File
@@ -37,7 +37,7 @@ def pay():
elif form.errors:
redirect(URL('pay_error'))
return dict(form=form)
"""
URL_CHARGE = 'https://%s:@api.stripe.com/v1/charges'
@@ -114,7 +114,7 @@ class StripeForm(object):
def process(self):
from gluon import current
request = current.request
request = current.request
if request.post_vars:
if self.signature == request.post_vars.signature:
self.response = Stripe(self.sk).charge(
@@ -127,7 +127,7 @@ class StripeForm(object):
return self
self.errors = True
return self
def xml(self):
from gluon.template import render
if self.accepted:
@@ -135,8 +135,8 @@ class StripeForm(object):
elif self.errors:
return "There was an processing error"
else:
context = dict(amount=self.amount,
signature=self.signature, pk=self.pk,
context = dict(amount=self.amount,
signature=self.signature, pk=self.pk,
currency_symbol=self.currency_symbol,
security_notice=self.security_notice,
disclosure_notice=self.disclosure_notice)
@@ -145,14 +145,14 @@ class StripeForm(object):
TEMPLATE = """
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
<script>
<script>
jQuery(function(){
// This identifies your website in the createToken call below
Stripe.setPublishableKey('{{=pk}}');
var stripeResponseHandler = function(status, response) {
var jQueryform = jQuery('#payment-form');
if (response.error) {
// Show the errors on the form
jQuery('.payment-errors').text(response.error.message).show();
@@ -167,17 +167,17 @@ jQuery(function(){
jQueryform.get(0).submit();
}
};
jQuery(function(jQuery) {
jQuery('#payment-form').submit(function(e) {
var jQueryform = jQuery(this);
// Disable the submit button to prevent repeated clicks
jQueryform.find('button').prop('disabled', true);
Stripe.createToken(jQueryform, stripeResponseHandler);
// Prevent the form from submitting with the default action
return false;
});
@@ -189,33 +189,33 @@ jQuery(function(){
<form action="" method="POST" id="payment-form" class="form-horizontal">
<div class="form-row control-group">
<label class="control-label">Card Number</label>
<label class="control-label">Card Number</label>
<div class="controls">
<input type="text" size="20" data-stripe="number"
placeholder="4242424242424242"/>
placeholder="4242424242424242"/>
</div>
</div>
<div class="form-row control-group">
<label class="control-label">CVC</label>
<label class="control-label">CVC</label>
<div class="controls">
<input type="text" size="4" style="width:80px" data-stripe="cvc"
placeholder="XXX"/>
placeholder="XXX"/>
<a href="http://en.wikipedia.org/wiki/Card_Verification_Code" target="_blank">What is this?</a>
</div>
</div>
<div class="form-row control-group">
<label class="control-label">Expiration</label>
<label class="control-label">Expiration</label>
<div class="controls">
<input type="text" size="2" style="width:40px" data-stripe="exp-month"
placeholder="MM"/>
placeholder="MM"/>
/
<input type="text" size="4" style="width:80px" data-stripe="exp-year"
placeholder="YYYY"/>
placeholder="YYYY"/>
</div>
</div>
<div class="control-group">
<div class="controls">
-1
View File
@@ -1,4 +1,3 @@
"""
Developed by Massimo Di Pierro
Released under the web2py license (LGPL)
+2 -2
View File
@@ -74,9 +74,9 @@ https is possible too using 'https://127.0.0.1:8888' instead of 'http://127.0.0.
be started with
python gluon/contrib/websocket_messaging.py -k mykey -p 8888 -s keyfile.pem -c certfile.pem
for secure websocket do:
web2py_websocket('wss://127.0.0.1:8888/realtime/mygroup',callback)
Acknowledgements:
+1 -1
View File
@@ -41,7 +41,7 @@ class CustomImportException(ImportError):
def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
"""
web2py's custom importer. It behaves like the standard Python importer but
web2py's custom importer. It behaves like the standard Python importer but
it tries to transform import statements as something like
"import applications.app_name.modules.x".
If the import fails, it falls back on naive_importer
+4 -4
View File
@@ -94,7 +94,7 @@ def parse_version(version):
return version_tuple
def read_file(filename, mode='r'):
"""Returns content from filename, making sure to close the file explicitly
"""Returns content from filename, making sure to close the file explicitly
on exit.
"""
f = open(filename, mode)
@@ -105,7 +105,7 @@ def read_file(filename, mode='r'):
def write_file(filename, value, mode='w'):
"""Writes <value> to filename, making sure to close the file
"""Writes <value> to filename, making sure to close the file
explicitly on exit.
"""
f = open(filename, mode)
@@ -346,7 +346,7 @@ def get_session(request, other_application='admin'):
session_filename = os.path.join(
up(request.folder), other_application, 'sessions', session_id)
if not os.path.exists(session_filename):
session_filename = generate(session_filename)
session_filename = generate(session_filename)
osession = storage.load_storage(session_filename)
except Exception, e:
osession = storage.Storage()
@@ -438,7 +438,7 @@ from settings import global_settings # we need to import settings here because
def abspath(*relpath, **base):
"""Converts relative path to absolute path based (by default) on
"""Converts relative path to absolute path based (by default) on
applications_parent
"""
path = os.path.join(*relpath)
+3 -1
View File
@@ -550,7 +550,7 @@ class Response(Storage):
else:
attname = filename
headers["Content-Disposition"] = \
"attachment;filename=%s" % attname
'attachment;filename="%s"' % attname
if not request:
request = current.request
@@ -630,6 +630,8 @@ class Response(Storage):
return self.stream(stream, chunk_size=chunk_size, request=request)
def json(self, data, default=None):
if 'Content-Type' not in self.headers:
self.headers['Content-Type'] = 'application/json'
return json(data, default=default or custom_json)
def xmlrpc(self, request, methods):
+2 -2
View File
@@ -850,7 +850,7 @@ class DIV(XmlComponent):
"""
components = []
for c in self.components:
if isinstance(c, allowed_parents):
if isinstance(c, (allowed_parents,CAT)):
pass
elif wrap_lambda:
c = wrap_lambda(c)
@@ -1864,7 +1864,7 @@ class INPUT(DIV):
break
if not name in self.errors:
self.vars[name] = value
return True
return True
return False
def _postprocessing(self):
+63 -63
View File
@@ -1,63 +1,63 @@
import os, uuid
def generate(filename, depth=2, base=512):
if os.path.sep in filename:
path, filename = os.path.split(filename)
else:
path = None
dummyhash = sum(ord(c)*256**(i % 4) for i,c in enumerate(filename)) % base**depth
folders = []
for level in range(depth-1,-1,-1):
code, dummyhash = divmod(dummyhash, base**level)
folders.append("%03x" % code)
folders.append(filename)
if path:
folders.insert(0,path)
return os.path.join(*folders)
def exists(filename, path=None):
if os.path.exists(filename):
return True
if path is None:
path, filename = os.path.split(filename)
fullfilename = os.path.join(path, generate(filename))
if os.path.exists(fullfilename):
return True
return False
def remove(filename, path=None):
if os.path.exists(filename):
return os.unlink(filename)
if path is None:
path, filename = os.path.split(filename)
fullfilename = os.path.join(path, generate(filename))
if os.path.exists(fullfilename):
return os.unlink(fullfilename)
raise IOError
def open(filename, mode="r", path=None):
if not path:
path, filename = os.path.split(filename)
fullfilename = None
if not mode.startswith('w'):
fullfilename = os.path.join(path, filename)
if not os.path.exists(fullfilename):
fullfilename = None
if not fullfilename:
fullfilename = os.path.join(path, generate(filename))
if mode.startswith('w') and not os.path.exists(os.path.dirname(fullfilename)):
os.makedirs(os.path.dirname(fullfilename))
return file(fullfilename, mode)
def test():
if not os.path.exists('tests'):
os.mkdir('tests')
for k in range(20):
filename = os.path.join('tests',str(uuid.uuid4())+'.test')
open(filename, "w").write('test')
assert open(filename, "r").read()=='test'
if exists(filename):
remove(filename)
if __name__ == '__main__':
test()
import os, uuid
def generate(filename, depth=2, base=512):
if os.path.sep in filename:
path, filename = os.path.split(filename)
else:
path = None
dummyhash = sum(ord(c)*256**(i % 4) for i,c in enumerate(filename)) % base**depth
folders = []
for level in range(depth-1,-1,-1):
code, dummyhash = divmod(dummyhash, base**level)
folders.append("%03x" % code)
folders.append(filename)
if path:
folders.insert(0,path)
return os.path.join(*folders)
def exists(filename, path=None):
if os.path.exists(filename):
return True
if path is None:
path, filename = os.path.split(filename)
fullfilename = os.path.join(path, generate(filename))
if os.path.exists(fullfilename):
return True
return False
def remove(filename, path=None):
if os.path.exists(filename):
return os.unlink(filename)
if path is None:
path, filename = os.path.split(filename)
fullfilename = os.path.join(path, generate(filename))
if os.path.exists(fullfilename):
return os.unlink(fullfilename)
raise IOError
def open(filename, mode="r", path=None):
if not path:
path, filename = os.path.split(filename)
fullfilename = None
if not mode.startswith('w'):
fullfilename = os.path.join(path, filename)
if not os.path.exists(fullfilename):
fullfilename = None
if not fullfilename:
fullfilename = os.path.join(path, generate(filename))
if mode.startswith('w') and not os.path.exists(os.path.dirname(fullfilename)):
os.makedirs(os.path.dirname(fullfilename))
return file(fullfilename, mode)
def test():
if not os.path.exists('tests'):
os.mkdir('tests')
for k in range(20):
filename = os.path.join('tests',str(uuid.uuid4())+'.test')
open(filename, "w").write('test')
assert open(filename, "r").read()=='test'
if exists(filename):
remove(filename)
if __name__ == '__main__':
test()
+2 -1
View File
@@ -878,6 +878,7 @@ class MapUrlIn(object):
self.domain_application = None
self.domain_controller = None
self.domain_function = None
self.map_hyphen = base.map_hyphen
arg0 = self.harg0
if not base.exclusive_domain and base.applications and arg0 in base.applications:
self.application = arg0
@@ -1256,9 +1257,9 @@ class MapUrlOut(object):
"Builds a/c/f from components"
acf = ''
if self.map_hyphen:
self.application = self.application.replace('_', '-')
self.controller = self.controller.replace('_', '-')
if self.controller != 'static' and not self.controller.startswith('static/'):
self.application = self.application.replace('_', '-')
self.function = self.function.replace('_', '-')
if not self.omit_application:
acf += '/' + self.application
+5 -4
View File
@@ -1850,13 +1850,14 @@ class WSGIWorker(Worker):
if data:
self.write(data, sections)
if self.chunked:
# If chunked, send our final chunk length
self.conn.sendall(b('0\r\n\r\n'))
elif not self.headers_sent:
if not self.headers_sent:
# Send headers if the body was empty
self.send_headers('', sections)
if self.chunked and self.request_method != 'HEAD':
# If chunked, send our final chunk length
self.conn.sendall(b('0\r\n\r\n'))
# Don't capture exceptions here. The Worker class handles
# them appropriately.
finally:
+34 -20
View File
@@ -87,11 +87,17 @@ if 'WEB2PY_PATH' not in os.environ:
os.environ['WEB2PY_PATH'] = path
try:
from gluon.contrib.simplejson import loads, dumps
except:
# try external module
from simplejson import loads, dumps
except ImportError:
try:
# try stdlib (Python >= 2.6)
from json import loads, dumps
except:
# fallback to pure-Python module
from gluon.contrib.simplejson import loads, dumps
IDENTIFIER = "%s#%s" % (socket.gethostname(),os.getpid())
IDENTIFIER = "%s#%s" % (socket.gethostname(), os.getpid())
logger = logging.getLogger('web2py.scheduler.%s' % IDENTIFIER)
@@ -160,6 +166,7 @@ class TaskReport(object):
def __str__(self):
return '<TaskReport: %s>' % self.status
class JobGraph(object):
"""Experimental: with JobGraph you can specify
dependencies amongs tasks"""
@@ -170,7 +177,9 @@ class JobGraph(object):
def add_deps(self, task_parent, task_child):
"""Creates a dependency between task_parent and task_child"""
self.db.scheduler_task_deps.insert(task_parent=task_parent, task_child=task_child, job_name=self.job_name)
self.db.scheduler_task_deps.insert(task_parent=task_parent,
task_child=task_child,
job_name=self.job_name)
def validate(self, job_name):
"""Validates if all tasks job_name can be completed, i.e. there
@@ -195,16 +204,18 @@ class JobGraph(object):
try:
rtn = []
for k, v in nested_dict.items():
v.discard(k) # Ignore self dependencies
v.discard(k) # Ignore self dependencies
extra_items_in_deps = reduce(set.union, nested_dict.values()) - set(nested_dict.keys())
nested_dict.update(dict((item, set()) for item in extra_items_in_deps))
while True:
ordered = set(item for item,dep in nested_dict.items() if not dep)
ordered = set(item for item, dep in nested_dict.items() if not dep)
if not ordered:
break
rtn.append(ordered)
nested_dict = dict((item, (dep - ordered)) for item, dep in nested_dict.items()
if item not in ordered)
nested_dict = dict(
(item, (dep - ordered)) for item, dep in nested_dict.items()
if item not in ordered
)
assert not nested_dict, "A cyclic dependency exists amongst %r" % nested_dict
db.commit()
return rtn
@@ -212,6 +223,7 @@ class JobGraph(object):
db.rollback()
return None
def demo_function(*argv, **kwargs):
""" test function """
for i in range(argv[0]):
@@ -268,7 +280,7 @@ def executor(queue, task, out):
def write(self, data):
self.out_queue.put(data)
W2P_TASK = Storage({'id' : task.task_id, 'uuid' : task.uuid})
W2P_TASK = Storage({'id': task.task_id, 'uuid': task.uuid})
stdout = LogOutput(out)
try:
if task.app:
@@ -293,7 +305,7 @@ def executor(queue, task, out):
raise NameError(
"name '%s' not found in scheduler's environment" % f)
#Inject W2P_TASK into environment
_env.update({'W2P_TASK' : W2P_TASK})
_env.update({'W2P_TASK': W2P_TASK})
#Inject W2P_TASK into current
from gluon import current
current.W2P_TASK = W2P_TASK
@@ -357,8 +369,7 @@ class MetaScheduler(threading.Thread):
start = time.time()
while p.is_alive() and (
not task.timeout or time.time() - start < task.timeout):
while p.is_alive() and (not task.timeout or time.time() - start < task.timeout):
if tout:
try:
logger.debug(' partial output saved')
@@ -568,7 +579,7 @@ class Scheduler(MetaScheduler):
queue=0,
distribution=None,
workers=0)
) #dict holding statistics
) # dict holding statistics
from gluon import current
current._scheduler = self
@@ -740,7 +751,7 @@ class Scheduler(MetaScheduler):
contention and retries `assign_task` after 0.5 seconds
"""
logger.debug('Assigning tasks...')
db.commit() #db.commit() only for Mysql
db.commit() # db.commit() only for Mysql
x = 0
while x < 10:
try:
@@ -761,7 +772,7 @@ class Scheduler(MetaScheduler):
contention and retries `pop_task` after 0.5 seconds
"""
db = self.db
db.commit() #another nifty db.commit() only for Mysql
db.commit() # another nifty db.commit() only for Mysql
x = 0
while x < 10:
try:
@@ -1077,6 +1088,8 @@ class Scheduler(MetaScheduler):
#build workers as dict of groups
wkgroups = {}
for w in all_workers:
if w.worker_stats['status'] == 'RUNNING':
continue
group_names = w.group_names
for gname in group_names:
if gname not in wkgroups:
@@ -1090,8 +1103,9 @@ class Scheduler(MetaScheduler):
db(
(st.status.belongs((QUEUED, ASSIGNED))) &
(st.stop_time < now)
).update(status=EXPIRED)
).update(status=EXPIRED)
#calculate dependencies
deps_with_no_deps = db(
(sd.can_visit == False) &
(~sd.task_child.belongs(
@@ -1163,7 +1177,7 @@ class Scheduler(MetaScheduler):
if not task.task_name:
d['task_name'] = task.function_name
db(
(st.id==task.id) &
(st.id == task.id) &
(st.status.belongs((QUEUED, ASSIGNED)))
).update(**d)
wkgroups[gname]['workers'][myw]['c'] += 1
@@ -1207,8 +1221,8 @@ class Scheduler(MetaScheduler):
else:
for group in group_names:
workers = self.db(
(ws.group_names.contains(group)) &
(~ws.status.belongs(exclusion))
(ws.group_names.contains(group)) &
(~ws.status.belongs(exclusion))
)._select(ws.id, limitby=(0,limit))
self.db(ws.id.belongs(workers)).update(status=action)
@@ -1339,7 +1353,7 @@ class Scheduler(MetaScheduler):
**dict(orderby=orderby,
left=left,
limitby=(0, 1))
).first()
).first()
if row and output:
row.result = row.scheduler_run.run_result and \
loads(row.scheduler_run.run_result,
+2
View File
@@ -100,6 +100,8 @@ def custom_json(o):
return str(o)
elif isinstance(o, XmlComponent):
return str(o)
elif isinstance(o, set):
return list(o)
elif hasattr(o, 'as_list') and callable(o.as_list):
return o.as_list()
elif hasattr(o, 'as_dict') and callable(o.as_dict):
+15 -5
View File
@@ -1988,9 +1988,9 @@ class SQLFORM(FORM):
buttonback='icon leftarrow icon-arrow-left glyphicon glyphicon-arrow-left',
buttonexport='icon downarrow icon-download glyphicon glyphicon-download',
buttondelete='icon trash icon-trash glyphicon glyphicon-trash',
buttonedit='icon pen icon-pencil glyphicon glyphicon-arrow-pencil',
buttonedit='icon pen icon-pencil glyphicon glyphicon-pencil',
buttontable='icon rightarrow icon-arrow-right glyphicon glyphicon-arrow-right',
buttonview='icon magnifier icon-zoom-in glyphicon glyphicon-arrow-zoom-in',
buttonview='icon magnifier icon-zoom-in glyphicon glyphicon-zoom-in',
)
elif not isinstance(ui, dict):
raise RuntimeError('SQLFORM.grid ui argument must be a dictionary')
@@ -2333,8 +2333,12 @@ class SQLFORM(FORM):
#fields but not virtual fields
sfields = reduce(lambda a, b: a + b,
[[f for f in t if f.readable and not isinstance(f, Field.Virtual)] for t in tables])
dbset = dbset(SQLFORM.build_query(
sfields, keywords))
#use custom_query using searchable
if callable(searchable):
dbset = dbset(searchable(sfields, keywords))
else:
dbset = dbset(SQLFORM.build_query(
sfields, keywords))
rows = dbset.select(left=left, orderby=orderby,
cacheable=True, *selectable_columns)
except Exception, e:
@@ -2349,7 +2353,9 @@ class SQLFORM(FORM):
# expcolumns is all cols to be exported including virtual fields
rows.colnames = expcolumns
oExp = clazz(rows)
filename = '.'.join(('rows', oExp.file_ext))
export_filename = \
request.vars.get('_export_filename') or 'rows'
filename = '.'.join((export_filename, oExp.file_ext))
response.headers['Content-Type'] = oExp.content_type
response.headers['Content-Disposition'] = \
'attachment;filename=' + filename + ';'
@@ -2386,6 +2392,8 @@ class SQLFORM(FORM):
spanel_id = '%s_query_fields' % prefix
sfields_id = '%s_query_panel' % prefix
skeywords_id = '%s_keywords' % prefix
## hidden fields to presever keywords in url after the submit
hidden_fields = [INPUT(_type='hidden', _value=value, _name=key) for key, value in request.get_vars.items() if key not in ['keywords', 'page']]
search_widget = lambda sfield, url: CAT(FORM(
INPUT(_name='keywords', _value=keywords,
_id=skeywords_id,_class='form-control',
@@ -2394,7 +2402,9 @@ class SQLFORM(FORM):
INPUT(_type='submit', _value=T('Search'), _class="btn btn-default"),
INPUT(_type='submit', _value=T('Clear'), _class="btn btn-default",
_onclick="jQuery('#%s').val('');" % skeywords_id),
*hidden_fields,
_method="GET", _action=url), search_menu)
# TODO vars from the url should be removed, they are not used by the submit
form = search_widget and search_widget(sfields, url()) or ''
console.append(add)
console.append(form)
+1 -1
View File
@@ -195,7 +195,7 @@ class Messages(Settings):
def __getattr__(self, key):
value = self[key]
if isinstance(value, str):
return str(self.T(value))
return self.T(value)
return value
class FastStorage(dict):
+1 -1
View File
@@ -279,7 +279,7 @@ class TemplateParser(object):
self.context = context
# allow optional alternative delimiters
if delimiters != self.default_delimiters:
escaped_delimiters = (escape(delimiters[0]),
escape(delimiters[1]))
+1
View File
@@ -17,6 +17,7 @@ from test_utils import *
from test_contribs import *
from test_web import *
from test_dal import *
from test_tools import *
if sys.version[:3] == '2.7':
from test_old_doctests import *
+1 -1
View File
@@ -27,4 +27,4 @@ def fix_sys_path(current_path):
os.path.abspath(os.path.join(path, 'site-packages')),
os.path.abspath(os.path.join(path, 'gluon')),
'']
[add_path_first(path) for path in paths]
[add_path_first(path) for path in paths]
+8 -4
View File
@@ -37,7 +37,6 @@ class TestCache(unittest.TestCase):
def testCacheInRam(self):
# defaults to mode='http'
cache = CacheInRam()
self.assertEqual(cache('a', lambda: 1, 0), 1)
self.assertEqual(cache('a', lambda: 2, 100), 1)
@@ -66,7 +65,6 @@ class TestCache(unittest.TestCase):
def testCacheOnDisk(self):
# defaults to mode='http'
s = Storage({'application': 'admin',
'folder': 'applications/admin'})
cache = CacheOnDisk(s)
@@ -102,8 +100,14 @@ class TestCache(unittest.TestCase):
self.assertEqual(prefix('a', lambda: 2, 100), 1)
self.assertEqual(cache.ram('prefixa', lambda: 2, 100), 1)
def testRegex(self):
cache = CacheInRam()
self.assertEqual(cache('a1', lambda: 1, 0), 1)
self.assertEqual(cache('a2', lambda: 2, 100), 2)
cache.clear(regex=r'a*')
self.assertEqual(cache('a1', lambda: 2, 0), 2)
self.assertEqual(cache('a2', lambda: 3, 100), 3)
return
if __name__ == '__main__':
setUpModule() # pre-python-2.7
+35 -3
View File
@@ -4,14 +4,24 @@
""" Unit tests for contribs """
import unittest
import os
from fix_path import fix_sys_path
fix_sys_path(__file__)
from gluon.storage import Storage
import gluon.contrib.fpdf as fpdf
import gluon.contrib.pyfpdf as pyfpdf
from gluon.contrib.appconfig import AppConfig
from utils import md5_hash
import contrib.fpdf as fpdf
import contrib.pyfpdf as pyfpdf
def setUpModule():
pass
def tearDownModule():
if os.path.isfile('appconfig.json'):
os.unlink('appconfig.json')
class TestContribs(unittest.TestCase):
@@ -35,6 +45,28 @@ class TestContribs(unittest.TestCase):
self.assertTrue(fpdf.FPDF_VERSION in pdf_out, 'version string')
self.assertTrue('hello world' in pdf_out, 'sample message')
def test_appconfig(self):
"""
Test for the appconfig module
"""
from gluon import current
s = Storage({'application': 'admin',
'folder': 'applications/admin'})
current.request = s
simple_config = '{"config1" : "abc", "config2" : "bcd", "config3" : { "key1" : 1, "key2" : 2} }'
with open('appconfig.json', 'w') as g:
g.write(simple_config)
myappconfig = AppConfig('appconfig.json')
self.assertEqual(myappconfig['config1'], 'abc')
self.assertEqual(myappconfig['config2'], 'bcd')
self.assertEqual(myappconfig.take('config1'), 'abc')
self.assertEqual(myappconfig.take('config3.key1', cast=str), '1')
# once parsed, can't be casted to other types
self.assertEqual(myappconfig.take('config3.key1', cast=int), '1')
self.assertEqual(myappconfig.take('config3.key2'), 2)
current.request = {}
if __name__ == '__main__':
unittest.main()
+22 -22
View File
@@ -131,7 +131,7 @@ class TestRouter(unittest.TestCase):
'http://domain.com/abc/def'), "/init/default/abc ['def']")
self.assertEqual(filter_url(
'http://domain.com/index/a%20bc'), "/init/default/index ['a bc']")
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/to/static" % root))
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic')
try:
@@ -195,7 +195,7 @@ class TestRouter(unittest.TestCase):
norm_root('%s/applications/welcome/static/favicon.ico' % root))
self.assertEqual(filter_url('http://domain.com/static/abc'),
norm_root('%s/applications/welcome/static/abc' % root))
self.assertEqual(filter_url('http://domain.com/static/path/to/static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/static/path/to/static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/to/static" % root))
# outgoing
self.assertEqual(filter_url(
@@ -1086,13 +1086,13 @@ class TestRouter(unittest.TestCase):
norm_root("%s/applications/admin/static/file" % root))
self.assertEqual(filter_url('http://domain.com/en/static/file'),
norm_root("%s/applications/admin/static/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/en/static/file'),
self.assertEqual(filter_url('http://domain.com/examples/en/static/file'),
norm_root("%s/applications/examples/static/en/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/static/file'),
norm_root("%s/applications/examples/static/en/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/it/static/file'),
self.assertEqual(filter_url('http://domain.com/examples/it/static/file'),
norm_root("%s/applications/examples/static/it/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/it-it/static/file'),
self.assertEqual(filter_url('http://domain.com/examples/it-it/static/file'),
norm_root("%s/applications/examples/static/file" % root))
self.assertEqual(filter_url('https://domain.com/admin/ctr/fcn',
@@ -1188,19 +1188,19 @@ class TestRouter(unittest.TestCase):
norm_root("%s/applications/admin/static/file" % root))
self.assertEqual(filter_url('http://domain.com/en/static/file'),
norm_root("%s/applications/admin/static/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/en/static/file'),
self.assertEqual(filter_url('http://domain.com/examples/en/static/file'),
norm_root("%s/applications/examples/static/en/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/static/file'),
norm_root("%s/applications/examples/static/en/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/it/static/file'),
self.assertEqual(filter_url('http://domain.com/examples/it/static/file'),
norm_root("%s/applications/examples/static/it/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/it-it/static/file'),
self.assertEqual(filter_url('http://domain.com/examples/it-it/static/file'),
norm_root("%s/applications/examples/static/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/static/en/file').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/examples/static/en/file').replace('/', os.sep),
norm_root("%s/applications/examples/static/en/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/static/it/file').replace('/', os.sep),
norm_root("%s/applications/examples/static/it/file" % root))
self.assertEqual(filter_url('http://domain.com/examples/static/it-it/file').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/examples/static/it-it/file').replace('/', os.sep),
norm_root("%s/applications/examples/static/it-it/file" % root))
def test_router_get_effective(self):
@@ -1267,14 +1267,14 @@ class TestRouter(unittest.TestCase):
'''
load(rdict=dict())
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/to/static" % root))
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic')
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to--/static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to--/static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/to--/static" % root))
self.assertEqual(filter_url('http://domain.com/welcome/static/path/==to--/static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/==to--/static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/==to--/static" % root))
self.assertEqual(filter_url('http://domain.com/welcome/static/path/-+=@$%/static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/-+=@$%/static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/-+=@$%%/static" % root))
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/.static')
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/s..tatic')
@@ -1287,7 +1287,7 @@ class TestRouter(unittest.TestCase):
),
)
load(rdict=router_static)
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/to/#static" % root))
router_static = dict(
@@ -1296,23 +1296,23 @@ class TestRouter(unittest.TestCase):
),
)
load(rdict=router_static)
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/to/static" % root))
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to/st~tic')
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to--/static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to--/static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/to--/static" % root))
self.assertEqual(filter_url('http://domain.com/welcome/static/path/==to--/static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/==to--/static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/==to--/static" % root))
self.assertEqual(filter_url('http://domain.com/welcome/static/path/-+=@$%/static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/-+=@$%/static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/-+=@$%%/static" % root))
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/to//static')
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/#static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/to/#static" % root))
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/./static')
self.assertRaises(HTTP, filter_url, 'http://domain.com/welcome/static/bad/path/../static')
self.assertEqual(filter_url('http://domain.com/welcome/static/path/.../static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/.../static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/.../static" % root))
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/.static').replace('/', os.sep),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/.static').replace('/', os.sep),
norm_root("%s/applications/welcome/static/path/to/.static" % root))
def test_router_args(self):
+2 -2
View File
@@ -108,7 +108,7 @@ class TestRoutes(unittest.TestCase):
'http://domain.com/abc/def/ghi/jkl'), "/abc/def/ghi ['jkl']")
self.assertEqual(filter_url(
'http://domain.com/abc/def/ghi/j%20kl'), "/abc/def/ghi ['j_kl']")
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static'),
self.assertEqual(filter_url('http://domain.com/welcome/static/path/to/static'),
norm_root("%s/applications/welcome/static/path/to/static" % root))
# no more necessary since explcit check for directory traversal attacks
"""
@@ -174,7 +174,7 @@ default_application = 'defapp'
self.assertEqual(filter_url('http://domain.com/welcome/default/index/abc'), "/welcome/default/index ['abc']")
self.assertEqual(filter_url('http://domain.com/welcome/static/abc'),
norm_root('%s/applications/welcome/static/abc' % root))
self.assertEqual(filter_url('http://domain.com/defapp/static/path/to/static'),
self.assertEqual(filter_url('http://domain.com/defapp/static/path/to/static'),
norm_root("%s/applications/defapp/static/path/to/static" % root))
def test_routes_raise(self):
+83
View File
@@ -0,0 +1,83 @@
#!/bin/python
# -*- coding: utf-8 -*-
"""
Unit tests for gluon.tools
"""
import os
import sys
if sys.version < "2.7":
import unittest2 as unittest
else:
import unittest
from fix_path import fix_sys_path
fix_sys_path(__file__)
DEFAULT_URI = os.getenv('DB', 'sqlite:memory')
from gluon.dal import DAL, Field
from dal.objects import Table
from tools import Auth
from gluon.globals import Request, Response, Session
from storage import Storage
from languages import translator
from gluon.http import HTTP
python_version = sys.version[:3]
IS_IMAP = "imap" in DEFAULT_URI
@unittest.skipIf(IS_IMAP, "TODO: Imap raises 'Connection refused'")
class testAuth(unittest.TestCase):
def testRun(self):
# setup
request = Request(env={})
request.application = 'a'
request.controller = 'c'
request.function = 'f'
request.folder = 'applications/admin'
response = Response()
session = Session()
T = translator('', 'en')
session.connect(request, response)
from gluon.globals import current
current.request = request
current.response = response
current.session = session
current.T = T
db = DAL(DEFAULT_URI, check_reserved=['all'])
auth = Auth(db)
auth.define_tables(username=True, signature=False)
self.assertTrue('auth_user' in db)
self.assertTrue('auth_group' in db)
self.assertTrue('auth_membership' in db)
self.assertTrue('auth_permission' in db)
self.assertTrue('auth_event' in db)
db.define_table('t0', Field('tt'), auth.signature)
auth.enable_record_versioning(db)
self.assertTrue('t0_archive' in db)
for f in ['login', 'register', 'retrieve_password',
'retrieve_username']:
html_form = getattr(auth, f)().xml()
self.assertTrue('name="_formkey"' in html_form)
for f in ['logout', 'verify_email', 'reset_password',
'change_password', 'profile', 'groups']:
self.assertRaisesRegexp(HTTP, "303*", getattr(auth, f))
self.assertRaisesRegexp(HTTP, "401*", auth.impersonate)
try:
for t in ['t0_archive', 't0', 'auth_cas', 'auth_event',
'auth_membership', 'auth_permission', 'auth_group',
'auth_user']:
db[t].drop()
except SyntaxError as e:
# GAE doesn't support drop
pass
return
if __name__ == '__main__':
unittest.main()
+9 -5
View File
@@ -136,6 +136,7 @@ class Mail(object):
mail.settings.server
mail.settings.sender
mail.settings.login
mail.settings.timeout = 60 # seconds (default)
When server is 'logging', email is logged but not sent (debug mode)
@@ -265,6 +266,7 @@ class Mail(object):
settings.sender = sender
settings.login = login
settings.tls = tls
settings.timeout = 60 # seconds
settings.hostname = None
settings.ssl = False
settings.cipher_type = None
@@ -787,10 +789,11 @@ class Mail(object):
subject=subject, body=text, **xcc)
else:
smtp_args = self.settings.server.split(':')
kwargs = dict(timeout = self.settings.timeout)
if self.settings.ssl:
server = smtplib.SMTP_SSL(*smtp_args)
server = smtplib.SMTP_SSL(*smtp_args, **kwargs)
else:
server = smtplib.SMTP(*smtp_args)
server = smtplib.SMTP(*smtp_args, **kwargs)
if self.settings.tls and not self.settings.ssl:
server.ehlo(self.settings.hostname)
server.starttls()
@@ -842,6 +845,7 @@ class Recaptcha(DIV):
comment = '',
ajax=False
):
request = request or current.request
self.request_vars = request and request.vars or current.request.vars
self.remote_addr = request.env.remote_addr
self.public_key = public_key
@@ -1257,7 +1261,8 @@ class Auth(object):
def __init__(self, environment=None, db=None, mailer=True,
hmac_key=None, controller='default', function='user',
cas_provider=None, signature=True, secure=False,
csrf_prevention=True, propagate_extension=None):
csrf_prevention=True, propagate_extension=None,
url_index=None):
## next two lines for backward compatibility
if not db and environment and isinstance(environment, DAL):
@@ -1294,7 +1299,7 @@ class Auth(object):
del session.auth
# ## what happens after login?
url_index = URL(controller, 'index')
url_index = url_index or URL(controller, 'index')
url_login = URL(controller, function, args='login',
extension = propagate_extension)
# ## what happens after registration?
@@ -6164,4 +6169,3 @@ class Config(object):
if __name__ == '__main__':
import doctest
doctest.testmod()
-1
View File
@@ -350,4 +350,3 @@ def getipaddrinfo(host):
and isinstance(addrinfo[4][0], basestring)]
except socket.error:
return []