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
+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: