Compare commits

..

6 Commits

Author SHA1 Message Date
dlage 4228b070d7 fix wrong branch signature. 2020-03-17 16:40:48 +00:00
dlage c4e0ab2316 fix wrong submodule repo url. 2020-03-17 16:29:22 +00:00
dlage ac6494387d Update to the web2py/pydal repo. 2020-03-17 16:26:47 +00:00
dlage f6b9d20fd6 Merge remote-tracking branch 'web2py/master' into ah-stable 2020-03-17 16:24:00 +00:00
dlage 9d934e0e7b updated pydal to master. 2020-03-17 16:22:05 +00:00
dlage a7963a2b0e point pydal to patched version. 2020-01-07 19:22:25 +00:00
44 changed files with 547 additions and 904 deletions
+5 -1
View File
@@ -12,7 +12,11 @@ python:
- '2.7' - '2.7'
- '3.6' - '3.6'
- '3.7' - '3.7'
- '3.8' - 'pypy3.5'
matrix:
allow_failures:
- python: 'pypy3.5'
install: install:
- pip install -e . - pip install -e .
-4
View File
@@ -1,7 +1,3 @@
## 2.20.1
new makefile to update binaries from Nico Zanferrari
## 2.19.0 ## 2.19.0
- new command line options (Thanks Paolo Pastori) - new command line options (Thanks Paolo Pastori)
+1 -58
View File
@@ -45,7 +45,7 @@ rmfiles:
rm -rf applications/examples/uploads/* rm -rf applications/examples/uploads/*
src: src:
### Use semantic versioning ### Use semantic versioning
echo 'Version 2.20.4-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION echo 'Version 2.18.5-stable+timestamp.'`date +%Y.%m.%d.%H.%M.%S` > VERSION
### rm -f all junk files ### rm -f all junk files
make clean make clean
# make rmfiles # make rmfiles
@@ -104,63 +104,6 @@ win:
mv ../web2py_win/web2py/_ssl.pyd ../web2py_win/web2py/_ssl.pyd.legacy | echo 'done' mv ../web2py_win/web2py/_ssl.pyd ../web2py_win/web2py/_ssl.pyd.legacy | echo 'done'
cd ../web2py_win; zip -r web2py_win.zip web2py cd ../web2py_win; zip -r web2py_win.zip web2py
mv ../web2py_win/web2py_win.zip . mv ../web2py_win/web2py_win.zip .
binaries:
echo '' > NEWINSTALL
cp VERSION ../web2py_win_py27/web2py/
cp README.markdown ../web2py_win_py27/web2py/
cp NEWINSTALL ../web2py_win_py27/web2py/
cp LICENSE ../web2py_win_py27/web2py/
cp CHANGELOG ../web2py_win_py27/web2py/
rm -rf ../web2py_win_py27/web2py/gluon
cp -r gluon ../web2py_win_py27/web2py/gluon
rm -rf ../web2py_win_py27/web2py/applications/*
cp -r applications/__init__.py ../web2py_win_py27/web2py/applications/
cp -r applications/admin ../web2py_win_py27/web2py/applications/
cp -r applications/welcome ../web2py_win_py27/web2py/applications/
cp -r applications/examples ../web2py_win_py27/web2py/applications/
cd ../web2py_win_py27; zip -r ../web2py/web2py_win_py27.zip web2py
cp VERSION ../web2py_win_py37/web2py/
cp README.markdown ../web2py_win_py37/web2py/
cp NEWINSTALL ../web2py_win_py37/web2py/
cp LICENSE ../web2py_win_py37/web2py/
cp CHANGELOG ../web2py_win_py37/web2py/
rm -rf ../web2py_win_py37/web2py/gluon
cp -r gluon ../web2py_win_py37/web2py/gluon
rm -rf ../web2py_win_py37/web2py/applications/*
cp -r applications/__init__.py ../web2py_win_py37/web2py/applications/
cp -r applications/admin ../web2py_win_py37/web2py/applications/
cp -r applications/welcome ../web2py_win_py37/web2py/applications/
cp -r applications/examples ../web2py_win_py37/web2py/applications/
cd ../web2py_win_py37; zip -r ../web2py/web2py_win_py37.zip web2py
cp VERSION ../web2py_osx_py27/web2py.app/Contents/MacOS/
cp README.markdown ../web2py_osx_py27/web2py.app/Contents/MacOS/
cp NEWINSTALL ../web2py_osx_py27/web2py.app/Contents/MacOS/
cp LICENSE ../web2py_osx_py27/web2py.app/Contents/MacOS/
cp CHANGELOG ../web2py_osx_py27/web2py.app/Contents/MacOS/
rm -rf ../web2py_osx_py27/web2py.app/Contents/MacOS/gluon
cp -r gluon ../web2py_osx_py27/web2py.app/Contents/MacOS/gluon
rm -rf ../web2py_osx_py27/web2py.app/Contents/MacOS/applications/*
cp -r applications/__init__.py ../web2py_osx_py27/web2py.app/Contents/MacOS/applications/
cp -r applications/admin ../web2py_osx_py27/web2py.app/Contents/MacOS/applications/
cp -r applications/welcome ../web2py_osx_py27/web2py.app/Contents/MacOS/applications/
cp -r applications/examples ../web2py_osx_py27/web2py.app/Contents/MacOS/applications/
cd ../web2py_osx_py27; zip -r ../web2py/web2py_osx_py27.zip web2py.app
cp VERSION ../web2py_osx_py37/web2py.app/Contents/MacOS/
cp README.markdown ../web2py_osx_py37/web2py.app/Contents/MacOS/
cp NEWINSTALL ../web2py_osx_py37/web2py.app/Contents/MacOS/
cp LICENSE ../web2py_osx_py37/web2py.app/Contents/MacOS/
cp CHANGELOG ../web2py_osx_py37/web2py.app/Contents/MacOS/
rm -rf ../web2py_osx_py37/web2py.app/Contents/MacOS/gluon
cp -r gluon ../web2py_osx_py37/web2py.app/Contents/MacOS/gluon
rm -rf ../web2py_osx_py37/web2py.app/Contents/MacOS/applications/*
cp -r applications/__init__.py ../web2py_osx_py37/web2py.app/Contents/MacOS/applications/
cp -r applications/admin ../web2py_osx_py37/web2py.app/Contents/MacOS/applications/
cp -r applications/welcome ../web2py_osx_py37/web2py.app/Contents/MacOS/applications/
cp -r applications/examples ../web2py_osx_py37/web2py.app/Contents/MacOS/applications/
cd ../web2py_osx_py37; zip -r ../web2py/web2py_osx_py37.zip web2py.app
run: run:
python2.7 web2py.py -a hello python2.7 web2py.py -a hello
commit: commit:
+1 -1
View File
@@ -1 +1 @@
Version 2.20.4-stable+timestamp.2020.05.02.22.03.36 Version 2.18.5-stable+timestamp.2019.04.07.21.13.59
@@ -194,6 +194,8 @@ def select():
request.vars.query = '%s.%s.%s==%s' % (request.args[0], request.vars.query = '%s.%s.%s==%s' % (request.args[0],
match.group('table'), match.group('field'), match.group('table'), match.group('field'),
match.group('value')) match.group('value'))
else:
request.vars.query = session.last_query
query = get_query(request) query = get_query(request)
if request.vars.start: if request.vars.start:
start = int(request.vars.start) start = int(request.vars.start)
@@ -220,6 +222,7 @@ def select():
else: else:
orderby = '~' + orderby orderby = '~' + orderby
session.last_orderby = orderby session.last_orderby = orderby
session.last_query = request.vars.query
form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
_name='query', _value=request.vars.query or '', _class='form-control', _name='query', _value=request.vars.query or '', _class='form-control',
requires=IS_NOT_EMPTY( requires=IS_NOT_EMPTY(
+1 -1
View File
@@ -1261,7 +1261,7 @@ def plugin():
defines = {} defines = {}
for m in models: for m in models:
data = safe_read(apath('%s/models/%s' % (app, m), r=request)) data = safe_read(apath('%s/models/%s' % (app, m), r=request))
defines[m] = re.findall(REGEX_DEFINE_TABLE, data, re.MULTILINE) defines[m] = regex_tables.findall(data)
defines[m].sort() defines[m].sort()
# Get all controllers # Get all controllers
+1 -7
View File
@@ -6,10 +6,7 @@ from gluon.fileutils import read_file
from gluon.utils import web2py_uuid from gluon.utils import web2py_uuid
from pydal.contrib import portalocker from pydal.contrib import portalocker
# ########################################################### # ###########################################################
# ## make sure administrator is on localhost or https, # ## make sure administrator is on localhost or https
# ## or from
# ## gluon.settings.global_settings.trusted_lan_prefix
# ## subnet
# ########################################################### # ###########################################################
@@ -25,9 +22,6 @@ else:
if request.is_https: if request.is_https:
session.secure() session.secure()
elif request.env.trusted_lan_prefix and \
request.client.startswith(request.env.trusted_lan_prefix):
request.is_local = True
elif not request.is_local and not DEMO_MODE: elif not request.is_local and not DEMO_MODE:
raise HTTP(200, T('Admin is disabled because insecure channel')) raise HTTP(200, T('Admin is disabled because insecure channel'))
+1 -1
View File
@@ -82,7 +82,7 @@
<div style="overflow:auto; width:80%;"> <div style="overflow:auto; width:80%;">
{{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}} {{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}}
{{upload=URL('download',args=request.args[0])}} {{upload=URL('download',args=request.args[0])}}
{{=SQLTABLE(rows,linkto,upload,orderby=True,query=query,_class='table table-striped table-bordered sortable')}} {{=SQLTABLE(rows,linkto,upload,orderby=True,_class='table table-striped table-bordered sortable')}}
</div> </div>
{{pass}} {{pass}}
<br/><br/> <br/><br/>
@@ -194,6 +194,8 @@ def select():
request.vars.query = '%s.%s.%s==%s' % (request.args[0], request.vars.query = '%s.%s.%s==%s' % (request.args[0],
match.group('table'), match.group('field'), match.group('table'), match.group('field'),
match.group('value')) match.group('value'))
else:
request.vars.query = session.last_query
query = get_query(request) query = get_query(request)
if request.vars.start: if request.vars.start:
start = int(request.vars.start) start = int(request.vars.start)
@@ -220,6 +222,7 @@ def select():
else: else:
orderby = '~' + orderby orderby = '~' + orderby
session.last_orderby = orderby session.last_orderby = orderby
session.last_query = request.vars.query
form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
_name='query', _value=request.vars.query or '', _class='form-control', _name='query', _value=request.vars.query or '', _class='form-control',
requires=IS_NOT_EMPTY( requires=IS_NOT_EMPTY(
+1 -1
View File
@@ -82,7 +82,7 @@
<div style="overflow:auto; width:80%;"> <div style="overflow:auto; width:80%;">
{{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}} {{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}}
{{upload=URL('download',args=request.args[0])}} {{upload=URL('download',args=request.args[0])}}
{{=SQLTABLE(rows,linkto,upload,orderby=True,query=query,_class='table table-striped table-bordered sortable')}} {{=SQLTABLE(rows,linkto,upload,orderby=True,_class='table table-striped table-bordered sortable')}}
</div> </div>
{{pass}} {{pass}}
<br/><br/> <br/><br/>
@@ -9,8 +9,8 @@
<table class="twothirds"> <table class="twothirds">
<thead> <thead>
<tr> <tr>
<th>For Python 3.7</th> <th>For Normal Users (Py3)</th>
<th>For Python 2.7</th> <th>For Legacy Users (Py2)</th>
<th>For Testers</th> <th>For Testers</th>
<th>For Developers</th> <th>For Developers</th>
</tr> </tr>
@@ -18,13 +18,13 @@
<tbody> <tbody>
<tr> <tr>
<td> <td>
<a class="btn btn180 rounded green" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_win_py37.zip">Windows binaries</a> <a class="btn btn180 rounded green" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_win.zip">Windows binaries</a>
</td> </td>
<td> <td>
<a class="btn btn180 rounded" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_win_py27.zip">Windows binaries</a> <a class="btn btn180 rounded" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_win_py2.zip">Windows binaries</a>
</td> </td>
<td> <td>
<a class="btn btn180 rounded yellow" href="https://mdipierro.pythonanywhere.com/examples/static/nightly/web2py_win_py27.zip">Windows binaries</a> <a class="btn btn180 rounded yellow" href="https://mdipierro.pythonanywhere.com/examples/static/nightly/web2py_win.zip">Windows binaries</a>
</td> </td>
<td> <td>
<a class="btn btn180 rounded red" href="http://github.com/web2py/web2py/">Git Repository</a> <a class="btn btn180 rounded red" href="http://github.com/web2py/web2py/">Git Repository</a>
@@ -32,13 +32,13 @@
</tr> </tr>
<tr> <tr>
<td> <td>
<a class="btn btn180 rounded green" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_osx_py37.zip">Mac binaries</a> <a class="btn btn180 rounded green" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_osx.zip">Mac binaries</a>
</td> </td>
<td> <td>
<a class="btn btn180 rounded" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_osx_py27.zip">Mac binaries</a> <a class="btn btn180 rounded" href="https://mdipierro.pythonanywhere.com/examples/static/web2py_osx_py2.zip">Mac binaries</a>
</td> </td>
<td> <td>
<a class="btn btn180 rounded yellow" href="https://mdipierro.pythonanywhere.com/examples/static/nightly/web2py_osx_py37.zip">Mac binaries</a> <a class="btn btn180 rounded yellow" href="https://mdipierro.pythonanywhere.com/examples/static/nightly/web2py_osx.zip">Mac binaries</a>
</td> </td>
<td> <td>
<a class="btn btn180 rounded" href="http://mdipierro.github.io/web2py/web2py_manual_5th.pdf">Manual</a> <a class="btn btn180 rounded" href="http://mdipierro.github.io/web2py/web2py_manual_5th.pdf">Manual</a>
+3 -3
View File
@@ -30,9 +30,6 @@ except:
if request.is_https: if request.is_https:
session.secure() session.secure()
elif request.env.trusted_lan_prefix and \
remote_addr.startswith(request.env.trusted_lan_prefix):
request.is_local = True
elif (remote_addr not in hosts) and (remote_addr != '127.0.0.1') and \ elif (remote_addr not in hosts) and (remote_addr != '127.0.0.1') and \
(request.function != 'manage'): (request.function != 'manage'):
raise HTTP(200, T('appadmin is disabled because insecure channel')) raise HTTP(200, T('appadmin is disabled because insecure channel'))
@@ -197,6 +194,8 @@ def select():
request.vars.query = '%s.%s.%s==%s' % (request.args[0], request.vars.query = '%s.%s.%s==%s' % (request.args[0],
match.group('table'), match.group('field'), match.group('table'), match.group('field'),
match.group('value')) match.group('value'))
else:
request.vars.query = session.last_query
query = get_query(request) query = get_query(request)
if request.vars.start: if request.vars.start:
start = int(request.vars.start) start = int(request.vars.start)
@@ -223,6 +222,7 @@ def select():
else: else:
orderby = '~' + orderby orderby = '~' + orderby
session.last_orderby = orderby session.last_orderby = orderby
session.last_query = request.vars.query
form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px', form = FORM(TABLE(TR(T('Query:'), '', INPUT(_style='width:400px',
_name='query', _value=request.vars.query or '', _class='form-control', _name='query', _value=request.vars.query or '', _class='form-control',
requires=IS_NOT_EMPTY( requires=IS_NOT_EMPTY(
+1 -1
View File
@@ -82,7 +82,7 @@
<div style="overflow:auto; width:80%;"> <div style="overflow:auto; width:80%;">
{{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}} {{linkto = lambda f, t, r: URL('update', args=[request.args[0], r, f]) if f else "#"}}
{{upload=URL('download',args=request.args[0])}} {{upload=URL('download',args=request.args[0])}}
{{=SQLTABLE(rows,linkto,upload,orderby=True,query=query,_class='table table-striped table-bordered sortable')}} {{=SQLTABLE(rows,linkto,upload,orderby=True,_class='table table-striped table-bordered sortable')}}
</div> </div>
{{pass}} {{pass}}
<br/><br/> <br/><br/>
+4
View File
@@ -10,6 +10,10 @@ environment:
COVERAGE_PROCESS_START: gluon/tests/coverage.ini COVERAGE_PROCESS_START: gluon/tests/coverage.ini
PYTHON_ARCH: "64" PYTHON_ARCH: "64"
- PYTHON: "C:/Python35"
COVERAGE_PROCESS_START: gluon/tests/coverage.ini
PYTHON_ARCH: "64"
- PYTHON: "C:/Python36" - PYTHON: "C:/Python36"
COVERAGE_PROCESS_START: gluon/tests/coverage.ini COVERAGE_PROCESS_START: gluon/tests/coverage.ini
PYTHON_ARCH: "64" PYTHON_ARCH: "64"
+1 -1
View File
@@ -7,7 +7,7 @@ a = Analysis(['web2py.py'],
pathex=['.'], pathex=['.'],
binaries=[('/System/Library/Frameworks/Tk.framework/Tk', 'tk'), ('/System/Library/Frameworks/Tcl.framework/Tcl', 'tcl')], binaries=[('/System/Library/Frameworks/Tk.framework/Tk', 'tk'), ('/System/Library/Frameworks/Tcl.framework/Tcl', 'tcl')],
datas=[], datas=[],
hiddenimports=['site-packages', 'argparse', 'cgi', 'cgitb', 'code', 'concurrent', 'concurrent.futures', hiddenimports=['site-packages', 'cgi', 'cgitb', 'code', 'concurrent', 'concurrent.futures',
'concurrent.futures._base', 'concurrent.futures.process', 'concurrent.futures.thread', 'configparser', 'cProfile', 'csv', 'ctypes.wintypes', 'concurrent.futures._base', 'concurrent.futures.process', 'concurrent.futures.thread', 'configparser', 'cProfile', 'csv', 'ctypes.wintypes',
'email.mime', 'email.mime.base', 'email.mime.multipart', 'email.mime.nonmultipart', 'email.mime.text', 'html.parser', 'http.cookies', 'email.mime', 'email.mime.base', 'email.mime.multipart', 'email.mime.nonmultipart', 'email.mime.text', 'html.parser', 'http.cookies',
'ipaddress', 'imaplib', 'imp', 'json', 'json.decoder', 'json.encoder', 'json.scanner', 'logging.config', 'logging.handlers', 'profile', 'pstats', 'ipaddress', 'imaplib', 'imp', 'json', 'json.decoder', 'json.encoder', 'json.scanner', 'logging.config', 'logging.handlers', 'profile', 'pstats',
+1 -1
View File
@@ -7,7 +7,7 @@ a = Analysis(['web2py.py'],
pathex=['.'], pathex=['.'],
binaries=[], binaries=[],
datas=[], datas=[],
hiddenimports=['site-packages', 'argparse', 'cgi', 'cgitb', 'code', 'concurrent', 'concurrent.futures', hiddenimports=['site-packages', 'cgi', 'cgitb', 'code', 'concurrent', 'concurrent.futures',
'concurrent.futures._base', 'concurrent.futures.process', 'concurrent.futures.thread', 'configparser', 'csv', 'ctypes.wintypes', 'concurrent.futures._base', 'concurrent.futures.process', 'concurrent.futures.thread', 'configparser', 'csv', 'ctypes.wintypes',
'email.mime', 'email.mime.base', 'email.mime.multipart', 'email.mime.nonmultipart', 'email.mime.text', 'html.parser', 'http.cookies', 'email.mime', 'email.mime.base', 'email.mime.multipart', 'email.mime.nonmultipart', 'email.mime.text', 'html.parser', 'http.cookies',
'ipaddress', 'imp', 'json', 'json.decoder', 'json.encoder', 'json.scanner', 'logging.config', 'logging.handlers', 'profile', 'pstats', 'ipaddress', 'imp', 'json', 'json.decoder', 'json.encoder', 'json.scanner', 'logging.config', 'logging.handlers', 'profile', 'pstats',
@@ -7,7 +7,7 @@ a = Analysis(['web2py.py'],
pathex=['.'], pathex=['.'],
binaries=[], binaries=[],
datas=[], datas=[],
hiddenimports=['site-packages', 'argparse', 'cgi', 'cgitb', 'code', 'concurrent', 'concurrent.futures', hiddenimports=['site-packages', 'cgi', 'cgitb', 'code', 'concurrent', 'concurrent.futures',
'concurrent.futures._base', 'concurrent.futures.process', 'concurrent.futures.thread', 'configparser', 'csv', 'ctypes.wintypes', 'concurrent.futures._base', 'concurrent.futures.process', 'concurrent.futures.thread', 'configparser', 'csv', 'ctypes.wintypes',
'email.mime', 'email.mime.base', 'email.mime.multipart', 'email.mime.nonmultipart', 'email.mime.text', 'html.parser', 'http.cookies', 'email.mime', 'email.mime.base', 'email.mime.multipart', 'email.mime.nonmultipart', 'email.mime.text', 'html.parser', 'http.cookies',
'ipaddress', 'imp', 'json', 'json.decoder', 'json.encoder', 'json.scanner', 'logging.config', 'logging.handlers', 'profile', 'pstats', 'ipaddress', 'imp', 'json', 'json.decoder', 'json.encoder', 'json.scanner', 'logging.config', 'logging.handlers', 'profile', 'pstats',
+27 -33
View File
@@ -44,18 +44,16 @@ See the documentation for WSGIServer/Server for more information.
On most platforms, fcgi will fallback to regular CGI behavior if run in a On most platforms, fcgi will fallback to regular CGI behavior if run in a
non-FastCGI context. If you want to force CGI behavior, set the environment non-FastCGI context. If you want to force CGI behavior, set the environment
variable FCGI_FORCE_CGI to "Y" or "y". variable FCGI_FORCE_CGI to "Y" or "y".
Modified for web2py
""" """
__author__ = 'Allan Saddi <allan@saddi.com>' __author__ = 'Allan Saddi <allan@saddi.com>'
__version__ = '$Revision$' __version__ = '$Revision$'
import cgi
import sys import sys
import os import os
import signal import signal
import struct import struct
import cStringIO as StringIO
import select import select
import socket import socket
import errno import errno
@@ -70,15 +68,6 @@ except ImportError:
import dummy_threading as threading import dummy_threading as threading
thread_available = False thread_available = False
is_py2 = sys.version_info[0] == 2
if is_py2:
from cgi import escape
import cStringIO as StringIO
else:
from io import StringIO
from html import escape
# Apparently 2.3 doesn't define SHUT_WR? Assume it is 1 in this case. # Apparently 2.3 doesn't define SHUT_WR? Assume it is 1 in this case.
if not hasattr(socket, 'SHUT_WR'): if not hasattr(socket, 'SHUT_WR'):
socket.SHUT_WR = 1 socket.SHUT_WR = 1
@@ -245,9 +234,11 @@ class InputStream(object):
total += len(line) total += len(line)
if 0 < sizehint <= total: if 0 < sizehint <= total:
break break
yield self.readline() line = self.readline()
return lines
__iter__ = readlines def __iter__(self):
return self
def next(self): def next(self):
r = self.readline() r = self.readline()
@@ -445,13 +436,13 @@ def encode_pair(name, value):
if nameLength < 128: if nameLength < 128:
s = chr(nameLength) s = chr(nameLength)
else: else:
s = struct.pack('!L', nameLength | 0x80000000) s = struct.pack('!L', nameLength | 0x80000000L)
valueLength = len(value) valueLength = len(value)
if valueLength < 128: if valueLength < 128:
s += chr(valueLength) s += chr(valueLength)
else: else:
s += struct.pack('!L', valueLength | 0x80000000) s += struct.pack('!L', valueLength | 0x80000000L)
return s + name + value return s + name + value
@@ -601,7 +592,7 @@ class Request(object):
self._flush() self._flush()
self._end(appStatus, protocolStatus) self._end(appStatus, protocolStatus)
def _end(self, appStatus=0, protocolStatus=FCGI_REQUEST_COMPLETE): def _end(self, appStatus=0L, protocolStatus=FCGI_REQUEST_COMPLETE):
self._conn.end_request(self, appStatus, protocolStatus) self._conn.end_request(self, appStatus, protocolStatus)
def _flush(self): def _flush(self):
@@ -624,7 +615,7 @@ class CGIRequest(Request):
self.stderr = sys.stderr self.stderr = sys.stderr
self.data = StringIO.StringIO() self.data = StringIO.StringIO()
def _end(self, appStatus=0, protocolStatus=FCGI_REQUEST_COMPLETE): def _end(self, appStatus=0L, protocolStatus=FCGI_REQUEST_COMPLETE):
sys.exit(appStatus) sys.exit(appStatus)
def _flush(self): def _flush(self):
@@ -673,7 +664,7 @@ class Connection(object):
self.process_input() self.process_input()
except EOFError: except EOFError:
break break
except (select.error, socket.error) as e: except (select.error, socket.error), e:
if e.errno == errno.EBADF: # Socket was closed by Request. if e.errno == errno.EBADF: # Socket was closed by Request.
break break
raise raise
@@ -723,7 +714,7 @@ class Connection(object):
""" """
rec.write(self._sock) rec.write(self._sock)
def end_request(self, req, appStatus=0, def end_request(self, req, appStatus=0L,
protocolStatus=FCGI_REQUEST_COMPLETE, remove=True): protocolStatus=FCGI_REQUEST_COMPLETE, remove=True):
""" """
End a Request. End a Request.
@@ -772,7 +763,7 @@ class Connection(object):
if not self._multiplexed and self._requests: if not self._multiplexed and self._requests:
# Can't multiplex requests. # Can't multiplex requests.
self.end_request(req, 0, FCGI_CANT_MPX_CONN, remove=False) self.end_request(req, 0L, FCGI_CANT_MPX_CONN, remove=False)
else: else:
self._requests[inrec.requestId] = req self._requests[inrec.requestId] = req
@@ -863,7 +854,7 @@ class MultiplexedConnection(Connection):
finally: finally:
self._lock.release() self._lock.release()
def end_request(self, req, appStatus=0, def end_request(self, req, appStatus=0L,
protocolStatus=FCGI_REQUEST_COMPLETE, remove=True): protocolStatus=FCGI_REQUEST_COMPLETE, remove=True):
self._lock.acquire() self._lock.acquire()
try: try:
@@ -958,7 +949,8 @@ class Server(object):
this keyword is ignored; it's not possible to multiplex requests this keyword is ignored; it's not possible to multiplex requests
at all. at all.
""" """
self.handler = handler or self.default_handler if handler is not None:
self.handler = handler
self.maxwrite = maxwrite self.maxwrite = maxwrite
if thread_available: if thread_available:
try: try:
@@ -1085,7 +1077,7 @@ class Server(object):
while self._keepGoing: while self._keepGoing:
try: try:
r, w, e = select.select([sock], [], [], timeout) r, w, e = select.select([sock], [], [], timeout)
except select.error as e: except select.error, e:
if e.errno == errno.EINTR: if e.errno == errno.EINTR:
continue continue
raise raise
@@ -1133,13 +1125,13 @@ class Server(object):
self._keepGoing = False self._keepGoing = False
self._hupReceived = reload self._hupReceived = reload
def default_handler(self, req): def handler(self, req):
""" """
Default handler, which just raises an exception. Unless a handler Default handler, which just raises an exception. Unless a handler
is passed at initialization time, this must be implemented by is passed at initialization time, this must be implemented by
a subclass. a subclass.
""" """
raise NotImplementedError(self.__class__.__name__ + '.handler') raise NotImplementedError, self.__class__.__name__ + '.handler'
def error(self, req): def error(self, req):
""" """
@@ -1155,7 +1147,8 @@ class WSGIServer(Server):
FastCGI server that supports the Web Server Gateway Interface. See FastCGI server that supports the Web Server Gateway Interface. See
<http://www.python.org/peps/pep-0333.html>. <http://www.python.org/peps/pep-0333.html>.
""" """
def __init__(self, application, environ=None, multithreaded=True, **kw): def __init__(self, application, environ=None,
multithreaded=True, **kw):
""" """
environ, if present, must be a dictionary-like object. Its environ, if present, must be a dictionary-like object. Its
contents will be copied into application's environ. Useful contents will be copied into application's environ. Useful
@@ -1163,7 +1156,7 @@ class WSGIServer(Server):
Set multithreaded to False if your application is not MT-safe. Set multithreaded to False if your application is not MT-safe.
""" """
if 'handler'in kw: if kw.has_key('handler'):
del kw['handler'] # Doesn't make sense to let this through del kw['handler'] # Doesn't make sense to let this through
super(WSGIServer, self).__init__(**kw) super(WSGIServer, self).__init__(**kw)
@@ -1177,7 +1170,7 @@ class WSGIServer(Server):
# Used to force single-threadedness # Used to force single-threadedness
self._app_lock = thread.allocate_lock() self._app_lock = thread.allocate_lock()
def default_handler(self, req): def handler(self, req):
"""Special handler for WSGI.""" """Special handler for WSGI."""
if req.role != FCGI_RESPONDER: if req.role != FCGI_RESPONDER:
return FCGI_UNKNOWN_ROLE, 0 return FCGI_UNKNOWN_ROLE, 0
@@ -1247,7 +1240,7 @@ class WSGIServer(Server):
try: try:
if headers_sent: if headers_sent:
# Re-raise if too late # Re-raise if too late
raise exc_info[0](exc_info[1], exc_info[2]) raise exc_info[0], exc_info[1], exc_info[2]
finally: finally:
exc_info = None # avoid dangling circular ref exc_info = None # avoid dangling circular ref
else: else:
@@ -1311,6 +1304,7 @@ class WSGIServer(Server):
if __name__ == '__main__': if __name__ == '__main__':
def test_app(environ, start_response): def test_app(environ, start_response):
"""Probably not the most efficient example.""" """Probably not the most efficient example."""
import cgi
start_response('200 OK', [('Content-Type', 'text/html')]) start_response('200 OK', [('Content-Type', 'text/html')])
yield '<html><head><title>Hello World!</title></head>\n' \ yield '<html><head><title>Hello World!</title></head>\n' \
'<body>\n' \ '<body>\n' \
@@ -1319,10 +1313,10 @@ if __name__ == '__main__':
names = environ.keys() names = environ.keys()
names.sort() names.sort()
for name in names: for name in names:
yield '<tr><td>%s</td><td>%s</td></tr>\n' % (name, escape(environ[name])) yield '<tr><td>%s</td><td>%s</td></tr>\n' % (
name, cgi.escape(`environ[name]`))
form = cgi.FieldStorage(fp=environ['wsgi.input'], form = cgi.FieldStorage(fp=environ['wsgi.input'], environ=environ,
environ=environ,
keep_blank_values=1) keep_blank_values=1)
if form.list: if form.list:
yield '<tr><th colspan="2">Form data</th></tr>' yield '<tr><th colspan="2">Form data</th></tr>'
+1 -1
View File
@@ -109,7 +109,7 @@ def saml2_handler(session, request, config_filename = None, entityid = None):
client = Saml2Client(config_file = config_filename) client = Saml2Client(config_file = config_filename)
if not entityid: if not entityid:
idps = client.metadata.with_descriptor("idpsso") idps = client.metadata.with_descriptor("idpsso")
entityid = list(idps.keys())[0] entityid = idps.keys()[0]
bindings = [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST] bindings = [BINDING_HTTP_REDIRECT, BINDING_HTTP_POST]
binding, destination = client.pick_binding( binding, destination = client.pick_binding(
"single_sign_on_service", bindings, "idpsso", entity_id=entityid) "single_sign_on_service", bindings, "idpsso", entity_id=entityid)
+8 -13
View File
@@ -15,11 +15,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import sys import sys
if sys.version > '3':
if sys.version_info[0] < 3:
is_py2 = True
else:
is_py2 = False
unicode = str unicode = str
@@ -164,8 +160,7 @@ class SoapDispatcher(object):
# Now we change 'external' and 'model' to the received forms i.e. 'ext' and 'mod' # Now we change 'external' and 'model' to the received forms i.e. 'ext' and 'mod'
# After that we know how the client has prefixed additional namespaces # After that we know how the client has prefixed additional namespaces
decoded_xml = xml if is_py2 else xml.decode('utf8') ns = NS_RX.findall(xml)
ns = NS_RX.findall(decoded_xml)
for k, v in ns: for k, v in ns:
if v in self.namespaces.values(): if v in self.namespaces.values():
_ns_reversed[v] = k _ns_reversed[v] = k
@@ -374,23 +369,23 @@ class SoapDispatcher(object):
e['name'] = k e['name'] = k
if array: if array:
e[:] = {'minOccurs': "0", 'maxOccurs': "unbounded"} e[:] = {'minOccurs': "0", 'maxOccurs': "unbounded"}
if v is None: if v in TYPE_MAP.keys():
t = 'xsd:%s' % TYPE_MAP[v]
elif v is None:
t = 'xsd:anyType' t = 'xsd:anyType'
elif type(v) == list: elif isinstance(v, list):
n = "ArrayOf%s%s" % (name, k) n = "ArrayOf%s%s" % (name, k)
l = [] l = []
for d in v: for d in v:
l.extend(d.items()) l.extend(d.items())
parse_element(n, l, array=True, complex=True) parse_element(n, l, array=True, complex=True)
t = "tns:%s" % n t = "tns:%s" % n
elif type(v) == dict: elif isinstance(v, dict):
n = "%s%s" % (name, k) n = "%s%s" % (name, k)
parse_element(n, v.items(), complex=True) parse_element(n, v.items(), complex=True)
t = "tns:%s" % n t = "tns:%s" % n
elif v in TYPE_MAP:
t = 'xsd:%s' % TYPE_MAP[v]
else: else:
raise TypeError("unknown type %s for marshalling" % str(v)) raise TypeError("unknonw type %s for marshalling" % str(v))
e.add_attribute('type', t) e.add_attribute('type', t)
parse_element("%s" % method, args and args.items()) parse_element("%s" % method, args and args.items())
+1 -9
View File
@@ -242,15 +242,7 @@ class Request(Storage):
# parse POST variables on POST, PUT, BOTH only in post_vars # parse POST variables on POST, PUT, BOTH only in post_vars
if body and not is_json and env.request_method in ('POST', 'PUT', 'DELETE', 'BOTH'): if body and not is_json and env.request_method in ('POST', 'PUT', 'DELETE', 'BOTH'):
query_string = env.pop('QUERY_STRING', None) query_string = env.pop('QUERY_STRING', None)
content_disposition = env.get('HTTP_CONTENT_DISPOSITION') dpost = cgi.FieldStorage(fp=body, environ=env, keep_blank_values=1)
if content_disposition:
headers = {'content-disposition': content_disposition,
'content-type': env['CONTENT_TYPE'],
'content-length': env['CONTENT_LENGTH'],
}
else:
headers = None
dpost = cgi.FieldStorage(fp=body, environ=env, headers=headers, keep_blank_values=1)
try: try:
post_vars.update(dpost) post_vars.update(dpost)
except: except:
+7 -20
View File
@@ -190,8 +190,7 @@ def URL(a=None,
port=None, port=None,
encode_embedded_slash=False, encode_embedded_slash=False,
url_encode=True, url_encode=True,
language=None, language=None
hash_extension=True
): ):
""" """
generates a url '/a/c/f' corresponding to application a, controller c generates a url '/a/c/f' corresponding to application a, controller c
@@ -340,8 +339,7 @@ def URL(a=None,
if '.' in function: if '.' in function:
function, extension = function.rsplit('.', 1) function, extension = function.rsplit('.', 1)
# only include the extension as part of the variables for the hash if requested function2 = '%s.%s' % (function, extension or 'html')
function2 = '%s.%s' % (function, extension or 'html') if hash_extension else function
if not (application and controller and function): if not (application and controller and function):
raise SyntaxError('not enough information to build the url (%s %s %s)' % (application, controller, function)) raise SyntaxError('not enough information to build the url (%s %s %s)' % (application, controller, function))
@@ -418,7 +416,7 @@ def URL(a=None,
return url return url
def verifyURL(request, hmac_key=None, hash_vars=True, salt=None, user_signature=None, hash_extension=True): def verifyURL(request, hmac_key=None, hash_vars=True, salt=None, user_signature=None):
""" """
Verifies that a request's args & vars have not been tampered with by the user Verifies that a request's args & vars have not been tampered with by the user
@@ -479,19 +477,10 @@ def verifyURL(request, hmac_key=None, hash_vars=True, salt=None, user_signature=
# always include all of the args # always include all of the args
other = args and urllib_quote('/' + '/'.join([str(x) for x in args])) or '' other = args and urllib_quote('/' + '/'.join([str(x) for x in args])) or ''
h_args = '/%s/%s/%s.%s%s' % (request.application,
# decide whether the extension should be part of the hash verification
h_extension = request.extension if hash_extension else ''
# only add a period to the extension when it exists otherwise empty
# extensions will fail to validate
if h_extension:
h_extension = '.%s' % (h_extension)
h_args = '/%s/%s/%s%s%s' % (request.application,
request.controller, request.controller,
request.function, request.function,
h_extension, request.extension,
other) other)
# but only include those vars specified (allows more flexibility for use with # but only include those vars specified (allows more flexibility for use with
@@ -1461,10 +1450,8 @@ class SCRIPT(DIV):
(fa, co) = self._xml() (fa, co) = self._xml()
fa = to_bytes(fa) fa = to_bytes(fa)
# no escaping of subcomponents # no escaping of subcomponents
co = b'\n'.join(map(to_bytes, co = b'\n'.join([to_bytes(component) for component in
# allow xml components (i.e. ASSIGNJS) self.components])
map(lambda c: c.xml() if hasattr(c, 'xml') and callable(c.xml) else c,
self.components)))
if co: if co:
# <script [attributes]><!--//--><![CDATA[//><!-- # <script [attributes]><!--//--><![CDATA[//><!--
# script body # script body
+20 -19
View File
@@ -56,18 +56,19 @@ create_missing_folders()
# set up logging for subsequent imports # set up logging for subsequent imports
import logging.config import logging.config
# do not need fancy graphical loggers when served by handler # This needed to prevent exception on Python 2.5:
# (e.g. apache + mod_wsgi), speed up execution # NameError: name 'gluon' is not defined
if not global_settings.web2py_runtime_handler: # See http://bugs.python.org/issue1436
# attention!, the import Tkinter in messageboxhandler, changes locale ...
import gluon.messageboxhandler # attention!, the import Tkinter in messageboxhandler, changes locale ...
# This needed to prevent exception on Python 2.5: import gluon.messageboxhandler
# NameError: name 'gluon' is not defined logging.gluon = gluon
# See http://bugs.python.org/issue1436 # so we must restore it! Thanks ozancag
logging.gluon = gluon import locale
# so we must restore it! Thanks ozancag locale.setlocale(locale.LC_CTYPE, "C") # IMPORTANT, web2py requires locale "C"
import locale
locale.setlocale(locale.LC_CTYPE, "C") # IMPORTANT, web2py requires locale "C" exists = os.path.exists
pjoin = os.path.join
try: try:
logging.config.fileConfig(abspath("logging.conf")) logging.config.fileConfig(abspath("logging.conf"))
@@ -75,9 +76,6 @@ except: # fails on GAE or when logfile is missing
logging.basicConfig() logging.basicConfig()
logger = logging.getLogger("web2py") logger = logging.getLogger("web2py")
exists = os.path.exists
pjoin = os.path.join
from gluon.restricted import RestrictedError from gluon.restricted import RestrictedError
from gluon.http import HTTP, redirect from gluon.http import HTTP, redirect
from gluon.globals import Request, Response, Session from gluon.globals import Request, Response, Session
@@ -101,14 +99,17 @@ requests = 0 # gc timer
try: try:
version_info = read_file(pjoin(global_settings.gluon_parent, 'VERSION')) version_info = read_file(pjoin(global_settings.gluon_parent, 'VERSION'))
web2py_version = global_settings.web2py_version = version_info.split()[-1].strip() raw_version_string = version_info.split()[-1].strip()
global_settings.web2py_version = raw_version_string
web2py_version = global_settings.web2py_version
except: except:
raise RuntimeError("Cannot determine web2py version") raise RuntimeError("Cannot determine web2py version")
# do not need rocket nor HttpServer when served by handler try:
# (e.g. apache + mod_wsgi), speed up execution and save memory
if not global_settings.web2py_runtime_handler:
from gluon import rocket from gluon import rocket
except:
if not global_settings.web2py_runtime_gae:
logger.warn('unable to import Rocket')
load_routes() load_routes()
+394 -607
View File
File diff suppressed because it is too large Load Diff
+1 -5
View File
@@ -23,11 +23,7 @@ HTTP_SERVER_SOFTWARE = '%s Python/%s' % (
BUF_SIZE = 16384 BUF_SIZE = 16384
SOCKET_TIMEOUT = 10 # in secs SOCKET_TIMEOUT = 10 # in secs
THREAD_STOP_CHECK_INTERVAL = 1 # in secs, How often should threads check for a server stop message? THREAD_STOP_CHECK_INTERVAL = 1 # in secs, How often should threads check for a server stop message?
if hasattr(sys, 'frozen'): IS_JYTHON = platform.system() == 'Java' # Handle special cases for Jython
# py2installer
IS_JYTHON = False
else:
IS_JYTHON = platform.system() == 'Java' # Handle special cases for Jython
IGNORE_ERRORS_ON_CLOSE = set([errno.ECONNABORTED, errno.ECONNRESET]) IGNORE_ERRORS_ON_CLOSE = set([errno.ECONNABORTED, errno.ECONNRESET])
DEFAULT_LISTEN_QUEUE_SIZE = 5 DEFAULT_LISTEN_QUEUE_SIZE = 5
DEFAULT_MIN_THREADS = 10 DEFAULT_MIN_THREADS = 10
+1 -1
View File
@@ -84,7 +84,7 @@ def custom_json(o):
elif isinstance(o, decimal.Decimal): elif isinstance(o, decimal.Decimal):
return float(o) return float(o)
elif isinstance(o, (bytes, bytearray)): elif isinstance(o, (bytes, bytearray)):
return str(o) if hasattr(str, 'decode') else str(o, encoding='utf-8') return str(o)
elif isinstance(o, lazyT): elif isinstance(o, lazyT):
return str(o) return str(o)
elif isinstance(o, XmlComponent): elif isinstance(o, XmlComponent):
-4
View File
@@ -43,7 +43,3 @@ global_settings.is_source = os.path.exists(os.path.join(
global_settings.gluon_parent, 'web2py.py')) global_settings.gluon_parent, 'web2py.py'))
global_settings.is_py2 = PY2 global_settings.is_py2 = PY2
# allow admin app for clients on trusted LAN when over plain http,
# default is to allow only from localhost or when serving https
#global_settings.trusted_lan_prefix = '192.168.0.'
+1 -20
View File
@@ -20,7 +20,6 @@ import logging
import types import types
import re import re
import glob import glob
import site
import traceback import traceback
import gluon.fileutils as fileutils import gluon.fileutils as fileutils
from gluon.settings import global_settings from gluon.settings import global_settings
@@ -241,16 +240,13 @@ def run(
if not cron_job and not scheduler_job and \ if not cron_job and not scheduler_job and \
sys.stdin and not sys.stdin.name == '/dev/null': sys.stdin and not sys.stdin.name == '/dev/null':
confirm = raw_input( confirm = raw_input(
'application %s does not exist, create (y/N)?' % a) 'application %s does not exist, create (y/n)?' % a)
else: else:
logging.warn('application does not exist and will not be created') logging.warn('application does not exist and will not be created')
return return
if confirm.lower() in ('y', 'yes'): if confirm.lower() in ('y', 'yes'):
os.mkdir(adir) os.mkdir(adir)
fileutils.create_app(adir) fileutils.create_app(adir)
else:
logging.warn('application folder does not exist and has not been created as requested')
return
if force_migrate: if force_migrate:
c = 'appadmin' # Load all models (hack already used for appadmin controller) c = 'appadmin' # Load all models (hack already used for appadmin controller)
@@ -307,11 +303,6 @@ def run(
if import_models: if import_models:
BaseAdapter.close_all_instances('commit') BaseAdapter.close_all_instances('commit')
except SystemExit:
print(traceback.format_exc())
if import_models:
BaseAdapter.close_all_instances('rollback')
raise
except: except:
print(traceback.format_exc()) print(traceback.format_exc())
if import_models: if import_models:
@@ -321,11 +312,6 @@ def run(
exec(python_code, _env) exec(python_code, _env)
if import_models: if import_models:
BaseAdapter.close_all_instances('commit') BaseAdapter.close_all_instances('commit')
except SystemExit:
print(traceback.format_exc())
if import_models:
BaseAdapter.close_all_instances('rollback')
raise
except: except:
print(traceback.format_exc()) print(traceback.format_exc())
if import_models: if import_models:
@@ -335,11 +321,6 @@ def run(
execfile("scripts/migrator.py", _env) execfile("scripts/migrator.py", _env)
if import_models: if import_models:
BaseAdapter.close_all_instances('commit') BaseAdapter.close_all_instances('commit')
except SystemExit:
print(traceback.format_exc())
if import_models:
BaseAdapter.close_all_instances('rollback')
raise
except: except:
print(traceback.format_exc()) print(traceback.format_exc())
if import_models: if import_models:
+9 -13
View File
@@ -99,7 +99,7 @@ class CacheRepresenter(object):
nvalue = field.represent(value, row[field.tablename]) nvalue = field.represent(value, row[field.tablename])
except KeyError: except KeyError:
nvalue = None nvalue = None
if isinstance(field.represent, _repr_ref): # BKR ISSUE/2312 20200422 if isinstance(field, _repr_ref):
cache[field][value] = nvalue cache[field][value] = nvalue
return nvalue return nvalue
@@ -1330,7 +1330,7 @@ class SQLFORM(FORM):
# - user not trying to upload a new file # - user not trying to upload a new file
# - there is existing file and user is not trying to delete it # - there is existing file and user is not trying to delete it
# this is because removing the file may not pass validation # this is because removing the file may not pass validation
for key in list(self.errors): for key in self.errors.keys():
if key in self.table \ if key in self.table \
and self.table[key].type == 'upload' \ and self.table[key].type == 'upload' \
and request_vars.get(key, None) in (None, '') \ and request_vars.get(key, None) in (None, '') \
@@ -1501,7 +1501,7 @@ class SQLFORM(FORM):
if readonly and not ignore_rw and not field.readable: if readonly and not ignore_rw and not field.readable:
continue continue
if record and fieldname not in [x.name for x in extra_fields]: if record:
default = record[fieldname] default = record[fieldname]
else: else:
default = field.default default = field.default
@@ -2667,7 +2667,7 @@ class SQLFORM(FORM):
if export_type: if export_type:
order = request.vars.order or '' order = request.vars.order or ''
if sortable: if sortable:
if order: if order and not order == 'None':
otablename, ofieldname = order.split('~')[-1].split('.', 1) otablename, ofieldname = order.split('~')[-1].split('.', 1)
sort_field = db[otablename][ofieldname] sort_field = db[otablename][ofieldname]
orderby = sort_field if order[:1] != '~' else ~sort_field orderby = sort_field if order[:1] != '~' else ~sort_field
@@ -2800,7 +2800,7 @@ class SQLFORM(FORM):
order = request.vars.order or '' order = request.vars.order or ''
asc_icon, desc_icon = sorter_icons asc_icon, desc_icon = sorter_icons
if sortable: if sortable:
if order: if order and not order == 'None':
otablename, ofieldname = order.split('~')[-1].split('.', 1) otablename, ofieldname = order.split('~')[-1].split('.', 1)
sort_field = db[otablename][ofieldname] sort_field = db[otablename][ofieldname]
orderby = sort_field if order[:1] != '~' else ~sort_field orderby = sort_field if order[:1] != '~' else ~sort_field
@@ -2820,14 +2820,14 @@ class SQLFORM(FORM):
if key == order.lstrip('~'): if key == order.lstrip('~'):
if inverted: if inverted:
if key == order: if key == order:
marker = asc_icon key, marker = 'None', asc_icon
else: else:
key, marker = order[1:], desc_icon key, marker = order[1:], desc_icon
else: else:
if key == order: if key == order:
key, marker = '~' + order, asc_icon key, marker = '~' + order, asc_icon
else: else:
marker = desc_icon key, marker = 'None', desc_icon
elif inverted and key == str(field): elif inverted and key == str(field):
key = '~' + key key = '~' + key
header = A(header, marker, _href=url(vars=dict( header = A(header, marker, _href=url(vars=dict(
@@ -3393,7 +3393,6 @@ class SQLTABLE(TABLE):
linkto: URL (or lambda to generate a URL) to edit individual records linkto: URL (or lambda to generate a URL) to edit individual records
upload: URL to download uploaded files upload: URL to download uploaded files
orderby: Add an orderby link to column headers. orderby: Add an orderby link to column headers.
query: Query string to support orderby headers.
headers: dictionary of headers to headers redefinions headers: dictionary of headers to headers redefinions
headers can also be a string to generate the headers from data headers can also be a string to generate the headers from data
for now only headers="fieldname:capitalize", for now only headers="fieldname:capitalize",
@@ -3429,7 +3428,6 @@ class SQLTABLE(TABLE):
linkto=None, linkto=None,
upload=None, upload=None,
orderby=None, orderby=None,
query='',
headers={}, headers={},
truncate=16, truncate=16,
columns=None, columns=None,
@@ -3501,10 +3499,8 @@ class SQLTABLE(TABLE):
attrcol.update(_class=coldict['class']) attrcol.update(_class=coldict['class'])
row.append(TH(coldict['label'], **attrcol)) row.append(TH(coldict['label'], **attrcol))
elif orderby: elif orderby:
link = th_link + '?orderby=' + c row.append(TH(A(headers.get(c, c),
if query: _href=th_link + '?orderby=' + c, cid=cid)))
link += '&query=' + query
row.append(TH(A(headers.get(c, c), _href=link, cid=cid)))
else: else:
row.append(TH(headers.get(c, re.sub(self.REGEX_ALIAS_MATCH, r'\2', c)))) row.append(TH(headers.get(c, re.sub(self.REGEX_ALIAS_MATCH, r'\2', c))))
+1 -1
View File
@@ -188,7 +188,7 @@ class TestBareHelpers(unittest.TestCase):
XML('<h1>HelloWorld</h1>').__repr__()) XML('<h1>HelloWorld</h1>').__repr__())
# bug check for the sanitizer for closing no-close tags # bug check for the sanitizer for closing no-close tags
self.assertEqual(XML('<p>Test</p><br/><p>Test</p><br/>', sanitize=True).xml(), self.assertEqual(XML('<p>Test</p><br/><p>Test</p><br/>', sanitize=True).xml(),
XML('<p>Test</p><br/><p>Test</p><br/>').xml()) XML('<p>Test</p><br /><p>Test</p><br />').xml())
# basic flatten test # basic flatten test
self.assertEqual(XML('<p>Test</p>').flatten(), '<p>Test</p>') self.assertEqual(XML('<p>Test</p>').flatten(), '<p>Test</p>')
self.assertEqual(XML('<p>Test</p>').flatten(render=lambda text, tag, attr: text), '<p>Test</p>') self.assertEqual(XML('<p>Test</p>').flatten(render=lambda text, tag, attr: text), '<p>Test</p>')
+10 -15
View File
@@ -816,15 +816,7 @@ class Mail(object):
server.login(*self.settings.login.split(':', 1)) server.login(*self.settings.login.split(':', 1))
result = server.sendmail(sender, to, payload.as_string()) result = server.sendmail(sender, to, payload.as_string())
finally: finally:
# do not want to hide errors raising some exception here server.quit()
try:
server.quit()
except smtplib.SMTPException:
# ensure to close any socket with SMTP server
try:
server.close()
except Exception:
pass
except Exception as e: except Exception as e:
logger.warning('Mail.send failure:%s' % e) logger.warning('Mail.send failure:%s' % e)
self.result = result self.result = result
@@ -992,15 +984,15 @@ def addrow(form, a, b, c, style, _id, position=-1):
DIV(b, SPAN(c, _class='inline-help'), DIV(b, SPAN(c, _class='inline-help'),
_class='controls'), _class='controls'),
_class='control-group', _id=_id)) _class='control-group', _id=_id))
elif style in ("bootstrap3_inline", "bootstrap4_inline"): elif style == "bootstrap3_inline":
form[0].insert(position, DIV(LABEL(a, _class='control-label col-sm-3'), form[0].insert(position, DIV(LABEL(a, _class='control-label col-sm-3'),
DIV(b, SPAN(c, _class='help-block'), DIV(b, SPAN(c, _class='help-block'),
_class='col-sm-9'), _class='col-sm-9'),
_class='form-group row', _id=_id)) _class='form-group', _id=_id))
elif style in ("bootstrap3_stacked", "bootstrap4_stacked"): elif style == "bootstrap3_stacked":
form[0].insert(position, DIV(LABEL(a, _class='control-label'), form[0].insert(position, DIV(LABEL(a, _class='control-label'),
b, SPAN(c, _class='help-block'), b, SPAN(c, _class='help-block'),
_class='form-group row', _id=_id)) _class='form-group', _id=_id))
else: else:
form[0].insert(position, TR(TD(LABEL(a), _class='w2p_fl'), form[0].insert(position, TR(TD(LABEL(a), _class='w2p_fl'),
TD(b, _class='w2p_fw'), TD(b, _class='w2p_fw'),
@@ -3930,7 +3922,7 @@ class Auth(AuthAPI):
return self.has_permission(name, table_name, record_id) return self.has_permission(name, table_name, record_id)
return self.requires(has_permission, otherwise=otherwise) return self.requires(has_permission, otherwise=otherwise)
def requires_signature(self, otherwise=None, hash_vars=True, hash_extension=True): def requires_signature(self, otherwise=None, hash_vars=True):
""" """
Decorator that prevents access to action if not logged in or Decorator that prevents access to action if not logged in or
if user logged in is not a member of group_id. if user logged in is not a member of group_id.
@@ -3938,7 +3930,7 @@ class Auth(AuthAPI):
group_id is calculated. group_id is calculated.
""" """
def verify(): def verify():
return URL.verify(current.request, user_signature=True, hash_vars=hash_vars, hash_extension=True) return URL.verify(current.request, user_signature=True, hash_vars=hash_vars)
return self.requires(verify, otherwise) return self.requires(verify, otherwise)
def accessible_query(self, name, table, user_id=None): def accessible_query(self, name, table, user_id=None):
@@ -5613,6 +5605,9 @@ class Expose(object):
and file creation under `base`. and file creation under `base`.
""" """
# why would this not be callable? but otherwise tests do not pass
if current.session and callable(current.session.forget):
current.session.forget()
self.follow_symlink_out = follow_symlink_out self.follow_symlink_out = follow_symlink_out
self.base = self.normalize_path( self.base = self.normalize_path(
base or os.path.join(current.request.folder, 'static')) base or os.path.join(current.request.folder, 'static'))
+9 -13
View File
@@ -704,9 +704,14 @@ def start():
run_system_tests(options) run_system_tests(options)
if options.quiet: if options.quiet:
# mute existing loggers, to do that iterate # to prevent writes on stdout set a null stream
# over all loggers (root logger included) and remove class NullFile(object):
# attached logging.StreamHandler instances currently def write(self, x):
pass
sys.stdout = NullFile()
# but still has to mute existing loggers, to do that iterate
# over all existing loggers (root logger included) and remove
# all attached logging.StreamHandler instances currently
# streaming on sys.stdout or sys.stderr # streaming on sys.stdout or sys.stderr
loggers = [logging.getLogger()] loggers = [logging.getLogger()]
loggers.extend(logging.Logger.manager.loggerDict.values()) loggers.extend(logging.Logger.manager.loggerDict.values())
@@ -716,18 +721,9 @@ def start():
if isinstance(h, logging.StreamHandler) and \ if isinstance(h, logging.StreamHandler) and \
h.stream in (sys.stdout, sys.stderr): h.stream in (sys.stdout, sys.stderr):
l.removeHandler(h) l.removeHandler(h)
# this is to avoid the warning
# ``No handlers could be found for logger "..."``
# emitted by logging module when no handler is found
logging.Logger.manager.emittedNoHandlerWarning = 1
# to prevent writes on stdout set a null stream
class NullFile(object):
def write(self, x):
pass
sys.stdout = NullFile()
# NOTE: stderr.write() is still working # NOTE: stderr.write() is still working
elif not options.no_banner: if not options.no_banner:
# banner # banner
print(ProgramName) print(ProgramName)
print(ProgramAuthor) print(ProgramAuthor)
-3
View File
@@ -62,9 +62,6 @@ if not os.path.isdir('applications'):
sys.path = [path] + [p for p in sys.path if not p == path] sys.path = [path] + [p for p in sys.path if not p == path]
from gluon.settings import global_settings
global_settings.web2py_runtime_handler = True
import gluon.main import gluon.main
wsgiref.handlers.CGIHandler().run(gluon.main.wsgibase) wsgiref.handlers.CGIHandler().run(gluon.main.wsgibase)
+1 -3
View File
@@ -40,9 +40,6 @@ if not os.path.isdir('applications'):
sys.path = [path] + [p for p in sys.path if not p == path] sys.path = [path] + [p for p in sys.path if not p == path]
from gluon.settings import global_settings
global_settings.web2py_runtime_handler = True
import gluon.main import gluon.main
import gluon.contrib.gateways.fcgi as fcgi import gluon.contrib.gateways.fcgi as fcgi
@@ -54,6 +51,7 @@ else:
application = gluon.main.wsgibase application = gluon.main.wsgibase
if SOFTCRON: if SOFTCRON:
from gluon.settings import global_settings
global_settings.web2py_crontype = 'soft' global_settings.web2py_crontype = 'soft'
fcgi.WSGIServer(application, bindAddress='/tmp/fcgi.sock').run() fcgi.WSGIServer(application, bindAddress='/tmp/fcgi.sock').run()
+4 -5
View File
@@ -49,15 +49,14 @@ from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app from google.appengine.ext.webapp.util import run_wsgi_app
global_settings.web2py_runtime_handler = True
global_settings.web2py_runtime_gae = True global_settings.web2py_runtime_gae = True
global_settings.db_sessions = True global_settings.db_sessions = True
if os.environ.get('SERVER_SOFTWARE', '').startswith('Devel'): if os.environ.get('SERVER_SOFTWARE', '').startswith('Devel'):
global_settings.web2py_runtime = 'gae:development' (global_settings.web2py_runtime, DEBUG) = \
DEBUG = True ('gae:development', True)
else: else:
global_settings.web2py_runtime = 'gae:production' (global_settings.web2py_runtime, DEBUG) = \
DEBUG = False ('gae:production', False)
import gluon.main import gluon.main
-2
View File
@@ -13,8 +13,6 @@ def __ExtensionFactory__():
if not os.path.isdir('applications'): if not os.path.isdir('applications'):
raise RuntimeError('Running from the wrong folder') raise RuntimeError('Running from the wrong folder')
sys.path = [path] + [p for p in sys.path if not p == path] sys.path = [path] + [p for p in sys.path if not p == path]
from gluon.settings import global_settings
global_settings.web2py_runtime_handler = True
import gluon.main import gluon.main
import isapi_wsgi import isapi_wsgi
application = gluon.main.wsgibase application = gluon.main.wsgibase
-3
View File
@@ -40,9 +40,6 @@ if not os.path.isdir('applications'):
sys.path = [path] + [p for p in sys.path if not p == path] sys.path = [path] + [p for p in sys.path if not p == path]
from gluon.settings import global_settings
global_settings.web2py_runtime_handler = True
import gluon.main import gluon.main
+1 -3
View File
@@ -53,9 +53,6 @@ if not os.path.isdir('applications'):
sys.path = [path] + [p for p in sys.path if not p == path] sys.path = [path] + [p for p in sys.path if not p == path]
from gluon.settings import global_settings
global_settings.web2py_runtime_handler = True
import gluon.main import gluon.main
# uncomment one of the two imports below depending on the SCGIWSGI server installed # uncomment one of the two imports below depending on the SCGIWSGI server installed
@@ -73,6 +70,7 @@ else:
application = wsgiapp application = wsgiapp
if SOFTCRON: if SOFTCRON:
from gluon.settings import global_settings
global_settings.web2py_crontype = 'soft' global_settings.web2py_crontype = 'soft'
# uncomment one of the two rows below depending on the SCGIWSGI server installed # uncomment one of the two rows below depending on the SCGIWSGI server installed
-2
View File
@@ -21,8 +21,6 @@ from gevent import monkey
monkey.patch_all() monkey.patch_all()
def run(options): def run(options):
from gluon.settings import global_settings
global_settings.web2py_runtime_handler = True
import gluon.main import gluon.main
if options.password != '<recycle>': if options.password != '<recycle>':
gluon.main.save_password(options.password, int(options.port)) gluon.main.save_password(options.password, int(options.port))
+1 -3
View File
@@ -26,9 +26,6 @@ if not os.path.isdir('applications'):
sys.path = [path] + [p for p in sys.path if not p == path] sys.path = [path] + [p for p in sys.path if not p == path]
from gluon.settings import global_settings
global_settings.web2py_runtime_handler = True
import gluon.main import gluon.main
if LOGGING: if LOGGING:
@@ -39,4 +36,5 @@ else:
application = gluon.main.wsgibase application = gluon.main.wsgibase
if SOFTCRON: if SOFTCRON:
from gluon.settings import global_settings
global_settings.web2py_crontype = 'soft' global_settings.web2py_crontype = 'soft'
+12 -12
View File
@@ -41,7 +41,7 @@ from __future__ import with_statement
from gluon import current from gluon import current
from gluon.storage import Storage from gluon.storage import Storage
from gluon._compat import pickle from gluon._compat import pickle
from optparse import OptionParser
import datetime import datetime
import stat import stat
import time import time
@@ -62,12 +62,13 @@ class SessionSet(object):
def get(self): def get(self):
"""Get session files/records.""" """Get session files/records."""
raise NotImplementedError() raise NotImplementedError
def trash(self): def trash(self):
"""Trash expired sessions.""" """Trash expired sessions."""
now = datetime.datetime.now() now = datetime.datetime.now()
for item in self.get(): for item in self.get():
status = 'OK'
last_visit = item.last_visit_default() last_visit = item.last_visit_default()
try: try:
@@ -87,18 +88,16 @@ class SessionSet(object):
if age > self.expiration or not self.expiration: if age > self.expiration or not self.expiration:
item.delete() item.delete()
status = 'trashed' status = 'trashed'
else:
status = 'OK'
if self.verbose > 1: if self.verbose > 1:
print('key: %s' % item) print('key: %s' % str(item))
print('expiration: %s seconds' % self.expiration) print('expiration: %s seconds' % self.expiration)
print('last visit: %s' % last_visit) print('last visit: %s' % str(last_visit))
print('age: %s seconds' % age) print('age: %s seconds' % age)
print('status: %s' % status) print('status: %s' % status)
print('') print('')
elif self.verbose > 0: elif self.verbose > 0:
print('%s %s' % (item, status)) print('%s %s' % (str(item), status))
class SessionSetDb(SessionSet): class SessionSetDb(SessionSet):
@@ -180,8 +179,8 @@ class SessionFile(object):
def get(self): def get(self):
session = Storage() session = Storage()
with open(self.filename, 'rb') as f: with open(self.filename, 'rb+') as f:
session.update(pickle.load(f)) session.update(cPickle.load(f))
return session return session
def last_visit_default(self): def last_visit_default(self):
@@ -199,7 +198,8 @@ def total_seconds(delta):
Args: Args:
delta: datetime.timedelta instance. delta: datetime.timedelta instance.
""" """
return (delta.microseconds + (delta.seconds + (delta.days * 86400)) * 1000000) / 1e6 return (delta.microseconds + (delta.seconds + (delta.days * 24 * 3600)) *
10 ** 6) / 10 ** 6
def single_loop(expiration=None, force=False, verbose=False): def single_loop(expiration=None, force=False, verbose=False):
if expiration is None: if expiration is None:
@@ -216,9 +216,9 @@ def single_loop(expiration=None, force=False, verbose=False):
def main(): def main():
"""Main processing.""" """Main processing."""
from optparse import OptionParser
parser = OptionParser(usage="%%prog [options]\nVersion: %s" % VERSION) usage = '%prog [options]' + '\nVersion: %s' % VERSION
parser = OptionParser(usage=usage)
parser.add_option('-f', '--force', parser.add_option('-f', '--force',
action='store_true', dest='force', default=False, action='store_true', dest='force', default=False,
-3
View File
@@ -19,9 +19,6 @@ else:
# process -f (--folder) option # process -f (--folder) option
if '-f' in sys.argv: if '-f' in sys.argv:
fi = sys.argv.index('-f') fi = sys.argv.index('-f')
# maybe session2trash arg
if '-A' in sys.argv and fi > sys.argv.index('-A'):
fi = None
elif '--folder' in sys.argv: elif '--folder' in sys.argv:
fi = sys.argv.index('--folder') fi = sys.argv.index('--folder')
else: else: