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:
@@ -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
@@ -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"]
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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,4 +1,3 @@
|
||||
|
||||
"""
|
||||
Developed by Massimo Di Pierro
|
||||
Released under the web2py license (LGPL)
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user