Compare commits

..

41 Commits

Author SHA1 Message Date
mdipierro
2802e29945 2.9.5 2014-03-15 21:25:04 -05:00
mdipierro
e9eb8cbfc7 Merge pull request #403 from pyner/master
Update default.py
2014-03-15 21:09:44 -05:00
mdipierro
d343d8380c Merge pull request #401 from niphlod/docs/n-p
sphinx-compatible docstrings
2014-03-15 21:08:05 -05:00
mdipierro
02906fa39f Merge pull request #400 from timrichardson/issues/1898s
better error handling in jsonrpc requests
2014-03-15 21:07:31 -05:00
mdipierro
c1e797fe24 removed gluon/contrib/translitcodec because of backward compatibility issues and because it breaks Pypy 2014-03-15 10:54:48 -05:00
Massimo
d7a42d8445 fixed problem with groups 2014-03-14 09:00:19 -05:00
samuel bonilla
4f82bed52e Update default.py
new alert: it's a plugin is not an application
2014-03-13 20:04:00 -05:00
mdipierro
84365f6721 fixed problem with circular imputs on GAE? 2014-03-13 08:41:31 -05:00
niphlod
e209dcc8d3 sphinx-compatible docstrings 2014-03-12 22:03:42 +01:00
Tim Richardson
cd15a0f983 better error handling in jsonrpc requests 2014-03-13 07:29:42 +11:00
mdipierro
eb435e785c Merge pull request #399 from timrichardson/issues/1898
jsonrpc2 requests need mandatory key/pair jsonrpc:'2.0'
2014-03-12 13:39:05 -05:00
mdipierro
f03e521120 Merge pull request #398 from niphlod/docs/h-m
sphinx-compatible docstrings
2014-03-12 13:37:42 -05:00
mdipierro
883909af4b reverted s.encode('translit/long') which breaks Pypy 2014-03-12 13:31:09 -05:00
Tim Richardson
13d66433e7 jsonrpc2 requests need mandatory key/pair jsonrpc:'2.0' 2014-03-12 22:38:29 +11:00
niphlod
c44cfcb27c sphinx-compatible docstrings 2014-03-11 22:18:33 +01:00
mdipierro
c14e5cbae5 Merge pull request #396 from niphlod/docs/html
sphinx-compatible docstrings
2014-03-11 10:51:31 -05:00
niphlod
f09e0b4205 sphinx-compatible docstrings 2014-03-10 22:21:33 +01:00
mdipierro
ad72cea9a6 fixed issue 1891:Remove response.meta.description from menu.py in welcome app, thanks sourcingp 2014-03-09 17:29:41 -05:00
mdipierro
c289bc4239 fixed issue 1893:backwards compatibility problem with grid parameter exportclasses, thanks mweissen 2014-03-09 17:26:40 -05:00
mdipierro
733fa01b7e fixed issue 1894:represent requires 2 arguments in dal.py, thanks mweissen 2014-03-09 17:22:39 -05:00
mdipierro
4d03460944 fixed startup message, thanks mweissen 2014-03-09 17:19:03 -05:00
mdipierro
b3ba5d9eaa Merge pull request #393 from niphlod/fix/tests
fix travis.yml to fetch the latest release of gae
2014-03-09 09:11:03 -05:00
mdipierro
9e8ef3585e fixed problem with login bare when registration_key is None 2014-03-09 08:55:22 -05:00
niphlod
5c9cf44720 fix travis.yml to fetch the latest release of gae (needs manual update whenever a version changes) 2014-03-09 14:11:53 +01:00
mdipierro
05a0d19ee9 cleanup 2014-03-08 14:40:34 -06:00
mdipierro
d38adbe6ca Merge pull request #392 from apa-1/master
Fix for session unique_key query
2014-03-08 14:32:27 -06:00
mdipierro
871981cec7 Merge pull request #391 from michele-comitini/oauth_content-type
oauth strip encoding part in content-type response from server.
2014-03-08 14:31:32 -06:00
mdipierro
8a54001e11 Merge pull request #390 from dokime7/master
Fix DAL ADD method
2014-03-08 14:30:42 -06:00
mdipierro
1e59f6e8be Merge pull request #389 from timrichardson/issues/1888
add Recaptcha(...ajax=True) to use ajax api, fix Recaptcha in LOAD issue/1888
2014-03-08 14:29:58 -06:00
mdipierro
0e1eb5b56e Merge branch 'master' of github.com:web2py/web2py 2014-03-08 14:28:49 -06:00
mdipierro
e43df945b4 no more pypy test for today 2014-03-08 14:28:18 -06:00
mdipierro
c23706b794 Merge pull request #388 from gitanoqevaporelmundoentero/master
dal.py: Table class: validate_and_update_or_insert method created, validate_and_update minor changes
2014-03-08 14:15:43 -06:00
alex
98cd10c6f5 Fix for session unique_key query 2014-03-08 14:37:15 -05:00
Michele Comitini
55b92e854c use mimemessage method gettype() to extract content-type 2014-03-07 18:01:41 +01:00
Michele Comitini
0c926d60b8 oauth strip encoding part in content-type response from server. 2014-03-07 17:31:20 +01:00
Oscar Rodriguez
d4d91fd003 dal.py: Table class: validate_and_update_or_insert method created 2014-03-07 16:22:09 +01:00
Jeremie Dokime
89c4efbac3 Fix DAL ADD method
Fix regression of ADD method:
NameError: global name 'gluon' is not defined
2014-03-06 17:56:11 +01:00
Oscar Rodriguez
3a5a34da0a dal.py: Table class: validate_and_update_or_insert method created, and minor changes in validate_and_update 2014-03-06 12:06:57 +01:00
Tim Richardson
cf3992545d add Recaptcha(...ajax=True) to use ajax api, fix Recaptcha in LOAD 2014-03-06 09:40:48 +11:00
Oscar Rodriguez
0b7f663d43 dal.py: Table class: validate_and_update_or_insert method created, and minor changes in validate_and_update 2014-03-05 19:46:19 +01:00
Oscar Rodriguez
abd72f8df0 dal.py: Table class: validate_and_update_or_insert method created, and minor changes in validate_and_update 2014-03-05 17:44:56 +01:00
31 changed files with 691 additions and 3039 deletions

View File

@@ -22,13 +22,9 @@ before_script:
- if [[ $DB == mysql* ]]; then mysql -e 'create database test_w2p;'; fi
- if [[ $DB == postgres* ]]; then psql -c 'create database test_w2p;' -U postgres; fi
# Install last sdk for app engine
- if [[ $DB == google* ]]; then wget http://googleappengine.googlecode.com/svn/trunk/python/VERSION -O ./GAEVERSION; fi
- if [[ $DB == google* ]]; then GAERELEASE=$(cat ./GAEVERSION | grep -i release); fi
- if [[ $DB == google* ]]; then GAERELEASE=${GAERELEASE#*\"}; fi
- if [[ $DB == google* ]]; then GAERELEASE=${GAERELEASE%\"}; fi
- if [[ $DB == google* ]]; then wget http://googleappengine.googlecode.com/files/google_appengine_$GAERELEASE.zip -nv; fi
- if [[ $DB == google* ]]; then unzip -q google_appengine_$GAERELEASE.zip; fi
# Install last sdk for app engine (update only whenever a new release is available)
- if [[ $DB == google* ]]; then wget http://googleappengine.googlecode.com/files/google_appengine_1.8.9.zip -nv; fi
- if [[ $DB == google* ]]; then unzip -q google_appengine_1.8.9.zip; fi
- if [[ $DB == google* ]]; then mv -f ./google_appengine/google ./google; fi
- if [[ $DB == mongodb* ]]; then pip install pymongo; fi

View File

@@ -1,5 +1,6 @@
## 2.9.1 - 2.9.3
## 2.9.1 - 2.9.5
- many small but important bug fixes
- jquery 1.11
- codemirror 3.21, thanks Paolo Valleri
- fixed security issue with sessions in database, thanks Nathan Humphreys
@@ -20,7 +21,6 @@
- invalidate function in web2py.js, thanks Paolo
- DAL(...,adapter_args=dict(engine='MyISAM'))
- todolist panel in admin editor, thanks Paolo Valleri
- enable killing processed in windows, thanks Yair
## 2.8.1

View File

@@ -30,7 +30,7 @@ update:
echo "remember that pymysql was tweaked"
src:
### Use semantic versioning
echo 'Version 2.9.4-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
echo 'Version 2.9.5-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files
make clean
### clean up baisc apps

View File

@@ -1 +1 @@
Version 2.9.4-stable+timestamp.2014.03.04.22.40.54
Version 2.9.5-stable+timestamp.2014.03.15.21.24.06

View File

@@ -1937,7 +1937,7 @@ def install_plugin():
session.flash = T('New plugin installed: %s', filename)
else:
session.flash = \
T('unable to create application "%s"', filename)
T('unable to install plugin "%s"', filename)
redirect(URL(f="plugins", args=[app,]))
return dict(form=form, app=app, plugin=plugin, source=source)

View File

@@ -16,6 +16,7 @@
<ul>
<li><a target="_blank" href="http://www.metacryption.com">MetaCryption, LLC</a> (USA)</li>
<li><a target="_blank" href="http://www.planethost.com">PlanetHost</a> (USA)</li>
<li><a target="_blank" href="http://www.secution.com">Secution, Inc</a> (USA)</li>
<li><a target="_blank" href="http://www.wadecybertech.com">Wade Cybertech</a> (Canada)</li>
<li><a target="_blank" href="http://www.formatics.nl">Formatics</a> (Netherlands)</li>

View File

@@ -12,7 +12,6 @@ response.subtitle = ''
## read more at http://dev.w3.org/html5/markup/meta.name.html
response.meta.author = 'Your Name <you@example.com>'
response.meta.description = 'a cool new app'
response.meta.keywords = 'web2py, python, framework'
response.meta.generator = 'Web2py Web Framework'

View File

@@ -11,7 +11,7 @@ Web2Py framework modules
"""
__all__ = ['A', 'B', 'BEAUTIFY', 'BODY', 'BR', 'CAT', 'CENTER', 'CLEANUP', 'CODE', 'CRYPT', 'DAL', 'DIV', 'EM', 'EMBED', 'FIELDSET', 'FORM', 'Field', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'HEAD', 'HR', 'HTML', 'HTTP', 'I', 'IFRAME', 'IMG', 'INPUT', 'IS_ALPHANUMERIC', 'IS_DATE', 'IS_DATETIME', 'IS_DATETIME_IN_RANGE', 'IS_DATE_IN_RANGE', 'IS_DECIMAL_IN_RANGE', 'IS_EMAIL', 'IS_LIST_OF_EMAILS', 'IS_EMPTY_OR', 'IS_EQUAL_TO', 'IS_EXPR', 'IS_FLOAT_IN_RANGE', 'IS_IMAGE', 'IS_JSON', 'IS_INT_IN_RANGE', 'IS_IN_DB', 'IS_IN_SET', 'IS_IPV4', 'IS_LENGTH', 'IS_LIST_OF', 'IS_LOWER', 'IS_MATCH', 'IS_NOT_EMPTY', 'IS_NOT_IN_DB', 'IS_NULL_OR', 'IS_SLUG', 'IS_STRONG', 'IS_TIME', 'IS_UPLOAD_FILENAME', 'IS_UPPER', 'IS_URL', 'LABEL', 'LEGEND', 'LI', 'LINK', 'LOAD', 'MARKMIN', 'MENU', 'META', 'OBJECT', 'OL', 'ON', 'OPTGROUP', 'OPTION', 'P', 'PRE', 'SCRIPT', 'SELECT', 'SPAN', 'SQLFORM', 'SQLTABLE', 'STRONG', 'STYLE', 'TABLE', 'TAG', 'TBODY', 'TD', 'TEXTAREA', 'TFOOT', 'TH', 'THEAD', 'TITLE', 'TR', 'TT', 'UL', 'URL', 'XHTML', 'XML', 'redirect', 'current', 'embed64']
from globals import current
from html import *
from validators import *

View File

@@ -172,9 +172,9 @@ server for requests. It can be used for the optional"scope" parameters for Face
if open_url:
try:
data = open_url.read()
resp_type = open_url.info().get('Content-Type')
resp_type = open_url.info().gettype()
# try json style first
if not resp_type or resp_type == 'application/json':
if not resp_type or resp_type[:16] == 'application/json':
try:
tokendata = json.loads(data)
current.session.token = tokendata

View File

@@ -111,7 +111,7 @@ class MockTable(object):
self.session_expiry = session_expiry
self.with_lock = with_lock
def __call__(self, record_id):
def __call__(self, record_id, unique_key=None):
# Support DAL shortcut query: table(record_id)
q = self.id # This will call the __getattr__ below
@@ -120,6 +120,7 @@ class MockTable(object):
# Instructs MockQuery, to behave as db(table.id == record_id)
q.op = 'eq'
q.value = record_id
q.unique_key = unique_key
row = q.select()
return row[0] if row else Storage()
@@ -129,7 +130,7 @@ class MockTable(object):
#return a fake query. We need to query it just by id for normal operations
self.query = MockQuery(field='id', db=self.r_server,
prefix=self.keyprefix, session_expiry=self.session_expiry,
with_lock=self.with_lock)
with_lock=self.with_lock, unique_key=self.unique_key)
return self.query
elif key == '_db':
#needed because of the calls in sessions2trash.py and globals.py
@@ -162,7 +163,7 @@ class MockQuery(object):
and listing all keys. No other operation is supported
"""
def __init__(self, field=None, db=None, prefix=None, session_expiry=False,
with_lock=False):
with_lock=False, unique_key=None):
self.field = field
self.value = None
self.db = db
@@ -170,6 +171,7 @@ class MockQuery(object):
self.op = None
self.session_expiry = session_expiry
self.with_lock = with_lock
self.unique_key = unique_key
def __eq__(self, value, op='eq'):
self.value = value
@@ -187,7 +189,12 @@ class MockQuery(object):
acquire_lock(self.db, key + ':lock', self.value)
rtn = self.db.hgetall(key)
if rtn:
rtn['update_record'] = self.update # update record support
if self.unique_key:
#make sure the id and unique_key are correct
if rtn['unique_key'] == self.unique_key:
rtn['update_record'] = self.update # update record support
else:
rtn = None
return [Storage(rtn)] if rtn else []
elif self.op == 'ge' and self.field == 'id' and self.value == 0:
#means that someone wants the complete list

View File

@@ -81,12 +81,13 @@ class JSONSafeTransport(JSONTransportMixin, SafeTransport):
class ServerProxy(object):
"JSON RPC Simple Client Service Proxy"
def __init__(self, uri, transport=None, encoding=None, verbose=0):
def __init__(self, uri, transport=None, encoding=None, verbose=0,version=None):
self.location = uri # server location (url)
self.trace = verbose # show debug messages
self.exceptions = True # raise errors? (JSONRPCError)
self.timeout = None
self.json_request = self.json_response = ''
self.version = version # '2.0' for jsonrpc2
type, uri = urllib.splittype(uri)
if type not in ("http", "https"):
@@ -112,6 +113,8 @@ class ServerProxy(object):
# build data sent to the service
request_id = random.randint(0, sys.maxint)
data = {'id': request_id, 'method': method, 'params': args, }
if self.version:
data['jsonrpc'] = self.version #mandatory key/value for jsonrpc2 validation else err -32600
request = json.dumps(data)
# make HTTP request (retry if connection is lost)
@@ -130,14 +133,13 @@ class ServerProxy(object):
# {'version': '1.1', 'id': id, 'result': result, 'error': None}
response = json.loads(response)
if response['id'] != request_id:
raise JSONRPCError(0, "JSON Request ID != Response ID")
self.error = response.get('error', {})
if self.error and self.exceptions:
raise JSONRPCError(self.error.get('code', 0),
self.error.get('message', ''),
self.error.get('data', None))
if response['id'] != request_id:
raise JSONRPCError(0, "JSON Request ID != Response ID")
return response.get('result')

View File

@@ -1,9 +0,0 @@
translitcodec was originally written by Jason Kirtland in 2008.
Contributors are:
- Jason Kirtland <jek@discorporate.us>
- Craig Dennis <craig@idealist.org>
The translitcodec source distribution includes the 'transtab' package
by Markus Kuhn <mkuhn@acm.org>.

View File

@@ -1,31 +0,0 @@
=====================
translitcodec Changes
=====================
0.3
---
Released on February 14, 2011
- Fixes to the transtab table rebuilding tool.
- Added translitcodec.__version__
0.2
---
Released on January 27, 2011
- Resolves issue of "TypeError: character mapping must return integer,
None or unicode" when a blank value (eg: \N{ZERO WIDTH SPACE} \u200B)
was encoded. Unicode blanks are now returned.
- Characters in the ASCII range are no longer included in the translation
tables.
0.1
---
Released on December 28, 2008
- Initial packaged release.

View File

@@ -1,20 +0,0 @@
Copyright (c) 2008 Jason Kirtland <jek at discorporate us>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,44 +0,0 @@
-*- coding: utf-8 -*-
Unicode to 8-bit charset transliteration codec.
This package contains codecs for transliterating ISO 10646 texts into
best-effort representations using smaller coded character sets (ASCII,
ISO 8859, etc.). The translation tables used by the codecs are from
the ``transtab`` collection by Markus Kuhn.
Three types of transliterating codecs are provided:
"long", using as many characters as needed to make a natural
replacement. For example, \u00e4 LATIN SMALL LETTER A WITH
DIAERESIS ``ä`` will be replaced with ``ae``.
"short", using the minimum number of characters to make a
replacement. For example, \u00e4 LATIN SMALL LETTER A WITH
DIAERESIS ``ä`` will be replaced with ``a``.
"one", only performing single character replacements. Characters
that can not be transliterated with a single character are passed
through unchanged. For example, \u2639 WHITE FROWNING FACE ``☹``
will be passed through unchanged.
Using the codecs is simple::
>>> import translitcodec
>>> u'fácil € ☺'.encode('translit/long')
u'facil EUR :-)'
>>> u'fácil € ☺'.encode('translit/short')
u'facil E :-)'
The codecs return Unicode by default. To receive a bytestring back,
either chain the output of encode() to another codec, or append the
name of the desired byte encoding to the codec name::
>>> u'fácil € ☺'.encode('translit/one').encode('ascii', 'replace')
'facil E ?'
>>> u'fácil € ☺'.encode('translit/one/ascii', 'replace')
'facil E ?'
The package also supplies a 'transliterate' codec, an alias for
'translit/long'.

File diff suppressed because it is too large Load Diff

View File

@@ -1480,7 +1480,7 @@ class BaseAdapter(ConnectionPool):
return '(%s)' % ' || '.join(self.expand(x,'string') for x in items)
def ADD(self, first, second):
if self.is_numerical_type(first.type) or isinstance(first.type, gluon.dal.Field):
if self.is_numerical_type(first.type) or isinstance(first.type, Field):
return '(%s + %s)' % (self.expand(first),
self.expand(second, first.type))
else:
@@ -9153,8 +9153,17 @@ class Table(object):
record = self(_key)
if not response.errors and record:
row = self._db(self._id ==_key)
response.id = row.update(**fields)
if '_id' in self:
myset = self._db(self._id == record[self._id.name])
else:
query = None
for key, value in _key.iteritems():
if query is None:
query = getattr(self, key) == value
else:
query = query & (getattr(self, key) == value)
myset = self._db(query)
response.id = myset.update(**fields)
else:
response.id = None
return response
@@ -9173,6 +9182,37 @@ class Table(object):
newid = self.insert(**values)
return newid
def validate_and_update_or_insert(self, _key=DEFAULT, **fields):
if _key is DEFAULT or _key == '':
primary_keys = {}
for key, value in fields.iteritems():
if key in self._primarykey:
primary_keys[key] = value
if primary_keys != {}:
record = self(**primary_keys)
_key = primary_keys
else:
required_keys = {}
for key, value in fields.iteritems():
if getattr(self, key).required:
required_keys[key] = value
record = self(**required_keys)
_key = required_keys
elif isinstance(_key, dict):
record = self(**_key)
else:
record = self(_key)
if record:
response = self.validate_and_update(_key, **fields)
primary_keys = {}
for key in self._primarykey:
primary_keys[key] = getattr(record, key)
response.id = primary_keys
else:
response = self.validate_and_insert(**fields)
return response
def bulk_insert(self, items):
"""
here items is a list of dictionaries
@@ -11074,7 +11114,7 @@ class Rows(object):
if field.type=='blob' and not value is None:
value = base64.b64encode(value)
elif represent and field.represent:
value = field.represent(value)
value = field.represent(value,record)
row.append(none_exception(value))
writer.writerow(row)

View File

@@ -2,9 +2,12 @@
# -*- coding: utf-8 -*-
"""
This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
| This file is part of the web2py Web Framework
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Template helpers
--------------------------------------------
"""
import cgi
@@ -109,10 +112,11 @@ __all__ = [
def xmlescape(data, quote=True):
"""
returns an escaped string of the provided data
Returns an escaped string of the provided data
:param data: the data to be escaped
:param quote: optional (default False)
Args:
data: the data to be escaped
quote: optional (default False)
"""
# first try the xml function
@@ -164,80 +168,92 @@ def URL(
language=None,
):
"""
generate a URL
example::
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':1, 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=1&q=2#1'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(1,3), 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=1&p=3&q=2#1'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(3,1), 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=3&p=1&q=2#1'
>>> str(URL(a='a', c='c', f='f', anchor='1+2'))
'/a/c/f#1%2B2'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(1,3), 'q':2}, anchor='1', hmac_key='key'))
'/a/c/f/x/y/z?p=1&p=3&q=2&_signature=a32530f0d0caa80964bb92aad2bedf8a4486a31f#1'
>>> str(URL(a='a', c='c', f='f', args=['w/x', 'y/z']))
'/a/c/f/w/x/y/z'
>>> str(URL(a='a', c='c', f='f', args=['w/x', 'y/z'], encode_embedded_slash=True))
'/a/c/f/w%2Fx/y%2Fz'
>>> str(URL(a='a', c='c', f='f', args=['%(id)d'], url_encode=False))
'/a/c/f/%(id)d'
>>> str(URL(a='a', c='c', f='f', args=['%(id)d'], url_encode=True))
'/a/c/f/%25%28id%29d'
>>> str(URL(a='a', c='c', f='f', vars={'id' : '%(id)d' }, url_encode=False))
'/a/c/f?id=%(id)d'
>>> str(URL(a='a', c='c', f='f', vars={'id' : '%(id)d' }, url_encode=True))
'/a/c/f?id=%25%28id%29d'
>>> str(URL(a='a', c='c', f='f', anchor='%(id)d', url_encode=False))
'/a/c/f#%(id)d'
>>> str(URL(a='a', c='c', f='f', anchor='%(id)d', url_encode=True))
'/a/c/f#%25%28id%29d'
generates a url '/a/c/f' corresponding to application a, controller c
and function f. If r=request is passed, a, c, f are set, respectively,
to r.application, r.controller, r.function.
The more typical usage is:
URL(r=request, f='index') that generates a url for the index function
URL('index')
that generates a url for the index function
within the present application and controller.
:param a: application (default to current if r is given)
:param c: controller (default to current if r is given)
:param f: function (default to current if r is given)
:param r: request (optional)
:param args: any arguments (optional)
:param vars: any variables (optional)
:param anchor: anchorname, without # (optional)
:param hmac_key: key to use when generating hmac signature (optional)
:param hash_vars: which of the vars to include in our hmac signature
True (default) - hash all vars, False - hash none of the vars,
iterable - hash only the included vars ['key1','key2']
:param scheme: URI scheme (True, 'http' or 'https', etc); forces absolute URL (optional)
:param host: string to force absolute URL with host (True means http_host)
:param port: optional port number (forces absolute URL)
Args:
a: application (default to current if r is given)
c: controller (default to current if r is given)
f: function (default to current if r is given)
r: request (optional)
args: any arguments (optional). Additional "path" elements
vars: any variables (optional). Querystring elements
anchor: anchorname, without # (optional)
extension: force an extension
hmac_key: key to use when generating hmac signature (optional)
hash_vars: which of the vars to include in our hmac signature
True (default) - hash all vars, False - hash none of the vars,
iterable - hash only the included vars ['key1','key2']
salt: salt hashing with this string
user_signature: signs automatically the URL in such way that only the
user can access the URL (use with `URL.verify` or
`auth.requires_signature()`)
scheme: URI scheme (True, 'http' or 'https', etc); forces absolute URL (optional)
host: string to force absolute URL with host (True means http_host)
port: optional port number (forces absolute URL)
encode_embedded_slash: encode slash characters included in args
url_encode: encode characters included in vars
Raises:
SyntaxError: when no application, controller or function is available
or when a CRLF is found in the generated url
Examples:
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':1, 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=1&q=2#1'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(1,3), 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=1&p=3&q=2#1'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(3,1), 'q':2}, anchor='1'))
'/a/c/f/x/y/z?p=3&p=1&q=2#1'
>>> str(URL(a='a', c='c', f='f', anchor='1+2'))
'/a/c/f#1%2B2'
>>> str(URL(a='a', c='c', f='f', args=['x', 'y', 'z'],
... vars={'p':(1,3), 'q':2}, anchor='1', hmac_key='key'))
'/a/c/f/x/y/z?p=1&p=3&q=2&_signature=a32530f0d0caa80964bb92aad2bedf8a4486a31f#1'
>>> str(URL(a='a', c='c', f='f', args=['w/x', 'y/z']))
'/a/c/f/w/x/y/z'
>>> str(URL(a='a', c='c', f='f', args=['w/x', 'y/z'], encode_embedded_slash=True))
'/a/c/f/w%2Fx/y%2Fz'
>>> str(URL(a='a', c='c', f='f', args=['%(id)d'], url_encode=False))
'/a/c/f/%(id)d'
>>> str(URL(a='a', c='c', f='f', args=['%(id)d'], url_encode=True))
'/a/c/f/%25%28id%29d'
>>> str(URL(a='a', c='c', f='f', vars={'id' : '%(id)d' }, url_encode=False))
'/a/c/f?id=%(id)d'
>>> str(URL(a='a', c='c', f='f', vars={'id' : '%(id)d' }, url_encode=True))
'/a/c/f?id=%25%28id%29d'
>>> str(URL(a='a', c='c', f='f', anchor='%(id)d', url_encode=False))
'/a/c/f#%(id)d'
>>> str(URL(a='a', c='c', f='f', anchor='%(id)d', url_encode=True))
'/a/c/f#%25%28id%29d'
:raises SyntaxError: when no application, controller or function is
available
:raises SyntaxError: when a CRLF is found in the generated url
"""
from rewrite import url_out # done here in case used not-in web2py
@@ -528,7 +544,7 @@ class XML(XmlComponent):
use it to wrap a string that contains XML/HTML so that it will not be
escaped by the template
example:
Examples:
>>> XML('<h1>Hello</h1>').xml()
'<h1>Hello</h1>'
@@ -564,19 +580,19 @@ class XML(XmlComponent):
},
):
"""
:param text: the XML text
:param sanitize: sanitize text using the permitted tags and allowed
attributes (default False)
:param permitted_tags: list of permitted tags (default: simple list of
tags)
:param allowed_attributes: dictionary of allowed attributed (default
for A, IMG and BlockQuote).
The key is the tag; the value is a list of allowed attributes.
Args:
text: the XML text
sanitize: sanitize text using the permitted tags and allowed
attributes (default False)
permitted_tags: list of permitted tags (default: simple list of
tags)
allowed_attributes: dictionary of allowed attributed (default
for A, IMG and BlockQuote).
The key is the tag; the value is a list of allowed attributes.
"""
if sanitize:
text = sanitizer.sanitize(text, permitted_tags,
allowed_attributes)
text = sanitizer.sanitize(text, permitted_tags, allowed_attributes)
if isinstance(text, unicode):
text = text.encode('utf8', 'xmlcharrefreplace')
elif not isinstance(text, str):
@@ -620,7 +636,8 @@ class XML(XmlComponent):
def flatten(self, render=None):
"""
return the text stored by the XML object rendered by the render function
returns the text stored by the XML object rendered
by the `render` function
"""
if render:
return render(self.text, None, {})
@@ -628,8 +645,9 @@ class XML(XmlComponent):
def elements(self, *args, **kargs):
"""
to be considered experimental since the behavior of this method is questionable
another options could be TAG(self.text).elements(*args,**kargs)
to be considered experimental since the behavior of this method
is questionable
another option could be `TAG(self.text).elements(*args,**kwargs)`
"""
return []
@@ -653,15 +671,15 @@ class DIV(XmlComponent):
Behaves like a dictionary regarding updating of attributes.
Behaves like a list regarding inserting/appending components.
example::
Examples:
>>> DIV('hello', 'world', _style='color:red;').xml()
'<div style=\"color:red;\">helloworld</div>'
>>> DIV('hello', 'world', _style='color:red;').xml()
'<div style=\"color:red;\">helloworld</div>'
all other HTML helpers are derived from DIV.
All other HTML helpers are derived from `DIV`.
_something=\"value\" attributes are transparently translated into
something=\"value\" HTML attributes
`_something="value"` attributes are transparently translated into
`something="value"` HTML attributes
"""
# name of the tag, subclasses should update this
@@ -671,10 +689,12 @@ class DIV(XmlComponent):
def __init__(self, *components, **attributes):
"""
:param *components: any components that should be nested in this element
:param **attributes: any attributes you want to give to this element
Args:
components: any components that should be nested in this element
attributes: any attributes you want to give to this element
:raises SyntaxError: when a stand alone tag receives components
Raises:
SyntaxError: when a stand alone tag receives components
"""
if self.tag[-1:] == '/' and components:
@@ -705,6 +725,8 @@ class DIV(XmlComponent):
"""
list style appending of components
Examples:
>>> a=DIV()
>>> a.append(SPAN('x'))
>>> print a
@@ -717,7 +739,9 @@ class DIV(XmlComponent):
def insert(self, i, value):
"""
list style inserting of components
List-style inserting of components
Examples:
>>> a=DIV()
>>> a.insert(0,SPAN('x'))
@@ -731,12 +755,12 @@ class DIV(XmlComponent):
def __getitem__(self, i):
"""
gets attribute with name 'i' or component #i.
Gets attribute with name 'i' or component #i.
If attribute 'i' is not found returns None
:param i: index
if i is a string: the name of the attribute
otherwise references to number of the component
Args:
i: index. If i is a string: the name of the attribute
otherwise references to number of the component
"""
if isinstance(i, str):
@@ -749,12 +773,12 @@ class DIV(XmlComponent):
def __setitem__(self, i, value):
"""
sets attribute with name 'i' or component #i.
Sets attribute with name 'i' or component #i.
:param i: index
if i is a string: the name of the attribute
otherwise references to number of the component
:param value: the new value
Args:
i: index. If i is a string: the name of the attribute
otherwise references to number of the component
value: the new value
"""
self._setnode(value)
if isinstance(i, (str, unicode)):
@@ -764,11 +788,11 @@ class DIV(XmlComponent):
def __delitem__(self, i):
"""
deletes attribute with name 'i' or component #i.
Deletes attribute with name 'i' or component #i.
:param i: index
if i is a string: the name of the attribute
otherwise references to number of the component
Args:
i: index. If i is a string: the name of the attribute
otherwise references to number of the component
"""
if isinstance(i, str):
@@ -778,13 +802,13 @@ class DIV(XmlComponent):
def __len__(self):
"""
returns the number of included components
Returns the number of included components
"""
return len(self.components)
def __nonzero__(self):
"""
always return True
Always returns True
"""
return True
@@ -804,10 +828,11 @@ class DIV(XmlComponent):
helper for _fixup. Checks if a component is in allowed_parents,
otherwise wraps it in wrap_parent
:param allowed_parents: (tuple) classes that the component should be an
instance of
:param wrap_parent: the class to wrap the component in, if needed
:param wrap_lambda: lambda to use for wrapping, if needed
Args:
allowed_parents: (tuple) classes that the component should be an
instance of
wrap_parent: the class to wrap the component in, if needed
wrap_lambda: lambda to use for wrapping, if needed
"""
components = []
@@ -875,7 +900,7 @@ class DIV(XmlComponent):
def _xml(self):
"""
helper for xml generation. Returns separately:
Helper for xml generation. Returns separately:
- the component attributes
- the generated xml of the inner components
@@ -883,7 +908,8 @@ class DIV(XmlComponent):
do not have a False or None value. The underscore is removed.
A value of True is replaced with the attribute name.
:returns: tuple: (attributes, components)
Returns:
tuple: (attributes, components)
"""
# get the attributes for this component
@@ -932,16 +958,18 @@ class DIV(XmlComponent):
def __str__(self):
"""
str(COMPONENT) returns equals COMPONENT.xml()
str(COMPONENT) returns COMPONENT.xml()
"""
return self.xml()
def flatten(self, render=None):
"""
return the text stored by the DIV object rendered by the render function
Returns the text stored by the DIV object rendered by the render function
the render function must take text, tagname, and attributes
render=None is equivalent to render=lambda text, tag, attr: text
`render=None` is equivalent to `render=lambda text, tag, attr: text`
Examples:
>>> markdown = lambda text,tag=None,attributes={}: \
{None: re.sub('\s+',' ',text), \
@@ -972,11 +1000,13 @@ class DIV(XmlComponent):
def elements(self, *args, **kargs):
"""
find all component that match the supplied attribute dictionary,
Find all components that match the supplied attribute dictionary,
or None if nothing could be found
All components of the components are searched.
Examples:
>>> a = DIV(DIV(SPAN('x'),3,DIV(SPAN('y'))))
>>> for c in a.elements('span',first_only=True): c[0]='z'
>>> print a
@@ -987,6 +1017,8 @@ class DIV(XmlComponent):
It also supports a syntax compatible with jQuery
Examples:
>>> a=TAG('<div><span><a id="1-1" u:v=$>hello</a></span><p class="this is a test">world</p></div>')
>>> for e in a.elements('div a#1-1, p.is'): print e.flatten()
hello
@@ -995,7 +1027,6 @@ class DIV(XmlComponent):
hello
>>> a.elements('a[u:v=$]')[0].xml()
'<a id="1-1" u:v="$">hello</a>'
>>> a=FORM( INPUT(_type='text'), SELECT(range(1)), TEXTAREA() )
>>> for c in a.elements('input, select, textarea'): c['_disabled'] = 'disabled'
>>> a.xml()
@@ -1005,6 +1036,8 @@ class DIV(XmlComponent):
a "replace" argument (note, a list of the original matching elements
is still returned as usual).
Examples:
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements('span.abc', replace=P('x', _class='xyz'))
>>> print a
@@ -1013,6 +1046,8 @@ class DIV(XmlComponent):
"replace" can be a callable, which will be passed the original element and
should return a new element to replace it.
Examples:
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements('span.abc', replace=lambda el: P(el[0], _class='xyz'))
>>> print a
@@ -1020,6 +1055,8 @@ class DIV(XmlComponent):
If replace=None, matching elements will be removed completely.
Examples:
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements('span', find='y', replace=None)
>>> print a
@@ -1030,6 +1067,8 @@ class DIV(XmlComponent):
replaced (find_text is ignored if "replace" is not also specified).
Like the "find" argument, "find_text" can be a string or a compiled regex.
Examples:
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='abc'), SPAN('z', _class='abc'))))
>>> b = a.elements(find_text=re.compile('x|y|z'), replace='hello')
>>> print a
@@ -1038,6 +1077,8 @@ class DIV(XmlComponent):
If other attributes are specified along with find_text, then only components
that match the specified attributes will be searched for find_text.
Examples:
>>> a = DIV(DIV(SPAN('x', _class='abc'), DIV(SPAN('y', _class='efg'), SPAN('z', _class='abc'))))
>>> b = a.elements('span.efg', find_text=re.compile('x|y|z'), replace='hello')
>>> print a
@@ -1134,7 +1175,7 @@ class DIV(XmlComponent):
def element(self, *args, **kargs):
"""
find the first component that matches the supplied attribute dictionary,
Finds the first component that matches the supplied attribute dictionary,
or None if nothing could be found
Also the components of the components are searched.
@@ -1148,7 +1189,7 @@ class DIV(XmlComponent):
def siblings(self, *args, **kargs):
"""
find all sibling components that match the supplied argument list
Finds all sibling components that match the supplied argument list
and attribute dictionary, or None if nothing could be found
"""
sibs = [s for s in self.parent.components if not s == self]
@@ -1175,7 +1216,7 @@ class DIV(XmlComponent):
def sibling(self, *args, **kargs):
"""
find the first sibling component that match the supplied argument list
Finds the first sibling component that match the supplied argument list
and attribute dictionary, or None if nothing could be found
"""
kargs['first_only'] = True
@@ -1211,10 +1252,12 @@ copy_reg.pickle(__tag_div__, TAG_pickler, TAG_unpickler)
class __TAG__(XmlComponent):
"""
TAG factory example::
TAG factory
>>> print TAG.first(TAG.second('test'), _key = 3)
<first key=\"3\"><second>test</second></first>
Examples:
>>> print TAG.first(TAG.second('test'), _key = 3)
<first key=\"3\"><second>test</second></first>
"""
@@ -1239,16 +1282,16 @@ class HTML(DIV):
There are four predefined document type definitions.
They can be specified in the 'doctype' parameter:
-'strict' enables strict doctype
-'transitional' enables transitional doctype (default)
-'frameset' enables frameset doctype
-'html5' enables HTML 5 doctype
-any other string will be treated as user's own doctype
- 'strict' enables strict doctype
- 'transitional' enables transitional doctype (default)
- 'frameset' enables frameset doctype
- 'html5' enables HTML 5 doctype
- any other string will be treated as user's own doctype
'lang' parameter specifies the language of the document.
Defaults to 'en'.
See also :class:`DIV`
See also `DIV`
"""
tag = 'html'
@@ -1289,10 +1332,10 @@ class XHTML(DIV):
There are three predefined document type definitions.
They can be specified in the 'doctype' parameter:
-'strict' enables strict doctype
-'transitional' enables transitional doctype (default)
-'frameset' enables frameset doctype
-any other string will be treated as user's own doctype
- 'strict' enables strict doctype
- 'transitional' enables transitional doctype (default)
- 'frameset' enables frameset doctype
- any other string will be treated as user's own doctype
'lang' parameter specifies the language of the document and the xml document.
Defaults to 'en'.
@@ -1300,7 +1343,7 @@ class XHTML(DIV):
'xmlns' parameter specifies the xml namespace.
Defaults to 'http://www.w3.org/1999/xhtml'.
See also :class:`DIV`
See also `DIV`
"""
tag = 'html'
@@ -1443,7 +1486,7 @@ class P(DIV):
"""
Will replace ``\\n`` by ``<br />`` if the `cr2br` attribute is provided.
see also :class:`DIV`
see also `DIV`
"""
tag = 'p'
@@ -1476,6 +1519,23 @@ class HR(DIV):
class A(DIV):
"""
Generates an A() link.
A() in web2py is really important and with the included web2py.js
allows lots of Ajax interactions in the page
On top of "usual" `_attributes`, it takes
Args:
callback: an url to call but not redirect to
cid: if you want to load the _href into an element of the page (component)
pass its id (without the #) here
delete: element to delete after calling callback
target: same thing as cid
confirm: text to display upon a callback with a delete
noconfirm: don't display alert upon a callback with delete
"""
tag = 'a'
@@ -1546,24 +1606,25 @@ class CENTER(DIV):
class CODE(DIV):
"""
displays code in HTML with syntax highlighting.
Displays code in HTML with syntax highlighting.
:param attributes: optional attributes:
Args:
language: indicates the language, otherwise PYTHON is assumed
link: can provide a link
styles: for styles
- language: indicates the language, otherwise PYTHON is assumed
- link: can provide a link
- styles: for styles
Examples:
Example::
{{=CODE(\"print 'hello world'\", language='python', link=None,
counter=1, styles={}, highlight_line=None)}}
{{=CODE(\"print 'hello world'\", language='python', link=None,
counter=1, styles={}, highlight_line=None)}}
supported languages are \"python\", \"html_plain\", \"c\", \"cpp\",
\"web2py\", \"html\".
The \"html\" language interprets {{ and }} tags as \"web2py\" code,
\"html_plain\" doesn't.
supported languages are
"python", "html_plain", "c", "cpp", "web2py", "html"
The "html" language interprets {{ and }} tags as "web2py" code,
"html_plain" doesn't.
if a link='/examples/global/vars/' is provided web2py keywords are linked to
the online docs.
@@ -1607,7 +1668,6 @@ class UL(DIV):
If subcomponents are not LI-components they will be wrapped in a LI
see also :class:`DIV`
"""
tag = 'ul'
@@ -1637,7 +1697,6 @@ class TR(DIV):
If subcomponents are not TD/TH-components they will be wrapped in a TD
see also :class:`DIV`
"""
tag = 'tr'
@@ -1652,7 +1711,6 @@ class __TRHEAD__(DIV):
If subcomponents are not TD/TH-components they will be wrapped in a TH
see also :class:`DIV`
"""
tag = 'tr'
@@ -1702,7 +1760,6 @@ class TABLE(DIV):
If subcomponents are not TR/TBODY/THEAD/TFOOT-components
they will be wrapped in a TR
see also :class:`DIV`
"""
tag = 'table'
@@ -1724,34 +1781,36 @@ class IFRAME(DIV):
class INPUT(DIV):
"""
INPUT Component
INPUT Component
examples::
Takes two special attributes value= and requires=.
>>> INPUT(_type='text', _name='name', value='Max').xml()
'<input name=\"name\" type=\"text\" value=\"Max\" />'
>>> INPUT(_type='checkbox', _name='checkbox', value='on').xml()
'<input checked=\"checked\" name=\"checkbox\" type=\"checkbox\" value=\"on\" />'
>>> INPUT(_type='radio', _name='radio', _value='yes', value='yes').xml()
'<input checked=\"checked\" name=\"radio\" type=\"radio\" value=\"yes\" />'
>>> INPUT(_type='radio', _name='radio', _value='no', value='yes').xml()
'<input name=\"radio\" type=\"radio\" value=\"no\" />'
the input helper takes two special attributes value= and requires=.
:param value: used to pass the initial value for the input field.
Args:
value: used to pass the initial value for the input field.
value differs from _value because it works for checkboxes, radio,
textarea and select/option too.
For a checkbox value should be '' or 'on'.
For a radio or select/option value should be the _value
of the checked/selected item.
- for a checkbox value should be '' or 'on'.
- for a radio or select/option value should be the _value
of the checked/selected item.
:param requires: should be None, or a validator or a list of validators
requires: should be None, or a validator or a list of validators
for the value of the field.
Examples:
>>> INPUT(_type='text', _name='name', value='Max').xml()
'<input name=\"name\" type=\"text\" value=\"Max\" />'
>>> INPUT(_type='checkbox', _name='checkbox', value='on').xml()
'<input checked=\"checked\" name=\"checkbox\" type=\"checkbox\" value=\"on\" />'
>>> INPUT(_type='radio', _name='radio', _value='yes', value='yes').xml()
'<input checked=\"checked\" name=\"radio\" type=\"radio\" value=\"yes\" />'
>>> INPUT(_type='radio', _name='radio', _value='no', value='yes').xml()
'<input name=\"radio\" type=\"radio\" value=\"no\" />'
"""
tag = 'input/'
@@ -1847,11 +1906,12 @@ class INPUT(DIV):
class TEXTAREA(INPUT):
"""
example::
Examples::
TEXTAREA(_name='sometext', value='blah '*100, requires=IS_NOT_EMPTY())
TEXTAREA(_name='sometext', value='blah ' * 100, requires=IS_NOT_EMPTY())
'blah blah blah ...' will be the content of the textarea field.
"""
tag = 'textarea'
@@ -1898,12 +1958,12 @@ class OPTGROUP(DIV):
class SELECT(INPUT):
"""
example::
Examples:
>>> from validators import IS_IN_SET
>>> SELECT('yes', 'no', _name='selector', value='yes',
... requires=IS_IN_SET(['yes', 'no'])).xml()
'<select name=\"selector\"><option selected=\"selected\" value=\"yes\">yes</option><option value=\"no\">no</option></select>'
>>> from validators import IS_IN_SET
>>> SELECT('yes', 'no', _name='selector', value='yes',
... requires=IS_IN_SET(['yes', 'no'])).xml()
'<select name=\"selector\"><option selected=\"selected\" value=\"yes\">yes</option><option value=\"no\">no</option></select>'
"""
@@ -1962,12 +2022,13 @@ class LEGEND(DIV):
class FORM(DIV):
"""
example::
Examples:
>>> from validators import IS_NOT_EMPTY
>>> form=FORM(INPUT(_name="test", requires=IS_NOT_EMPTY()))
>>> form.xml()
'<form action=\"#\" enctype=\"multipart/form-data\" method=\"post\"><input name=\"test\" type=\"text\" /></form>'
>>> from validators import IS_NOT_EMPTY
>>> form=FORM(INPUT(_name="test", requires=IS_NOT_EMPTY()))
>>> form.xml()
'<form action=\"#\" enctype=\"multipart/form-data\" method=\"post\"><input name=\"test\" type=\"text\" /></form>'
a FORM is container for INPUT, TEXTAREA, SELECT and other helpers
@@ -2107,12 +2168,12 @@ class FORM(DIV):
you can use it instead of directly form.accepts.
Usage:
In controller
In controller::
def action():
form=FORM(INPUT(_name=\"test\", requires=IS_NOT_EMPTY()))
form.validate() #you can pass some args here - see below
return dict(form=form)
def action():
form=FORM(INPUT(_name=\"test\", requires=IS_NOT_EMPTY()))
form.validate() #you can pass some args here - see below
return dict(form=form)
This can receive a bunch of arguments
@@ -2190,29 +2251,31 @@ class FORM(DIV):
"""
Perform the .validate() method but returns the form
Usage in controllers:
# directly on return
def action():
#some code here
return dict(form=FORM(...).process(...))
Usage in controllers::
You can use it with FORM, SQLFORM or FORM based plugins
# directly on return
def action():
#some code here
return dict(form=FORM(...).process(...))
Examples:
#response.flash messages
def action():
form = SQLFORM(db.table).process(message_onsuccess='Sucess!')
retutn dict(form=form)
You can use it with FORM, SQLFORM or FORM based plugins::
# response.flash messages
def action():
form = SQLFORM(db.table).process(message_onsuccess='Sucess!')
return dict(form=form)
# callback function
# callback receives True or False as first arg, and a list of args.
def my_callback(status, msg):
response.flash = "Success! "+msg if status else "Errors occured"
# after argument can be 'flash' to response.flash messages
# or a function name to use as callback or None to do nothing.
def action():
return dict(form=SQLFORM(db.table).process(onsuccess=my_callback)
# callback function
# callback receives True or False as first arg, and a list of args.
def my_callback(status, msg):
response.flash = "Success! "+msg if status else "Errors occured"
# after argument can be 'flash' to response.flash messages
# or a function name to use as callback or None to do nothing.
def action():
return dict(form=SQLFORM(db.table).process(onsuccess=my_callback)
"""
kwargs['dbio'] = kwargs.get('dbio', True)
# necessary for SQLHTML forms
@@ -2223,7 +2286,7 @@ class FORM(DIV):
def add_button(self, value, url, _class=None):
submit = self.element(_type='submit')
_class = "%s w2p-form-button" % _class if _class else "w2p-form-button"
_class = "%s w2p-form-button" % _class if _class else "w2p-form-button"
submit.parent.append(
TAG['button'](value, _class=_class,
_onclick=url if url.startswith('javascript:') else
@@ -2313,16 +2376,20 @@ class FORM(DIV):
class BEAUTIFY(DIV):
"""
example::
Turns any list, dictionary, etc into decent looking html.
>>> BEAUTIFY(['a', 'b', {'hello': 'world'}]).xml()
'<div><table><tr><td><div>a</div></td></tr><tr><td><div>b</div></td></tr><tr><td><div><table><tr><td style="font-weight:bold;vertical-align:top;">hello</td><td style="vertical-align:top;">:</td><td><div>world</div></td></tr></table></div></td></tr></table></div>'
turns any list, dictionary, etc into decent looking html.
Two special attributes are
:sorted: a function that takes the dict and returned sorted keys
:keyfilter: a funciton that takes a key and returns its representation
or None if the key is to be skipped. By default key[:1]=='_' is skipped.
- sorted: a function that takes the dict and returned sorted keys
- keyfilter: a function that takes a key and returns its representation or
None if the key is to be skipped.
By default key[:1]=='_' is skipped.
Examples:
>>> BEAUTIFY(['a', 'b', {'hello': 'world'}]).xml()
'<div><table><tr><td><div>a</div></td></tr><tr><td><div>b</div></td></tr><tr><td><div><table><tr><td style="font-weight:bold;vertical-align:top;">hello</td><td style="vertical-align:top;">:</td><td><div>world</div></td></tr></table></div></td></tr></table></div>'
"""
tag = 'div'
@@ -2394,16 +2461,18 @@ class MENU(DIV):
"""
Used to build menus
Optional arguments
_class: defaults to 'web2py-menu web2py-menu-vertical'
ul_class: defaults to 'web2py-menu-vertical'
li_class: defaults to 'web2py-menu-expand'
li_first: defaults to 'web2py-menu-first'
li_last: defaults to 'web2py-menu-last'
Args:
_class: defaults to 'web2py-menu web2py-menu-vertical'
ul_class: defaults to 'web2py-menu-vertical'
li_class: defaults to 'web2py-menu-expand'
li_first: defaults to 'web2py-menu-first'
li_last: defaults to 'web2py-menu-last'
Use like::
Example:
menu = MENU([['name', False, URL(...), [submenu]], ...])
{{=menu}}
"""
tag = 'ul'
@@ -2503,9 +2572,10 @@ def embed64(
"""
helper to encode the provided (binary) data into base64.
:param filename: if provided, opens and reads this file in 'rb' mode
:param file: if provided, reads this file
:param data: if provided, uses the provided data
Args:
filename: if provided, opens and reads this file in 'rb' mode
file: if provided, reads this file
data: if provided, uses the provided data
"""
if filename and os.path.exists(file):

View File

@@ -2,9 +2,12 @@
# -*- coding: utf-8 -*-
"""
This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
| This file is part of the web2py Web Framework
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
HTTP statuses helpers
--------------------------------------------
"""
import re
@@ -58,6 +61,18 @@ defined_status = {
regex_status = re.compile('^\d{3} [0-9A-Z ]+$')
class HTTP(Exception):
"""Raises an HTTP response
Args:
status: usually an integer. If it's a well known status code, the ERROR
message will be automatically added. A string can also be passed
as `510 Foo Bar` and in that case the status code and the error
message will be parsed accordingly
body: what to return as body. If left as is, will return the error code
and the status message in the body itself
cookies: pass cookies along (usually not needed)
headers: pass headers as usual dict mapping
"""
def __init__(
self,
@@ -136,6 +151,14 @@ class HTTP(Exception):
def redirect(location='', how=303, client_side=False):
"""Raises a redirect (303)
Args:
location: the url where to redirect
how: what HTTP status code to use when redirecting
client_side: if set to True, it triggers a reload of the entire page
when the fragment has been loaded as a component
"""
if location:
from gluon import current
loc = location.replace('\r', '%0D').replace('\n', '%0A')

View File

@@ -2,12 +2,13 @@
# -*- coding: utf-8 -*-
"""
This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
| This file is part of the web2py Web Framework
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
| Plural subsystem is created by Vladyslav Kozlovskyy (Ukraine) <dbdevelop@gmail.com>
Plural subsystem is created by Vladyslav Kozlovskyy (Ukraine)
<dbdevelop@gmail.com>
Translation system
--------------------------------------------
"""
import os
@@ -27,7 +28,6 @@ from gluon.portalocker import read_locked, LockedFile
from utf8 import Utf8
from gluon.fileutils import listdir
import gluon.settings as settings
from gluon.cfs import getcfs
from gluon.html import XML, xmlescape
from gluon.contrib.markmin.markmin2html import render, markmin_escape
@@ -41,7 +41,6 @@ pjoin = os.path.join
pexists = os.path.exists
pdirname = os.path.dirname
isdir = os.path.isdir
is_gae = False # settings.global_settings.web2py_runtime_gae
DEFAULT_LANGUAGE = 'en'
DEFAULT_LANGUAGE_NAME = 'English'
@@ -76,6 +75,10 @@ regex_plural_tuple = re.compile(
'^{(?P<w>[^[\]()]+)(?:\[(?P<i>\d+)\])?}$') # %%{word[index]} or %%{word}
regex_plural_file = re.compile('^plural-[a-zA-Z]{2}(-[a-zA-Z]{2})?\.py$')
def is_writable():
""" returns True if and only if the filesystem is writable """
from gluon.settings import global_settings
return not global_settings.web2py_runtime_gae
def safe_eval(text):
if text.strip():
@@ -165,7 +168,7 @@ def read_dict_aux(filename):
def read_dict(filename):
""" return dictionary with translation messages
""" Returns dictionary with translation messages
"""
return getcfs('lang:' + filename, filename,
lambda: read_dict_aux(filename))
@@ -173,8 +176,8 @@ def read_dict(filename):
def read_possible_plural_rules():
"""
create list of all possible plural rules files
result is cached in PLURAL_RULES dictionary to increase speed
Creates list of all possible plural rules files
The result is cached in PLURAL_RULES dictionary to increase speed
"""
plurals = {}
try:
@@ -294,41 +297,49 @@ def read_plural_dict(filename):
def write_plural_dict(filename, contents):
if '__corrupted__' in contents:
return
fp = None
try:
fp = LockedFile(filename, 'w')
fp.write('#!/usr/bin/env python\n# -*- coding: utf-8 -*-\n{\n# "singular form (0)": ["first plural form (1)", "second plural form (2)", ...],\n')
for key in sorted(contents, lambda x, y: cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())):
for key in sorted(contents, sort_function):
forms = '[' + ','.join([repr(Utf8(form))
for form in contents[key]]) + ']'
for form in contents[key]]) + ']'
fp.write('%s: %s,\n' % (repr(Utf8(key)), forms))
fp.write('}\n')
except (IOError, OSError):
if not is_gae:
if is_writable():
logging.warning('Unable to write to file %s' % filename)
return
finally:
fp.close()
if fp:
fp.close()
def sort_function(x,y):
return cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())
def write_dict(filename, contents):
if '__corrupted__' in contents:
return
fp = None
try:
fp = LockedFile(filename, 'w')
fp.write('# -*- coding: utf-8 -*-\n{\n')
for key in sorted(contents, sort_function):
fp.write('%s: %s,\n' % (repr(Utf8(key)),
repr(Utf8(contents[key]))))
fp.write('}\n')
except (IOError, OSError):
if not settings.global_settings.web2py_runtime_gae:
if is_writable():
logging.warning('Unable to write to file %s' % filename)
return
fp.write('# -*- coding: utf-8 -*-\n{\n')
for key in sorted(contents, lambda x, y: cmp(unicode(x, 'utf-8').lower(), unicode(y, 'utf-8').lower())):
fp.write('%s: %s,\n' % (repr(Utf8(key)), repr(Utf8(contents[key]))))
fp.write('}\n')
fp.close()
finally:
if fp:
fp.close()
class lazyT(object):
"""
never to be called explicitly, returned by
Never to be called explicitly, returned by
translator.__call__() or translator.M()
"""
m = s = T = f = t = None
@@ -428,28 +439,27 @@ copy_reg.pickle(lazyT, pickle_lazyT)
class translator(object):
"""
this class is instantiated by gluon.compileapp.build_environment
This class is instantiated by gluon.compileapp.build_environment
as the T object
::
Example:
T.force(None) # turns off translation
T.force('fr, it') # forces web2py to translate using fr.py or it.py
T(\"Hello World\") # translates \"Hello World\" using the selected file
T("Hello World") # translates "Hello World" using the selected file
notice 1: there is no need to force since, by default, T uses
http_accept_language to determine a translation file.
notice 2:
en and en-en are considered different languages!
notice 3:
if language xx-yy is not found force() probes other similar
languages using such algorithm:
xx-yy.py -> xx.py -> xx-yy*.py -> xx*.py
Note:
- there is no need to force since, by default, T uses
http_accept_language to determine a translation file.
- en and en-en are considered different languages!
- if language xx-yy is not found force() probes other similar languages
using such algorithm: `xx-yy.py -> xx.py -> xx-yy*.py -> xx*.py`
"""
def __init__(self, langpath, http_accept_language):
self.langpath = langpath
self.http_accept_language = http_accept_language
self.is_writable = not is_gae
# filled in self.force():
#------------------------
# self.cache
@@ -473,25 +483,26 @@ class translator(object):
self.otherTs = {}
self.filter = markmin
self.ftag = 'markmin'
self.ns = None
def get_possible_languages_info(self, lang=None):
"""
return info for selected language or dictionary with all
possible languages info from APP/languages/*.py
args:
*lang* (str): language
returns:
if *lang* is defined:
return tuple(langcode, langname, langfile_mtime,
pluraldict_fname, pluraldict_mtime,
prules_langcode, nplurals,
get_plural_id, construct_plural_form)
or None
Returns info for selected language or dictionary with all
possible languages info from `APP/languages/*.py`
It Returns:
- a tuple containing::
langcode, langname, langfile_mtime,
pluraldict_fname, pluraldict_mtime,
prules_langcode, nplurals,
get_plural_id, construct_plural_form
or None
- if *lang* is NOT defined a dictionary with all possible
languages::
if *lang* is NOT defined:
returns dictionary with all possible languages:
{ langcode(from filename):
( langcode, # language code from !langcode!
langname,
@@ -504,6 +515,10 @@ class translator(object):
get_plural_id, # get_plural_id() for current language
construct_plural_form) # construct_plural_form() for current language
}
Args:
lang (str): language
"""
info = read_possible_languages(self.langpath)
if lang:
@@ -511,19 +526,18 @@ class translator(object):
return info
def get_possible_languages(self):
""" get list of all possible languages for current applications """
""" Gets list of all possible languages for current application """
return list(set(self.current_languages +
[lang for lang in read_possible_languages(self.langpath).iterkeys()
if lang != 'default']))
def set_current_languages(self, *languages):
"""
set current AKA "default" languages
setting one of this languages makes force() function
turn translation off to use default language
Sets current AKA "default" languages
Setting one of this languages makes the force() function to turn
translation off
"""
if len(languages) == 1 and isinstance(
languages[0], (tuple, list)):
if len(languages) == 1 and isinstance(languages[0], (tuple, list)):
languages = languages[0]
if not languages or languages[0] is None:
# set default language from default.py/DEFAULT_LANGUAGE
@@ -543,17 +557,19 @@ class translator(object):
self.force(self.http_accept_language)
def plural(self, word, n):
""" get plural form of word for number *n*
NOTE: *word" MUST be defined in current language
(T.accepted_language)
""" Gets plural form of word for number *n*
invoked from T()/T.M() in `%%{}` tag
invoked from T()/T.M() in %%{} tag
args:
word (str): word in singular
n (numeric): number plural form created for
Args:
word (str): word in singular
n (numeric): number plural form created for
Returns:
word (str): word in appropriate singular/plural form
Note:
"word" MUST be defined in current language (T.accepted_language)
returns:
(str): word in appropriate singular/plural form
"""
if int(n) == 1:
return word
@@ -574,7 +590,7 @@ class translator(object):
form = self.construct_plural_form(word, id)
forms[id - 1] = form
self.plural_dict[word] = forms
if self.is_writable and self.plural_file:
if is_writable() and self.plural_file:
write_plural_dict(self.plural_file,
self.plural_dict)
return form
@@ -582,11 +598,10 @@ class translator(object):
def force(self, *languages):
"""
select language(s) for translation
Selects language(s) for translation
if a list of languages is passed as a parameter,
first language from this list that matches the ones
the first language from this list that matches the ones
from the possible_languages dictionary will be
selected
@@ -691,11 +706,11 @@ class translator(object):
self.ns = ns
otherT = self.__get_otherT__(language, ns)
return otherT(message, symbols, lazy=lazy)
def __get_otherT__(self, language=None, namespace=None):
if not language and not namespace:
raise Exception('Incorrect parameters')
if namespace:
if language:
index = '%s/%s' % (namespace, language)
@@ -742,7 +757,7 @@ class translator(object):
def M(self, message, symbols={}, language=None,
lazy=None, filter=None, ftag=None, ns=None):
"""
get cached translated markmin-message with inserted parametes
Gets cached translated markmin-message with inserted parametes
if lazy==True lazyT object is returned
"""
if lazy is None:
@@ -760,16 +775,16 @@ class translator(object):
def get_t(self, message, prefix=''):
"""
user ## to add a comment into a translation string
Use ## to add a comment into a translation string
the comment can be useful do discriminate different possible
translations for the same string (for example different locations)
translations for the same string (for example different locations)::
T(' hello world ') -> ' hello world '
T(' hello world ## token') -> ' hello world '
T('hello ## world## token') -> 'hello ## world'
T(' hello world ') -> ' hello world '
T(' hello world ## token') -> ' hello world '
T('hello ## world## token') -> 'hello ## world'
the ## notation is ignored in multiline strings and strings that
start with ##. this is to allow markmin syntax to be translated
start with ##. This is needed to allow markmin syntax to be translated
"""
if isinstance(message, unicode):
message = message.encode('utf8')
@@ -786,32 +801,38 @@ class translator(object):
# guess translation same as original
self.t[key] = mt = self.default_t.get(key, message)
# update language file for latter translation
if self.is_writable and self.language_file != self.default_language_file:
if is_writable() and \
self.language_file != self.default_language_file:
write_dict(self.language_file, self.t)
return regex_backslash.sub(
lambda m: m.group(1).translate(ttab_in), mt)
def params_substitution(self, message, symbols):
"""
substitute parameters from symbols into message using %.
also parse %%{} placeholders for plural-forms processing.
returns: string with parameters
NOTE: *symbols* MUST BE OR tuple OR dict of parameters!
Substitutes parameters from symbols into message using %.
also parse `%%{}` placeholders for plural-forms processing.
Returns:
string with parameters
Note:
*symbols* MUST BE OR tuple OR dict of parameters!
"""
def sub_plural(m):
"""string in %{} is transformed by this rules:
If string starts with \\, ! or ? such transformations
take place:
"""String in `%{}` is transformed by this rules:
If string starts with `\\`, `!` or `?` such transformations
take place::
"!string of words" -> "String of word" (Capitalize)
"!!string of words" -> "String Of Word" (Title)
"!!!string of words" -> "STRING OF WORD" (Upper)
"\\!string of words" -> "!string of word"
(remove \\ and disable transformations)
"?word?number" -> "word" (return word, if number == 1)
"?number" or "??number" -> "" (remove number,
if number == 1)
"?word?number" -> "number" (if number != 1)
"!string of words" -> "String of word" (Capitalize)
"!!string of words" -> "String Of Word" (Title)
"!!!string of words" -> "STRING OF WORD" (Upper)
"\\!string of words" -> "!string of word"
(remove \\ and disable transformations)
"?word?number" -> "word" (return word, if number == 1)
"?number" or "??number" -> "" (remove number,
if number == 1)
"?word?number" -> "number" (if number != 1)
"""
def sub_tuple(m):
""" word[number], !word[number], !!word[number], !!!word[number]
@@ -894,7 +915,7 @@ class translator(object):
def translate(self, message, symbols):
"""
get cached translated message with inserted parameters(symbols)
Gets cached translated message with inserted parameters(symbols)
"""
message = get_from_cache(self.cache, message,
lambda: self.get_t(message))
@@ -917,7 +938,8 @@ class translator(object):
def findT(path, language=DEFAULT_LANGUAGE):
"""
must be run by the admin app
Note:
Must be run by the admin app
"""
lang_file = pjoin(path, 'languages', language + '.py')
sentences = read_dict(lang_file)
@@ -954,6 +976,10 @@ def findT(path, language=DEFAULT_LANGUAGE):
write_dict(lang_file, sentences)
def update_all_languages(application_path):
"""
Note:
Must be run by the admin app
"""
path = pjoin(application_path, 'languages/')
for language in oslistdir(path):
if regex_langfile.match(language):

View File

@@ -2,14 +2,12 @@
# -*- coding: utf-8 -*-
"""
This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Contains:
- wsgibase: the gluon wsgi application
| This file is part of the web2py Web Framework
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
The gluon wsgi application
---------------------------
"""
if False: import import_all # DO NOT REMOVE PART OF FREEZE PROCESS
@@ -135,9 +133,9 @@ HTTPS_SCHEMES = set(('https', 'HTTPS'))
def get_client(env):
"""
guess the client address from the environment variables
Guesses the client address from the environment variables
first tries 'http_x_forwarded_for', secondly 'remote_addr'
First tries 'http_x_forwarded_for', secondly 'remote_addr'
if all fails, assume '127.0.0.1' or '::1' (running locally)
"""
eget = env.get
@@ -160,7 +158,7 @@ def get_client(env):
def serve_controller(request, response, session):
"""
this function is used to generate a dynamic page.
This function is used to generate a dynamic page.
It first runs all models, then runs the function in the controller,
and then tries to render the output using a view/template.
this function must run from the [application] folder.
@@ -234,7 +232,7 @@ class LazyWSGI(object):
return self._environ
def start_response(self,status='200', headers=[], exec_info=None):
"""
in controller you can use::
in controller you can use:
- request.wsgi.environ
- request.wsgi.start_response
@@ -249,7 +247,7 @@ class LazyWSGI(object):
"""
In you controller use::
@request.wsgi.middleware(middleware1, middleware2, ...)
@request.wsgi.middleware(middleware1, middleware2, ...)
to decorate actions with WSGI middleware. actions must return strings.
uses a simulated environment so it may have weird behavior in some cases
@@ -271,9 +269,9 @@ class LazyWSGI(object):
def wsgibase(environ, responder):
"""
this is the gluon wsgi application. the first function called when a page
is requested (static or dynamic). it can be called by paste.httpserver
or by apache mod_wsgi.
The gluon wsgi application. The first function called when a page
is requested (static or dynamic). It can be called by paste.httpserver
or by apache mod_wsgi (or any WSGI-compatible server).
- fills request with info
- the environment variables, replacing '.' with '_'
@@ -290,13 +288,11 @@ def wsgibase(environ, responder):
2. for dynamic pages:
- /<application>[/<controller>[/<function>[/<sub>]]][.<extension>]
- (sub may go several levels deep, currently 3 levels are supported:
sub1/sub2/sub3)
The naming conventions are:
- application, controller, function and extension may only contain
[a-zA-Z0-9_]
`[a-zA-Z0-9_]`
- file and sub may also contain '-', '=', '.' and '/'
"""
eget = environ.get
@@ -567,7 +563,7 @@ def wsgibase(environ, responder):
def save_password(password, port):
"""
used by main() to save the password in the parameters_port.py file.
Used by main() to save the password in the parameters_port.py file.
"""
password_file = abspath('parameters_%i.py' % port)
@@ -607,10 +603,10 @@ def appfactory(wsgiapp=wsgibase,
generates a wsgi application that does logging and profiling and calls
wsgibase
.. function:: gluon.main.appfactory(
[wsgiapp=wsgibase
[, logfilename='httpserver.log'
[, profilerfilename='profiler.log']]])
Args:
wsgiapp: the base application
logfilename: where to store apache-compatible requests log
profiler_dir: where to store profile files
"""
if profilerfilename is not None:

View File

@@ -2,9 +2,12 @@
# -*- coding: utf-8 -*-
"""
This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
| This file is part of the web2py Web Framework
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Useful regexes
---------------
"""
import re

View File

@@ -2,8 +2,12 @@
# -*- coding: utf-8 -*-
"""
Created by Attila Csipa <web2py@csipa.in.rs>
Modified by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| This file is part of the web2py Web Framework
| Created by Attila Csipa <web2py@csipa.in.rs>
| Modified by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Cron-style interface
"""
import sys
@@ -27,7 +31,7 @@ _cron_subprocs = []
def absolute_path_link(path):
"""
Return an absolute path for the destination of a symlink
Returns an absolute path for the destination of a symlink
"""
if os.path.islink(path):
@@ -40,7 +44,7 @@ def absolute_path_link(path):
def stopcron():
"graceful shutdown of cron"
"Graceful shutdown of cron"
global _cron_stopping
_cron_stopping = True
while _cron_subprocs:
@@ -107,7 +111,7 @@ class Token(object):
def acquire(self, startup=False):
"""
returns the time when the lock is acquired or
Returns the time when the lock is acquired or
None if cron already running
lock is implemented by writing a pickle (start, stop) in cron.master
@@ -150,8 +154,7 @@ class Token(object):
def release(self):
"""
this function writes into cron.master the time when cron job
was completed
Writes into cron.master the time when cron job was completed
"""
if not self.master.closed:
portalocker.lock(self.master, portalocker.LOCK_EX)

View File

@@ -1,13 +1,10 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# portalocker.py
# Cross-platform (posix/nt) API for flock-style file locking.
# Requires python 1.5.2 or better.
"""
Cross-platform (posix/nt) API for flock-style file locking.
Synopsis:
Synopsis::
import portalocker
file = open(\"somefile\", \"r+\")
@@ -16,18 +13,18 @@ Synopsis:
file.write(\"foo\")
file.close()
If you know what you're doing, you may choose to
If you know what you're doing, you may choose to::
portalocker.unlock(file)
before closing the file, but why?
Methods:
Methods::
lock( file, flags )
unlock( file )
Constants:
Constants::
LOCK_EX
LOCK_SH
@@ -163,6 +160,7 @@ def write_locked(filename, data):
fp.close()
if __name__ == '__main__':
import sys
f = LockedFile('test.txt', mode='wb')
f.write('test ok')
f.close()

View File

@@ -22,6 +22,7 @@ __author__ = "Thadeus Burgess <thadeusb@thadeusb.com>"
# These are keywords that are common to all SQL dialects, and should
# never be used as a table or column. Even if you use one of these
# the cursor will throw an OperationalError for the SQL syntax.
COMMON = set((
'SELECT',
'INSERT',

View File

@@ -2,9 +2,12 @@
# -*- coding: utf-8 -*-
"""
This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
| This file is part of the web2py Web Framework
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Restricted environment to execute application's code
-----------------------------------------------------
"""
import sys
@@ -26,7 +29,7 @@ __all__ = ['RestrictedError', 'restricted', 'TicketStorage', 'compile2']
class TicketStorage(Storage):
"""
defines the ticket object and the default values of its members (None)
Defines the ticket object and the default values of its members (None)
"""
def __init__(
@@ -40,7 +43,7 @@ class TicketStorage(Storage):
def store(self, request, ticket_id, ticket_data):
"""
stores the ticket. It will figure out if this must be on disk or in db
Stores the ticket. It will figure out if this must be on disk or in db
"""
if self.db:
self._store_in_db(request, ticket_id, ticket_data)
@@ -51,12 +54,12 @@ class TicketStorage(Storage):
self.db._adapter.reconnect()
try:
table = self._get_table(self.db, self.tablename, request.application)
id = table.insert(ticket_id=ticket_id,
table.insert(ticket_id=ticket_id,
ticket_data=cPickle.dumps(ticket_data),
created_datetime=request.now)
self.db.commit()
message = 'In FILE: %(layer)s\n\n%(traceback)s\n'
except Exception, e:
except Exception:
self.db.rollback()
message =' Unable to store in FILE: %(layer)s\n\n%(traceback)s\n'
self.db.close()
@@ -111,8 +114,8 @@ class TicketStorage(Storage):
class RestrictedError(Exception):
"""
class used to wrap an exception that occurs in the restricted environment
below. the traceback is used to log the exception and generate a ticket.
Class used to wrap an exception that occurs in the restricted environment
below. The traceback is used to log the exception and generate a ticket.
"""
def __init__(
@@ -123,7 +126,7 @@ class RestrictedError(Exception):
environment=None,
):
"""
layer here is some description of where in the system the exception
Layer here is some description of where in the system the exception
occurred.
"""
if environment is None:
@@ -148,7 +151,7 @@ class RestrictedError(Exception):
def log(self, request):
"""
logs the exception.
Logs the exception.
"""
try:
@@ -168,7 +171,7 @@ class RestrictedError(Exception):
def load(self, request, app, ticket_id):
"""
loads a logged exception.
Loads a logged exception.
"""
ticket_storage = TicketStorage(db=request.tickets_db)
d = ticket_storage.load(request, app, ticket_id)
@@ -201,8 +204,8 @@ def compile2(code, layer):
def restricted(code, environment=None, layer='Unknown'):
"""
runs code in environment and returns the output. if an exception occurs
in code it raises a RestrictedError containing the traceback. layer is
Runs code in environment and returns the output. If an exception occurs
in code it raises a RestrictedError containing the traceback. Layer is
passed to RestrictedError to identify where the error occurred.
"""
if environment is None:

View File

@@ -2,9 +2,9 @@
# -*- coding: utf-8 -*-
"""
This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
| This file is part of the web2py Web Framework
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
gluon.rewrite parses incoming URLs and formats outgoing URLs for gluon.html.URL.
@@ -57,7 +57,7 @@ regex_args = re.compile('[^\w/.@=-]')
def _router_default():
"return new copy of default base router"
"Returns new copy of default base router"
router = Storage(
default_application='init',
applications='ALL',
@@ -85,7 +85,7 @@ def _router_default():
def _params_default(app=None):
"return new copy of default parameters"
"Returns a new copy of default parameters"
p = Storage()
p.name = app or "BASE"
p.default_application = app or "init"
@@ -181,7 +181,7 @@ def fixup_missing_path_info(environ):
def url_in(request, environ):
"parse and rewrite incoming URL"
"Parses and rewrites incoming URL"
if routers:
return map_url_in(request, environ)
return regex_url_in(request, environ)
@@ -189,7 +189,7 @@ def url_in(request, environ):
def url_out(request, environ, application, controller, function,
args, other, scheme, host, port, language=None):
"assemble and rewrite outgoing URL"
"Assembles and rewrites outgoing URL"
if routers:
acf = map_url_out(request, environ, application, controller,
function, args, other, scheme, host, port, language)
@@ -214,7 +214,7 @@ def url_out(request, environ, application, controller, function,
def try_rewrite_on_error(http_response, request, environ, ticket=None):
"""
called from main.wsgibase to rewrite the http response.
Called from main.wsgibase to rewrite the http response.
"""
status = int(str(http_response.status).split()[0])
if status >= 399 and THREAD_LOCAL.routes.routes_onerror:
@@ -255,7 +255,7 @@ def try_rewrite_on_error(http_response, request, environ, ticket=None):
def try_redirect_on_error(http_object, request, ticket=None):
"called from main.wsgibase to rewrite the http response"
"Called from main.wsgibase to rewrite the http response"
status = int(str(http_object.status).split()[0])
if status > 399 and THREAD_LOCAL.routes.routes_onerror:
keys = set(('%s/%s' % (request.application, status),
@@ -392,7 +392,7 @@ def load(routes='routes.py', app=None, data=None, rdict=None):
def compile_regex(k, v, env=None):
"""
Preprocess and compile the regular expressions in routes_app/in/out
The resulting regex will match a pattern of the form:
The resulting regex will match a pattern of the form::
[remote address]:[protocol]://[host]:[method] [path]
@@ -427,7 +427,7 @@ def compile_regex(k, v, env=None):
def load_routers(all_apps):
"load-time post-processing of routers"
"Load-time post-processing of routers"
for app in routers:
# initialize apps with routers that aren't present,
@@ -533,7 +533,7 @@ def load_routers(all_apps):
def regex_uri(e, regexes, tag, default=None):
"filter incoming URI against a list of regexes"
"Filters incoming URI against a list of regexes"
path = e['PATH_INFO']
host = e.get('HTTP_HOST', e.get('SERVER_NAME', 'localhost')).lower()
i = host.find(':')
@@ -555,7 +555,7 @@ def regex_uri(e, regexes, tag, default=None):
def regex_select(env=None, app=None, request=None):
"""
select a set of regex rewrite params for the current request
Selects a set of regex rewrite params for the current request
"""
if app:
THREAD_LOCAL.routes = params_apps.get(app, params)
@@ -572,7 +572,7 @@ def regex_select(env=None, app=None, request=None):
def regex_filter_in(e):
"regex rewrite incoming URL"
"Regex rewrite incoming URL"
routes = THREAD_LOCAL.routes
query = e.get('QUERY_STRING', None)
e['WEB2PY_ORIGINAL_URI'] = e['PATH_INFO'] + (query and ('?' + query) or '')
@@ -603,7 +603,7 @@ def invalid_url(routes):
web2py_error='invalid path')
def regex_url_in(request, environ):
"rewrite and parse incoming URL"
"Rewrites and parses incoming URL"
# ##################################################
# select application
@@ -667,7 +667,7 @@ def regex_url_in(request, environ):
def regex_filter_out(url, e=None):
"regex rewrite outgoing URL"
"Regex rewrite outgoing URL"
if not hasattr(THREAD_LOCAL, 'routes'):
regex_select() # ensure routes is set (for application threads)
routes = THREAD_LOCAL.routes
@@ -810,10 +810,10 @@ def filter_err(status, application='app', ticket='tkt'):
class MapUrlIn(object):
"logic for mapping incoming URLs"
"Logic for mapping incoming URLs"
def __init__(self, request=None, env=None):
"initialize a map-in object"
"Initializes a map-in object"
self.request = request
self.env = env
@@ -861,7 +861,7 @@ class MapUrlIn(object):
self.port = '443' if self.scheme == 'https' else '80'
def map_prefix(self):
"strip path prefix, if present in its entirety"
"Strips path prefix, if present in its entirety"
prefix = routers.BASE.path_prefix
if prefix:
prefixlen = len(prefix)
@@ -873,7 +873,7 @@ class MapUrlIn(object):
self.args = List(self.args[prefixlen:]) # strip the prefix
def map_app(self):
"determine application name"
"Determines application name"
base = routers.BASE # base router
self.domain_application = None
self.domain_controller = None
@@ -935,12 +935,12 @@ class MapUrlIn(object):
self._args_match = self.router._args_match
def map_root_static(self):
'''
handle root-static files (no hyphen mapping)
"""
Handles root-static files (no hyphen mapping)
a root-static file is one whose incoming URL expects it to be at the root,
typically robots.txt & favicon.ico
'''
"""
if len(self.args) == 1 and self.arg0 in self.router.root_static:
self.controller = self.request.controller = 'static'
@@ -952,7 +952,7 @@ class MapUrlIn(object):
return None, None
def map_language(self):
"handle language (no hyphen mapping)"
"Handles language (no hyphen mapping)"
arg0 = self.arg0 # no hyphen mapping
if arg0 and self.languages and arg0 in self.languages:
self.language = arg0
@@ -964,7 +964,7 @@ class MapUrlIn(object):
arg0 = self.arg0
def map_controller(self):
"identify controller"
"Identifies controller"
# handle controller
#
arg0 = self.harg0 # map hyphens
@@ -981,7 +981,7 @@ class MapUrlIn(object):
def map_static(self):
'''
handle static files
Handles static files
file_match but no hyphen mapping
'''
if self.controller != 'static':
@@ -1025,7 +1025,7 @@ class MapUrlIn(object):
return static_file, version
def map_function(self):
"handle function.extension"
"Handles function.extension"
arg0 = self.harg0 # map hyphens
functions = self.functions.get(self.controller, set())
if isinstance(self.router.default_function, dict):
@@ -1058,9 +1058,9 @@ class MapUrlIn(object):
web2py_error='invalid extension')
def validate_args(self):
'''
check args against validation pattern
'''
"""
Checks args against validation pattern
"""
for arg in self.args:
if not self.router._args_match.match(arg):
raise HTTP(
@@ -1068,16 +1068,15 @@ class MapUrlIn(object):
web2py_error='invalid arg <%s>' % arg)
def sluggify(self):
""
self.request.env.update(
(k.lower().replace('.', '_'), v) for k, v in self.env.iteritems())
def update_request(self):
'''
update request from self
build env.request_uri
make lower-case versions of http headers in env
'''
"""
Updates request from self
Builds env.request_uri
Makes lower-case versions of http headers in env
"""
self.request.application = self.application
self.request.controller = self.controller
self.request.function = self.function
@@ -1105,24 +1104,24 @@ class MapUrlIn(object):
@property
def arg0(self):
"return first arg"
"Returns first arg"
return self.args(0)
@property
def harg0(self):
"return first arg with optional hyphen mapping"
"Returns first arg with optional hyphen mapping"
if self.map_hyphen and self.args(0):
return self.args(0).replace('-', '_')
return self.args(0)
def pop_arg_if(self, dopop):
"conditionally remove first arg and return new first arg"
"Conditionally removes first arg and returns new first arg"
if dopop:
self.args.pop(0)
class MapUrlOut(object):
"logic for mapping outgoing URLs"
"Logic for mapping outgoing URLs"
def __init__(self, request, env, application, controller,
function, args, other, scheme, host, port, language):
@@ -1179,13 +1178,13 @@ class MapUrlOut(object):
self.omit_function = False
def omit_lang(self):
"omit language if possible"
"Omits language if possible"
if not self.language or self.language == self.default_language:
self.omit_language = True
def omit_acf(self):
"omit what we can of a/c/f"
"Omits what we can of a/c/f"
router = self.router
@@ -1254,7 +1253,7 @@ class MapUrlOut(object):
self.omit_function = False
def build_acf(self):
"build acf from components"
"Builds a/c/f from components"
acf = ''
if self.map_hyphen:
self.application = self.application.replace('_', '-')
@@ -1280,7 +1279,7 @@ class MapUrlOut(object):
return acf or '/'
def acf(self):
"convert components to /app/lang/controller/function"
"Converts components to /app/lang/controller/function"
if not routers:
return None # use regex filter
@@ -1290,7 +1289,7 @@ class MapUrlOut(object):
def map_url_in(request, env, app=False):
"route incoming URL"
"Routes incoming URL"
# initialize router-url object
#
@@ -1333,8 +1332,8 @@ def map_url_in(request, env, app=False):
def map_url_out(request, env, application, controller,
function, args, other, scheme, host, port, language=None):
'''
supply /a/c/f (or /a/lang/c/f) portion of outgoing url
"""
Supply /a/c/f (or /a/lang/c/f) portion of outgoing url
The basic rule is that we can only make transformations
that map_url_in can reverse.
@@ -1342,14 +1341,14 @@ def map_url_out(request, env, application, controller,
Suppose that the incoming arguments are a,c,f,args,lang
and that the router defaults are da, dc, df, dl.
We can perform these transformations trivially if args=[] and lang=None or dl:
We can perform these transformations trivially if args=[] and lang=None or dl::
/da/dc/df => /
/a/dc/df => /a
/a/c/df => /a/c
/da/dc/df => /
/a/dc/df => /a
/a/c/df => /a/c
We would also like to be able to strip the default application or application/controller
from URLs with function/args present, thus:
from URLs with function/args present, thus::
/da/c/f/args => /c/f/args
/da/dc/f/args => /f/args
@@ -1357,14 +1356,14 @@ def map_url_out(request, env, application, controller,
We use [applications] and [controllers] and {functions} to suppress ambiguous omissions.
We assume that language names do not collide with a/c/f names.
'''
"""
map = MapUrlOut(request, env, application, controller,
function, args, other, scheme, host, port, language)
return map.acf()
def get_effective_router(appname):
"return a private copy of the effective router for the specified application"
"Returns a private copy of the effective router for the specified application"
if not routers or appname not in routers:
return None
return Storage(routers[appname]) # return a copy

View File

@@ -2599,8 +2599,11 @@ class SQLFORM(FORM):
for k, v in sorted(exportManager.items()):
if not v:
continue
label = v[1] if hasattr(v, "__getitem__") else k
title = v[2] if hasattr(v, "__getitem__") else label
if hasattr(v, "__getitem__"):
label = v[1]
title = v[2] if len(v)>2 else label
else:
label = title = k
link = url2(vars=dict(
order=request.vars.order or '',
_export_type=k,
@@ -2926,7 +2929,8 @@ class SQLTABLE(TABLE):
if not sqlrows:
return
if not columns:
columns = ['.'.join(sqlrows.db._adapter.REGEX_TABLE_DOT_FIELD.match(c).groups()) for c in sqlrows.colnames]
REGEX_TABLE_DOT_FIELD = sqlrows.db._adapter.REGEX_TABLE_DOT_FIELD
columns = [c for c in sqlrows.colnames if REGEX_TABLE_DOT_FIELD.match(c)]
if headers == 'fieldname:capitalize':
headers = {}
for c in columns:

View File

@@ -757,7 +757,8 @@ class Recaptcha(DIV):
error_message='invalid',
label='Verify:',
options='',
comment = ''
comment = '',
ajax=False
):
self.request_vars = request and request.vars or current.request.vars
self.remote_addr = request.env.remote_addr
@@ -772,6 +773,7 @@ class Recaptcha(DIV):
self.label = label
self.options = options
self.comment = comment
self.ajax = ajax
def _validate(self):
@@ -825,18 +827,43 @@ class Recaptcha(DIV):
server = self.API_SSL_SERVER
else:
server = self.API_SERVER
captcha = DIV(
SCRIPT("var RecaptchaOptions = {%s};" % self.options),
SCRIPT(_type="text/javascript",
_src="%s/challenge?k=%s%s" % (server, public_key, error_param)),
TAG.noscript(
IFRAME(
_src="%s/noscript?k=%s%s" % (
server, public_key, error_param),
_height="300", _width="500", _frameborder="0"), BR(),
INPUT(
_type='hidden', _name='recaptcha_response_field',
_value='manual_challenge')), _id='recaptcha')
if not self.ajax:
captcha = DIV(
SCRIPT("var RecaptchaOptions = {%s};" % self.options),
SCRIPT(_type="text/javascript",
_src="%s/challenge?k=%s%s" % (server, public_key, error_param)),
TAG.noscript(
IFRAME(
_src="%s/noscript?k=%s%s" % (
server, public_key, error_param),
_height="300", _width="500", _frameborder="0"), BR(),
INPUT(
_type='hidden', _name='recaptcha_response_field',
_value='manual_challenge')), _id='recaptcha')
else: #use Google's ajax interface, needed for LOADed components
url_recaptcha_js = "%s/js/recaptcha_ajax.js" % server
RecaptchaOptions = "var RecaptchaOptions = {%s}" % self.options
script = """%(options)s;
jQuery.getScript('%(url)s',function() {
Recaptcha.create('%(public_key)s',
'recaptcha',jQuery.extend(RecaptchaOptions,{'callback':Recaptcha.focus_response_field}))
}) """ % ({'options':RecaptchaOptions,'url':url_recaptcha_js,'public_key':public_key})
captcha = DIV(
SCRIPT(
script,
_type="text/javascript",
),
TAG.noscript(
IFRAME(
_src="%s/noscript?k=%s%s" % (
server, public_key, error_param),
_height="300", _width="500", _frameborder="0"), BR(),
INPUT(
_type='hidden', _name='recaptcha_response_field',
_value='manual_challenge')), _id='recaptcha')
if not self.errors.captcha:
return XML(captcha).xml()
else:
@@ -2035,8 +2062,9 @@ class Auth(object):
if user and user.get(settings.passfield, False):
password = settings.table_user[
settings.passfield].validate(password)[0]
if not user.registration_key.strip() and password == \
user[settings.passfield]:
if ((user.registration_key is None or
not user.registration_key.strip()) and
password == user[settings.passfield]):
self.login_user(user)
return user
else:
@@ -2322,8 +2350,8 @@ class Auth(object):
elif temp_user.registration_key in ('disabled', 'blocked'):
response.flash = self.messages.login_disabled
return form
elif not temp_user.registration_key is None and \
temp_user.registration_key.strip():
elif (not temp_user.registration_key is None
and temp_user.registration_key.strip()):
response.flash = \
self.messages.registration_verifying
return form

View File

@@ -17,10 +17,10 @@ import cgi
import urllib
import struct
import decimal
import unicodedata
from cStringIO import StringIO
from gluon.utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE
from gluon.dal import FieldVirtual, FieldMethod
from gluon.contrib import translitcodec
regex_isint = re.compile('^[+-]?\d+$')
@@ -2519,7 +2519,7 @@ def urlify(s, maxlen=80, keep_underscores=False):
if isinstance(s, str):
s = s.decode('utf-8') # to unicode
s = s.lower() # to lowercase
s = s.encode('translit/long') # replace special characters
s = unicodedata.normalize('NFKD', s) # replace special characters
s = s.encode('ascii', 'ignore') # encode as ASCII
s = re.sub('&\w+?;', '', s) # strip html entities
if keep_underscores:

View File

@@ -1222,12 +1222,12 @@ end tell
url = get_url(ip, proto=proto, port=port)
if not options.nobanner:
print 'please visit:'
print '\t', url
message = '\nplease visit:\n\t%s\n' % url
if sys.platform.startswith('win'):
print 'use "taskkill /f /pid %i" to shutdown the web2py server' % os.getpid()
message += 'use "taskkill /f /pid %i" to shutdown the web2py server\n\n' % os.getpid()
else:
print 'use "kill -SIGTERM %i" to shutdown the web2py server' % os.getpid()
message += 'use "kill -SIGTERM %i" to shutdown the web2py server\n\n' % os.getpid()
print message
# enhance linecache.getline (used by debugger) to look at the source file
# if the line was not found (under py2exe & when file was modified)