Compare commits

..

33 Commits

Author SHA1 Message Date
mdipierro
f7bf1020df reverted gluon/packages/dal 2015-04-18 15:28:12 -05:00
mdipierro
75b8ceb022 Merge pull request #923 from gi0baro/validators
Fix serializers injection over new pydal
2015-04-18 15:17:06 -05:00
mdipierro
b4f3784136 Merge pull request #922 from dokime7/patch-7
Fix crash with list:reference field
2015-04-18 15:16:25 -05:00
mdipierro
f1297bb827 Merge pull request #918 from niphlod/enhancement/anyserver
added waitress to anyserver
2015-04-18 15:15:02 -05:00
mdipierro
435ebeaae4 more consulting companies 2015-04-18 15:13:03 -05:00
gi0baro
537045082c Updated dal test due to new serializers 2015-04-18 15:08:39 +02:00
gi0baro
4bea52a7b5 Fix serializers injection over new pydal 2015-04-18 15:04:01 +02:00
Jeremie Dokime
33295e516f Fix crash with list:reference field
When using validate_and_update() or validate_and_insert() on a table with a list:reference field, the request crashes after timeout with:
  File "web2py/gluon/globals.py", line 270, in body
    raise HTTP(400, "Bad Request - HTTP body is incomplete")

The request crashes because there is an hidden exception with the isinstance function:
isinstance() arg 2 must be a class, type, or tuple of classes and types

When no using GAE, the GoogleDatastoreAdapter variable is None, so isinstance crash.
See the last line of pydal/adapters/__init__.py

This is a regression intruduced after the v2.9.12.
2015-04-18 02:15:45 +02:00
mdipierro
f33ccf3366 experimental fix for represent 2015-04-16 16:53:12 -05:00
niphlod
0784680c90 added waitress to anyserver 2015-04-15 23:55:44 +02:00
mdipierro
e940228eaf Merge pull request #912 from ilvalle/sqlcustomtype
extend sqlcustomform to support widget/represent
2015-04-15 13:01:50 -05:00
mdipierro
50769a627a Merge pull request #916 from gi0baro/issue-904
Fixes #904
2015-04-15 13:01:19 -05:00
mdipierro
e68ecaa131 Merge pull request #915 from BuhtigithuB/update/gluon-contrib-pypyodbc
Update gluon/contrib/pypyodbc.py 1.3.0 -> 1.3.3
2015-04-15 13:01:01 -05:00
gi0baro
95e6e8577b Fixes #904 2015-04-14 15:25:26 +02:00
Hardirc
19c83d4ad6 Update gluon/contrib/pypyodbc.py 1.3.0 -> 1.3.3 2015-04-13 18:43:48 -04:00
ilvalle
9c92bd1050 extend sqlcustomform to support widget/represent (possible fix to web2py/pydal#127) 2015-04-12 21:04:05 +02:00
mdipierro
b3b95ccf5f Merge pull request #906 from niphlod/fix/895
file is already open at this point... fixes #895
2015-04-08 23:24:16 -05:00
niphlod
cefa30841b file is already open at this point... fixes #895 2015-04-08 21:47:04 +02:00
mdipierro
15ff8669cb fixed dal tracking, thanks Niphlod 2015-04-06 16:24:49 -05:00
mdipierro
a921751e8e stripe form bs3 compatible, disabled cache.ram max utilization 2015-04-05 18:17:27 -05:00
mdipierro
b02e8a6d5f fixed problem with custom import and upgraded jquery.js 2015-04-02 16:30:15 -05:00
mdipierro
a2b17967cf Merge pull request #898 from ilvalle/issue-896
fix web2py/web2py#896, avoid to select blob fields in sqlform.grid
2015-04-02 16:24:26 -05:00
mdipierro
343ebf1714 Merge pull request #897 from BuhtigithuB/update-contrib/feedparser-py
Update gluon/contrib/feedparser.py from 5.1.2 -> 5.1.3
2015-04-02 16:23:34 -05:00
mdipierro
e330791fe3 Merge pull request #894 from niphlod/fix/windows_build
fix windows build
2015-04-02 16:22:49 -05:00
mdipierro
5e1e97ffc4 Merge pull request #893 from niphlod/fix/duplicate_module
not needed anymore module
2015-04-02 16:20:39 -05:00
ilvalle
60f8816a10 fix web2py/web2py#896, avoid to select blob fields in sqlform.grid 2015-04-02 21:38:05 +02:00
Richard Vézina
6187c89ba6 Update gluon/contrib/feedparser.py from 5.1.2 -> 5.1.3 2015-04-02 13:58:48 -04:00
niphlod
e441e15907 fix windows build 2015-04-01 21:35:52 +02:00
niphlod
e6c3410639 not needed anymore module 2015-04-01 20:55:47 +02:00
mdipierro
6b6cb5839c Merge branch 'master' of github.com:web2py/web2py 2015-04-01 13:55:07 -05:00
mdipierro
2a7e9c0c59 possible fix for issue #892 2015-04-01 13:54:48 -05:00
mdipierro
5b637c0a82 Merge pull request #891 from leonelcamara/cache_cleanup
Fixes issue with locked files during cleanup
2015-04-01 12:41:52 -05:00
Leonel Câmara
5cdd7c1215 Fixes issue with locked files during cleanup 2015-04-01 18:27:05 +01:00
21 changed files with 590 additions and 778 deletions

View File

@@ -32,7 +32,7 @@ update:
echo "remember that pymysql was tweaked"
src:
### Use semantic versioning
echo 'Version 2.10.1-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
echo 'Version 2.10.3-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.10.2-stable+timestamp.2015.03.31.22.25.23
Version 2.10.3-stable+timestamp.2015.04.02.16.28.49

View File

@@ -180,6 +180,11 @@ class Servers:
s = wsgi.WSGIServer(callable=app, bind="%s:%d" % address)
s.start()
@staticmethod
def waitress(app, address, **options):
from waitress import serve
serve(app, host=address[0], port=address[1], _quiet=True)
def mongrel2_handler(application, conn, debug=False):
"""

File diff suppressed because one or more lines are too long

View File

@@ -18,7 +18,7 @@
</ul>
<div class="tab-content">
<div class="tab-pane active" id="alltables">
<table>
<table class="table">
{{for db in sorted(databases):}}
{{for table in databases[db].tables:}}
{{qry='%s.%s.id>0'%(db,table)}}
@@ -40,7 +40,7 @@
{{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}}
</th>
<td>
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn btn-default")}}
</td>
</tr>
{{pass}}
@@ -61,7 +61,7 @@
</pre>
{{pass}}
{{if table:}}
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn btn-default")}}<br/><br/>
<h3>{{=T("Rows in Table")}}</h3><br/>
{{else:}}
<h3>{{=T("Rows selected")}}</h3><br/>
@@ -72,8 +72,8 @@
{{=T('"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN')}}</p>
<br/><br/>
<h4>{{=T("%s selected", nrows)}}</h4>
{{if start>0:}}{{=A(T('previous %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start-step)),_class="btn")}}{{pass}}
{{if stop<nrows:}}{{=A(T('next %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start+step)),_class="btn")}}{{pass}}
{{if start>0:}}{{=A(T('previous %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start-step)),_class="btn btn-default")}}{{pass}}
{{if stop<nrows:}}{{=A(T('next %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start+step)),_class="btn btn-default")}}{{pass}}
{{if rows:}}
<div style="overflow:auto; width:80%;">
{{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}}
@@ -82,7 +82,7 @@
</div>
{{pass}}
<br/><br/><h3>{{=T("Import/Export")}}</h3><br/>
<a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}" class="btn">{{=T("export as csv file")}}</a>
<a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}" class="btn btn-default">{{=T("export as csv file")}}</a>
{{=formcsv or ''}}
{{elif request.function=='insert':}}

View File

@@ -18,7 +18,7 @@
</ul>
<div class="tab-content">
<div class="tab-pane active" id="alltables">
<table>
<table class="table">
{{for db in sorted(databases):}}
{{for table in databases[db].tables:}}
{{qry='%s.%s.id>0'%(db,table)}}
@@ -40,7 +40,7 @@
{{=A("%s.%s" % (db,table),_href=URL('select',args=[db],vars=dict(query=qry)))}}
</th>
<td>
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn")}}
{{=A(str(T('New Record')),_href=URL('insert',args=[db,table]),_class="btn btn-default")}}
</td>
</tr>
{{pass}}
@@ -61,7 +61,7 @@
</pre>
{{pass}}
{{if table:}}
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn")}}<br/><br/>
{{=A(str(T('New Record')),_href=URL('insert',args=[request.args[0],table]),_class="btn btn-default")}}<br/><br/>
<h3>{{=T("Rows in Table")}}</h3><br/>
{{else:}}
<h3>{{=T("Rows selected")}}</h3><br/>
@@ -72,8 +72,8 @@
{{=T('"update" is an optional expression like "field1=\'newvalue\'". You cannot update or delete the results of a JOIN')}}</p>
<br/><br/>
<h4>{{=T("%s selected", nrows)}}</h4>
{{if start>0:}}{{=A(T('previous %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start-step)),_class="btn")}}{{pass}}
{{if stop<nrows:}}{{=A(T('next %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start+step)),_class="btn")}}{{pass}}
{{if start>0:}}{{=A(T('previous %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start-step)),_class="btn btn-default")}}{{pass}}
{{if stop<nrows:}}{{=A(T('next %s rows') % step,_href=URL('select',args=request.args[0],vars=dict(start=start+step)),_class="btn btn-default")}}{{pass}}
{{if rows:}}
<div style="overflow:auto; width:80%;">
{{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}}
@@ -82,7 +82,7 @@
</div>
{{pass}}
<br/><br/><h3>{{=T("Import/Export")}}</h3><br/>
<a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}" class="btn">{{=T("export as csv file")}}</a>
<a href="{{=URL('csv',args=request.args[0],vars=dict(query=query))}}" class="btn btn-default">{{=T("export as csv file")}}</a>
{{=formcsv or ''}}
{{elif request.function=='insert':}}

View File

@@ -33,6 +33,9 @@
<li><a target="_blank" href="https://loadinfo-net.appspot.com">LoadInfo</a> (Bulgaria)</li>
<li><a target="_blank" href="http://www.appliedobjects.com">Applied Objects</a> (New Zealand)</li>
<li><a target="_blank" href="http://www.sistemasagiles.com.ar/">Sistemas Ágiles</a> ("Agile Systems") (Argentina)</li>
<li><a target="_blank" href="http://www.definescope.com/en/services/consulting/">DefineScope</a> (Portugal)</li>
<li><a target="_blank" href="http://10Biosystems.com">10BioSystems</a></li>
<li><a target="_blank" href="http://www.dutveul.nl">Dutveul</a> (Netherlands)</li>
</ul>
</div>

View File

@@ -114,17 +114,18 @@ def app_cleanup(app, request):
for f in os.listdir(path):
try:
if f[:1] != '.': recursive_unlink(os.path.join(path, f))
except IOError:
except (OSError, IOError):
r = False
# Remove cache files
CacheOnDisk(folder=apath('%s/cache/' % app, request)).clear()
path = apath('%s/cache/' % app, request)
CacheOnDisk(folder=path).clear()
if os.path.exists(path):
for f in os.listdir(path):
try:
if f[:1] != '.': recursive_unlink(os.path.join(path, f))
except IOError:
except (OSError, IOError):
r = False
return r

View File

@@ -99,7 +99,7 @@ class CacheAbstract(object):
"""
cache_stats_name = 'web2py_cache_statistics'
max_ram_utilization = 90 # percent
max_ram_utilization = None # percent
def __init__(self, request=None):
"""Initializes the object
@@ -353,7 +353,7 @@ class CacheOnDisk(CacheAbstract):
raise KeyError
self.wait_portalock(val_file)
value = pickle.load(recfile.open(key, 'rb', path=self.folder))
value = pickle.load(val_file)
val_file.close()
return value

74
gluon/contrib/feedparser.py Executable file → Normal file
View File

@@ -9,7 +9,7 @@ Required: Python 2.4 or later
Recommended: iconv_codec <http://cjkpython.i18n.org/>
"""
__version__ = "5.1.2"
__version__ = "5.1.3"
__license__ = """
Copyright (c) 2010-2012 Kurt McKee <contactme@kurtmckee.org>
Copyright (c) 2002-2008 Mark Pilgrim
@@ -44,7 +44,8 @@ __contributors__ = ["Jason Diamond <http://injektilo.org/>",
"Sam Ruby <http://intertwingly.net/>",
"Ade Oshineye <http://blog.oshineye.com/>",
"Martin Pool <http://sourcefrog.net/>",
"Kurt McKee <http://kurtmckee.org/>"]
"Kurt McKee <http://kurtmckee.org/>",
"Bernd Schlapsi <https://github.com/brot>",]
# HTTP "User-Agent" header to send to servers when downloading feeds.
# If you are embedding feedparser in a larger application, you should
@@ -1971,6 +1972,7 @@ class _BaseHTMLProcessor(sgmllib.SGMLParser):
def handle_charref(self, ref):
# called for each character reference, e.g. for '&#160;', ref will be '160'
# Reconstruct the original character reference.
ref = ref.lower()
if ref.startswith('x'):
value = int(ref[1:], 16)
else:
@@ -2455,7 +2457,10 @@ class _MicroformatsParser:
linktype.startswith('video/') or \
(linktype.startswith('application/') and not linktype.endswith('xml')):
return 1
path = urlparse.urlparse(attrsD['href'])[2]
try:
path = urlparse.urlparse(attrsD['href'])[2]
except ValueError:
return 0
if path.find('.') == -1:
return 0
fileext = path.split('.').pop().lower()
@@ -2541,7 +2546,8 @@ class _RelativeURIResolver(_BaseHTMLProcessor):
('object', 'data'),
('object', 'usemap'),
('q', 'cite'),
('script', 'src')])
('script', 'src'),
('video', 'poster')])
def __init__(self, baseuri, encoding, _type):
_BaseHTMLProcessor.__init__(self, encoding, _type)
@@ -2618,13 +2624,13 @@ class _HTMLSanitizer(_BaseHTMLProcessor):
'loop', 'loopcount', 'loopend', 'loopstart', 'low', 'lowsrc', 'max',
'maxlength', 'media', 'method', 'min', 'multiple', 'name', 'nohref',
'noshade', 'nowrap', 'open', 'optimum', 'pattern', 'ping', 'point-size',
'prompt', 'pqg', 'radiogroup', 'readonly', 'rel', 'repeat-max',
'repeat-min', 'replace', 'required', 'rev', 'rightspacing', 'rows',
'rowspan', 'rules', 'scope', 'selected', 'shape', 'size', 'span', 'src',
'start', 'step', 'summary', 'suppress', 'tabindex', 'target', 'template',
'title', 'toppadding', 'type', 'unselectable', 'usemap', 'urn', 'valign',
'value', 'variable', 'volume', 'vspace', 'vrml', 'width', 'wrap',
'xml:lang'])
'poster', 'pqg', 'preload', 'prompt', 'radiogroup', 'readonly', 'rel',
'repeat-max', 'repeat-min', 'replace', 'required', 'rev', 'rightspacing',
'rows', 'rowspan', 'rules', 'scope', 'selected', 'shape', 'size', 'span',
'src', 'start', 'step', 'summary', 'suppress', 'tabindex', 'target',
'template', 'title', 'toppadding', 'type', 'unselectable', 'usemap',
'urn', 'valign', 'value', 'variable', 'volume', 'vspace', 'vrml',
'width', 'wrap', 'xml:lang'])
unacceptable_elements_with_end_tag = set(['script', 'applet', 'style'])
@@ -2976,9 +2982,9 @@ def _open_resource(url_file_stream_or_string, etag, modified, agent, referrer, h
url_file_stream_or_string = 'http:' + url_file_stream_or_string[5:]
if not agent:
agent = USER_AGENT
# test for inline user:password for basic auth
# Test for inline user:password credentials for HTTP basic auth
auth = None
if base64:
if base64 and not url_file_stream_or_string.startswith('ftp:'):
urltype, rest = urllib.splittype(url_file_stream_or_string)
realhost, rest = urllib.splithost(rest)
if realhost:
@@ -3471,15 +3477,7 @@ _rfc822_match = re.compile(
"(?:%s, )?%s(?: %s)?" % (_rfc822_dayname, _rfc822_date, _rfc822_time)
).match
def _parse_date_rfc822(dt):
"""Parse RFC 822 dates and times, with one minor
difference: years may be 4DIGIT or 2DIGIT.
http://tools.ietf.org/html/rfc822#section-5"""
try:
m = _rfc822_match(dt.lower()).groupdict(0)
except AttributeError:
return None
def _parse_date_group_rfc822(m):
# Calculate a date and timestamp
for k in ('year', 'day', 'hour', 'minute', 'second'):
m[k] = int(m[k])
@@ -3487,7 +3485,7 @@ def _parse_date_rfc822(dt):
# If the year is 2 digits, assume everything in the 90's is the 1990's
if m['year'] < 100:
m['year'] += (1900, 2000)[m['year'] < 90]
stamp = datetime.datetime(*[m[i] for i in
stamp = datetime.datetime(*[m[i] for i in
('year', 'month', 'day', 'hour', 'minute', 'second')])
# Use the timezone information to calculate the difference between
@@ -3512,8 +3510,36 @@ def _parse_date_rfc822(dt):
# Return the date and timestamp in UTC
return (stamp - delta).utctimetuple()
def _parse_date_rfc822(dt):
"""Parse RFC 822 dates and times, with one minor
difference: years may be 4DIGIT or 2DIGIT.
http://tools.ietf.org/html/rfc822#section-5"""
try:
m = _rfc822_match(dt.lower()).groupdict(0)
except AttributeError:
return None
return _parse_date_group_rfc822(m)
registerDateHandler(_parse_date_rfc822)
def _parse_date_rfc822_grubby(dt):
"""Parse date format similar to RFC 822, but
the comma after the dayname is optional and
month/day are inverted"""
_rfc822_date_grubby = "%s %s %s" % (_rfc822_month, _rfc822_day, _rfc822_year)
_rfc822_match_grubby = re.compile(
"(?:%s[,]? )?%s(?: %s)?" % (_rfc822_dayname, _rfc822_date_grubby, _rfc822_time)
).match
try:
m = _rfc822_match_grubby(dt.lower()).groupdict(0)
except AttributeError:
return None
return _parse_date_group_rfc822(m)
registerDateHandler(_parse_date_rfc822_grubby)
def _parse_date_asctime(dt):
"""Parse asctime-style dates"""
dayname, month, day, remainder = dt.split(None, 3)
@@ -3699,7 +3725,7 @@ def convert_to_utf8(http_headers, data):
u'application/xml-external-parsed-entity')
text_content_types = (u'text/xml', u'text/xml-external-parsed-entity')
if (http_content_type in application_content_types) or \
(http_content_type.startswith(u'application/') and
(http_content_type.startswith(u'application/') and
http_content_type.endswith(u'+xml')):
acceptable_content_type = 1
rfc3023_encoding = http_encoding or xml_encoding or u'utf-8'

View File

@@ -1,254 +0,0 @@
# -*- encoding: utf-8 -*-
from imaplib import ParseFlags
# mockimaplib: A very simple mock server module for imap client APIs
# Copyright (C) 2014 Alan Etkin <spametki@gmail.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or(at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this program. If not, see
# <http://www.gnu.org/licenses/lgpl.html>
"""
mockimaplib allows you to test applications connecting to a dummy imap
service. For more details on the api subset implemented,
refer to the imaplib docs.
The client should configure a dictionary to map imap string queries to sets
of entries stored in a message dummy storage dictionary. The module includes
a small set of default message records (SPAM and MESSAGES), two mailboxes
(Draft and INBOX) and a list of query/resultset entries (RESULTS).
Usage:
>>> import mockimaplib
>>> connection = mockimaplib.IMAP4_SSL(<host>)
>>> connection.login(<user>, <password>)
None
>>> connection.select("INBOX")
("OK", ... <mailbox length>)
# fetch commands specifying single uid or message id
# will try to get messages recorded in SPAM
>>> connection.uid(...)
<search query or fetch result>
# returns a string list of matching message ids
>>> connection.search(<query>)
("OK", ... "1 2 ... n")
"""
MESSAGES = (
'MIME-Version: 1.0\r\nReceived: by 10.140.91.199 with HTTP; Mon, 27 Jan 2014 13:52:30 -0800 (PST)\r\nDate: Mon, 27 Jan 2014 19:52:30 -0200\r\nDelivered-To: nurse@example.com\r\nMessage-ID: <10101010101010010000010101010001010101001010010000001@mail.example.com>\r\nSubject: spam1\r\nFrom: Mr. Gumby <gumby@example.com>\r\nTo: The nurse <nurse@example.com>\r\nContent-Type: text/plain; charset=ISO-8859-1\r\n\r\nNurse!\r\n\r\n\r\n',
'MIME-Version: 1.0\r\nReceived: by 10.140.91.199 with HTTP; Mon, 27 Jan 2014 13:52:47 -0800 (PST)\r\nDate: Mon, 27 Jan 2014 19:52:47 -0200\r\nDelivered-To: nurse@example.com\r\nMessage-ID: <101010101010100100000101010100010101010010100100000010@mail.example.com>\r\nSubject: spam2\r\nFrom: Mr. Gumby <gumby@example.com>\r\nTo: The nurse <nurse@example.com>\r\nContent-Type: text/plain; charset=ISO-8859-1\r\n\r\nNurse, nurse!',
'MIME-Version: 1.0\r\nReceived: by 10.140.91.199 with HTTP; Mon, 27 Jan 2014 13:54:54 -0800 (PST)\r\nDate: Mon, 27 Jan 2014 19:54:54 -0200\r\nDelivered-To: nurse@example.com\r\nMessage-ID: <1010101010101001000001010101000101010100101001000000101@mail.example.com>\r\nSubject: spamalot1\r\nFrom: Mr. Gumby <gumby@example.com>\r\nTo: The nurse <nurse@example.com>\r\nContent-Type: text/plain; charset=ISO-8859-1\r\n\r\nNurse!\r\n\r\n\r\n',
'MIME-Version: 1.0\r\n\r\nReceived: by 10.140.91.199 with HTTP; Mon, 27 Jan 2014 13:54:54 -0800 (PST)\r\nDate: Mon, 27 Jan 2014 19:54:54 -0200\r\nDelivered-To: nurse@example.com\r\nMessage-ID: <101010101010100100000101010100010101010010100100000010101@mail.example.com>\r\nSubject: spamalot2\r\nFrom: Mr. Gumby <gumby@example.com>\r\nTo: The nurse <nurse@example.com>\r\nContent-Type: text/plain; charset=ISO-8859-1\r\n\r\nNurse! ... Nurse! ... Nurse!\r\n\r\n\r\n')
SPAM = {
"INBOX": [
{"uid": "483209",
"headers": MESSAGES[0],
"complete": MESSAGES[0],
"flags": ""},
{"uid": "483211",
"headers": MESSAGES[1],
"complete": MESSAGES[1],
"flags": ""},
{"uid": "483225",
"headers": MESSAGES[2],
"complete": MESSAGES[2],
"flags": ""}],
"Draft":[
{"uid": "483432",
"headers": MESSAGES[3],
"complete": MESSAGES[3],
"flags": ""},]
}
RESULTS = {
# <query string>: [<str uid> | <long id>, ...]
"INBOX": {
"(ALL)": (1, 2, 3),
"(1:3)": (1, 2, 3)},
"Draft": {
"(1:1)": (1,)},
}
class Connection(object):
"""Dummy connection object for the imap client.
By default, uses the module SPAM and RESULT
sets (use Connection.setup for custom values)"""
def login(self, user, password):
pass
def __init__(self):
self._readonly = False
self._mailbox = None
self.setup()
def list(self):
return ('OK', ['(\\HasNoChildren) "/" "%s"' % key for key in self.spam])
def select(self, tablename, readonly=False):
self._readonly = readonly
"""args: mailbox, boolean
result[1][0] -> int last message id / mailbox lenght
result[0] = 'OK'
"""
self._mailbox = tablename
return ('OK', (len(SPAM[self._mailbox]), None))
def uid(self, command, uid, arg):
""" args:
command: "search" | "fetch"
uid: None | uid
parts: "(ALL)" | "(RFC822 FLAGS)" | "(RFC822.HEADER FLAGS)"
"search", None, "(ALL)" -> ("OK", ("uid_1 uid_2 ... uid_<mailbox length>", None))
"search", None, "<query>" -> ("OK", ("uid_1 uid_2 ... uid_n", None))
"fetch", uid, parts -> ("OK", (("<id> ...", "<raw message as specified in parts>"), "<flags>")
[0] [1][0][0] [1][0][1] [1][1]
"""
if command == "search":
return self._search(arg)
elif command == "fetch":
return self._fetch(uid, arg)
def _search(self, query):
return ("OK", (" ".join([str(item["uid"]) for item in self._get_messages(query)]), None))
def _fetch(self, value, arg):
try:
message = self.spam[self._mailbox][value - 1]
message_id = value
except TypeError:
for x, item in enumerate(self.spam[self._mailbox]):
if item["uid"] == value:
message = item
message_id = x + 1
break
parts = "headers"
if arg in ("(ALL)", "(RFC822 FLAGS)"):
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],]
elif query[1:-1].strip().isdigit():
return [self.spam[self._mailbox][int(query[1:-1].strip()) -1],]
elif query[1:-1].replace("UID", "").strip().isdigit():
for item in self.spam[self._mailbox]:
if item["uid"] == query[1:-1].replace("UID", "").strip():
return [item,]
messages = []
try:
for m in self.results[self._mailbox][query]:
try:
self.spam[self._mailbox][m - 1]["id"] = m
messages.append(self.spam[self._mailbox][m - 1])
except TypeError:
for x, item in enumerate(self.spam[self._mailbox]):
if item["uid"] == m:
item["id"] = x + 1
messages.append(item)
break
except IndexError:
# message removed
pass
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.
"""
self.spam = spam
self.results = results
if not spam:
for key in SPAM:
self.spam[key] = []
for d in SPAM[key]:
self.spam[key].append(d.copy())
if not results:
for key in RESULTS:
self.results[key] = RESULTS[key].copy()
def search(self, first, query):
""" args:
first: None
query: string with mailbox query (flags, date, uid, id, ...)
example: '2:15723 BEFORE 27-Jan-2014 FROM "gumby"'
result[1][0] -> "id_1 id_2 ... id_n"
"""
messages = self._get_messages(query)
ids = " ".join([str(item["id"]) for item in messages])
return ("OK", (ids, None))
def append(self, mailbox, flags, struct_time, message):
"""
result, data = self.connection.append(mailbox, flags, struct_time, message)
if result == "OK":
uid = int(re.findall("\d+", str(data))[-1])
"""
last = self.spam[mailbox][-1]
try:
uid = int(last["uid"]) +1
except ValueError:
alluids = []
for _mailbox in self.spam.keys():
for item in self.spam[_mailbox]:
try:
alluids.append(int(item["uid"]))
except:
pass
if len(alluids) > 0:
uid = max(alluids) + 1
else:
uid = 1
flags = "FLAGS " + flags
item = {"uid": str(uid), "headers": message, "complete": message, "flags": flags}
self.spam[mailbox].append(item)
return ("OK", "spam spam %s spam" % uid)
def store(self, *args):
"""
implements some flag commands
args: ("<id>", "<+|->FLAGS", "(\\Flag1 \\Flag2 ... \\Flagn)")
"""
message = self.spam[self._mailbox][int(args[0] - 1)]
old_flags = ParseFlags(message["flags"])
flags = ParseFlags("FLAGS" + args[2])
if args[1].strip().startswith("+"):
message["flags"] = "FLAGS (%s)" % " ".join(set(flags + old_flags))
elif args[1].strip().startswith("-"):
message["flags"] = "FLAGS (%s)" % " ".join([flag for flag in old_flags if not flag in flags])
def expunge(self):
"""implements removal of deleted flag messages"""
for x, item in enumerate(self.spam[self._mailbox]):
if "\\Deleted" in item["flags"]:
self.spam[self._mailbox].pop(x)
class IMAP4(object):
""">>> connection = IMAP4() # creates the dummy imap4 client object"""
def __new__(self, *args, **kwargs):
# args: (server, port)
return Connection()
IMAP4_SSL = IMAP4

File diff suppressed because it is too large Load Diff

View File

@@ -4,7 +4,24 @@ from hashlib import sha1
class Stripe:
"""
Usage:
Use in WEB2PY (guaranteed PCI compliant)
def pay():
from gluon.contrib.stripe import StripeForm
form = StripeForm(
pk=STRIPE_PUBLISHABLE_KEY,
sk=STRIPE_SECRET_KEY,
amount=150, # $1.5 (amount is in cents)
description="Nothing").process()
if form.accepted:
payment_id = form.response['id']
redirect(URL('thank_you'))
elif form.errors:
redirect(URL('pay_error'))
return dict(form=form)
Low level API:
key='<api key>'
d = Stripe(key).charge(
amount=100, # 1 dollar!!!!
@@ -22,22 +39,6 @@ class Stripe:
{u'fee': 0, u'description': u'test charge', u'created': 1321242072, u'refunded': False, u'livemode': False, u'object': u'charge', u'currency': u'usd', u'amount': 100, u'paid': True, u'id': u'ch_sdjasgfga83asf', u'card': {u'exp_month': 5, u'country': u'US', u'object': u'card', u'last4': u'4242', u'exp_year': 2012, u'type': u'Visa'}}
if paid is True than transaction was processed
Use in WEB2PY (guaranteed PCI compliant)
def pay():
from gluon.contrib.stripe import StripeForm
form = StripeForm(
pk=STRIPE_PUBLISHABLE_KEY,
sk=STRIPE_SECRET_KEY,
amount=150, # $1.5 (amount is in cents)
description="Nothing").process()
if form.accepted:
payment_id = form.response['id']
redirect(URL('thank_you'))
elif form.errors:
redirect(URL('pay_error'))
return dict(form=form)
"""
URL_CHARGE = 'https://%s:@api.stripe.com/v1/charges'
@@ -188,37 +189,36 @@ jQuery(function(){
<h3>Payment Amount: {{=currency_symbol}} {{="%.2f" % (0.01*amount)}}</h3>
<form action="" method="POST" id="payment-form" class="form-horizontal">
<div class="form-row control-group">
<label class="control-label">Card Number</label>
<div class="controls">
<div class="form-row form-group">
<label class="col-sm-2 control-label">Card Number</label>
<div class="controls col-sm-10">
<input type="text" size="20" data-stripe="number"
placeholder="4242424242424242"/>
placeholder="4242424242424242" class="form-control"/>
</div>
</div>
<div class="form-row control-group">
<label class="control-label">CVC</label>
<div class="controls">
<div class="form-row form-group">
<label class="col-sm-2 control-label">CVC</label>
<div class="controls col-sm-10">
<input type="text" size="4" style="width:80px" data-stripe="cvc"
placeholder="XXX"/>
placeholder="XXX" class="form-control"/>
<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>
<div class="controls">
<input type="text" size="2" style="width:40px" data-stripe="exp-month"
placeholder="MM"/>
<div class="form-row form-group">
<label class="col-sm-2 control-label">Expiration</label>
<div class="controls col-sm-10">
<input type="text" size="2" style="width:40px; display:inline-block"
data-stripe="exp-month" placeholder="MM" class="form-control"/>
/
<input type="text" size="4" style="width:80px" data-stripe="exp-year"
placeholder="YYYY"/>
<input type="text" size="4" style="width:80px; display:inline-block"
data-stripe="exp-year" placeholder="YYYY" class="form-control"/>
</div>
</div>
<div class="control-group">
<div class="controls">
<div class="form-row form-group">
<div class="controls col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">Submit Payment</button>
<div class="payment-errors error hidden"></div>
</div>

View File

@@ -41,12 +41,15 @@ class CustomImportException(ImportError):
def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
"""
web2py's custom importer. It behaves like the standard Python importer but
web2py's custom importer. It behaves like the standard Python importer but
it tries to transform import statements as something like
"import applications.app_name.modules.x".
If the import fails, it falls back on naive_importer
"""
if isinstance(name, unicode):
name = name.encode('utf8')
globals = globals or {}
locals = locals or {}
fromlist = fromlist or []
@@ -77,7 +80,7 @@ def custom_importer(name, globals=None, locals=None, fromlist=None, level=-1):
if not fromlist:
# import like "import x" or "import x.y"
result = None
for itemname in name.split("."):
for itemname in name.split("."):
new_mod = base_importer(
modules_prefix, globals, locals, [itemname], level)
try:

View File

@@ -14,7 +14,7 @@ from pydal import DAL as DAL
from pydal import Field
from pydal.objects import Row, Rows, Table, Query, Expression
from pydal import SQLCustomType, geoPoint, geoLine, geoPolygon
import copy_reg as copyreg
def _default_validators(db, field):
"""
@@ -81,12 +81,12 @@ def _default_validators(db, field):
requires[0] = validators.IS_EMPTY_OR(requires[0])
return requires
from gluon import serializers as w2p_serializers
from gluon.serializers import custom_json, xml
from gluon.utils import web2py_uuid
from gluon import sqlhtml
DAL.serializers = w2p_serializers
DAL.serializers = {'json': custom_json, 'xml': xml}
DAL.validators_method = _default_validators
DAL.uuid = lambda x: web2py_uuid()
DAL.representers = {

View File

@@ -23,6 +23,7 @@ base_modules = ['aifc', 'anydbm', 'array', 'asynchat', 'asyncore', 'atexit',
'collections', 'colorsys', 'compileall', 'compiler',
'compiler.ast', 'compiler.visitor', 'ConfigParser',
'contextlib', 'Cookie', 'cookielib', 'copy', 'copy_reg',
'collections',
'cPickle', 'cProfile', 'cStringIO', 'csv', 'ctypes',
'datetime', 'decimal', 'difflib', 'dircache', 'dis',
'doctest', 'DocXMLRPCServer', 'dumbdbm', 'dummy_thread',
@@ -67,13 +68,6 @@ base_modules = ['aifc', 'anydbm', 'array', 'asynchat', 'asyncore', 'atexit',
'MimeWriter', 'mimify', 'multifile', 'sets']
contributed_modules = []
for root, dirs, files in os.walk('gluon'):
for candidate in ['.'.join(
os.path.join(root, os.path.splitext(name)[0]).split(os.sep))
for name in files if name.endswith('.py')
and root.split(os.sep) != ['gluon', 'tests']
]:
contributed_modules.append(candidate)
# Python base version
python_version = sys.version[:3]

View File

@@ -58,9 +58,12 @@ def represent(field, value, record):
f = field.represent
if not callable(f):
return str(value)
n = f.func_code.co_argcount - len(f.func_defaults or [])
if getattr(f, 'im_self', None):
n -= 1
if hasattr(f,'func_code'):
n = f.func_code.co_argcount - len(f.func_defaults or [])
if getattr(f, 'im_self', None):
n -= 1
else:
n = 1
if n == 1:
return f(value)
elif n == 2:
@@ -1205,6 +1208,9 @@ class SQLFORM(FORM):
elif field.type == 'boolean':
inp = self.widgets.boolean.widget(
field, default, _disabled=True)
elif isinstance(field.type, SQLCustomType) and callable(field.type.represent):
# SQLCustomType has a represent, use it
inp = field.type.represent(default, record)
else:
inp = field.formatter(default)
if getattr(field, 'show_if', None):
@@ -1246,6 +1252,9 @@ class SQLFORM(FORM):
dspval = ''
elif field.type == 'blob':
continue
elif isinstance(field.type, SQLCustomType) and callable(field.type.widget):
# SQLCustomType has a widget, use it
inp = field.type.widget(field, default)
else:
field_type = widget_class.match(str(field.type)).group()
field_type = field_type in self.widgets and field_type or 'string'
@@ -2148,7 +2157,7 @@ class SQLFORM(FORM):
else:
fields = []
columns = []
filter1 = lambda f: isinstance(f, Field)
filter1 = lambda f: isinstance(f, Field) and f.type != 'blob'
filter2 = lambda f: isinstance(f, Field) and f.readable
for table in tables:
fields += filter(filter1, table)
@@ -2708,6 +2717,9 @@ class SQLFORM(FORM):
_href='%s/%s' % (upload, value))
else:
value = ''
elif isinstance(field.type, SQLCustomType) and callable(field.type.represent):
# SQLCustomType has a represent, use it
value = field.type.represent(value, row)
if isinstance(value, str):
value = truncate_string(value, maxlength)
elif not isinstance(value, XmlComponent):

View File

@@ -15,10 +15,11 @@ from gluon.dal import DAL, Field
class TestDALSubclass(unittest.TestCase):
def testRun(self):
import gluon.serializers as mserializers
from gluon.serializers import custom_json, xml
from gluon import sqlhtml
db = DAL(check_reserved=['all'])
self.assertEqual(db.serializers, mserializers)
self.assertEqual(db.serializers['json'], custom_json)
self.assertEqual(db.serializers['xml'], xml)
self.assertEqual(db.representers['rows_render'], sqlhtml.represent)
self.assertEqual(db.representers['rows_xml'], sqlhtml.SQLTABLE)

View File

@@ -42,10 +42,13 @@ from gluon import *
from gluon.contrib.autolinks import expand_one
from gluon.contrib.markmin.markmin2html import \
replace_at_urls, replace_autolinks, replace_components
from pydal.objects import Table, Row, Set, Query
from pydal.objects import Row, Set, Query
import gluon.serializers as serializers
Table = DAL.Table
Field = DAL.Field
try:
# try stdlib (Python 2.6)
import json as json_parser

View File

@@ -611,7 +611,7 @@ class IS_IN_DB(Validator):
def count(values, s=self.dbset, f=field):
return s(f.belongs(map(int, values))).count()
if isinstance(self.dbset.db._adapter, GoogleDatastoreAdapter):
if GoogleDatastoreAdapter is not None and isinstance(self.dbset.db._adapter, GoogleDatastoreAdapter):
range_ids = range(0, len(values), 30)
total = sum(count(values[i:i + 30]) for i in range_ids)
if total == len(values):