Merge branch 'master' of github.com:web2py/web2py

This commit is contained in:
mdipierro
2015-03-31 14:45:59 -05:00
4 changed files with 347 additions and 366 deletions
+277 -309
View File
File diff suppressed because it is too large Load Diff
+2
View File
@@ -66,6 +66,7 @@ else:
logger = logging.getLogger("web2py") logger = logging.getLogger("web2py")
def AES_new(key, IV=None): def AES_new(key, IV=None):
""" Returns an AES cipher object and random IV if None specified """ """ Returns an AES cipher object and random IV if None specified """
if IV is None: if IV is None:
@@ -88,6 +89,7 @@ def md5_hash(text):
""" Generates a md5 hash with the given text """ """ Generates a md5 hash with the given text """
return md5(text).hexdigest() return md5(text).hexdigest()
def simple_hash(text, key='', salt='', digest_alg='md5'): def simple_hash(text, key='', salt='', digest_alg='md5'):
""" """
Generates hash with the given text using the specified Generates hash with the given text using the specified
+50 -40
View File
@@ -190,9 +190,9 @@ class IS_MATCH(Validator):
if not expression.endswith('$'): if not expression.endswith('$'):
expression = '(%s)$' % expression expression = '(%s)$' % expression
if is_unicode: if is_unicode:
if not isinstance(expression,unicode): if not isinstance(expression, unicode):
expression = expression.decode('utf8') expression = expression.decode('utf8')
self.regex = re.compile(expression,re.UNICODE) self.regex = re.compile(expression, re.UNICODE)
else: else:
self.regex = re.compile(expression) self.regex = re.compile(expression)
self.error_message = error_message self.error_message = error_message
@@ -200,7 +200,7 @@ class IS_MATCH(Validator):
self.is_unicode = is_unicode self.is_unicode = is_unicode
def __call__(self, value): def __call__(self, value):
if self.is_unicode and not isinstance(value,unicode): if self.is_unicode and not isinstance(value, unicode):
match = self.regex.search(str(value).decode('utf8')) match = self.regex.search(str(value).decode('utf8'))
else: else:
match = self.regex.search(str(value)) match = self.regex.search(str(value))
@@ -348,6 +348,7 @@ class IS_LENGTH(Validator):
return (value, translate(self.error_message) return (value, translate(self.error_message)
% dict(min=self.minsize, max=self.maxsize)) % dict(min=self.minsize, max=self.maxsize))
class IS_JSON(Validator): class IS_JSON(Validator):
""" """
Example: Example:
@@ -377,7 +378,7 @@ class IS_JSON(Validator):
except JSONErrors: except JSONErrors:
return (value, translate(self.error_message)) return (value, translate(self.error_message))
def formatter(self,value): def formatter(self, value):
if value is None: if value is None:
return None return None
if self.native_json: if self.native_json:
@@ -669,7 +670,7 @@ class IS_NOT_IN_DB(Validator):
self.record_id = id self.record_id = id
def __call__(self, value): def __call__(self, value):
if isinstance(value,unicode): if isinstance(value, unicode):
value = value.encode('utf8') value = value.encode('utf8')
else: else:
value = str(value) value = str(value)
@@ -696,7 +697,7 @@ class IS_NOT_IN_DB(Validator):
def range_error_message(error_message, what_to_enter, minimum, maximum): def range_error_message(error_message, what_to_enter, minimum, maximum):
"build the error message for the number range validators" """build the error message for the number range validators"""
if error_message is None: if error_message is None:
error_message = 'Enter ' + what_to_enter error_message = 'Enter ' + what_to_enter
if minimum is not None and maximum is not None: if minimum is not None and maximum is not None:
@@ -938,7 +939,7 @@ class IS_DECIMAL_IN_RANGE(Validator):
def is_empty(value, empty_regex=None): def is_empty(value, empty_regex=None):
"test empty field" """test empty field"""
if isinstance(value, (str, unicode)): if isinstance(value, (str, unicode)):
value = value.strip() value = value.strip()
if empty_regex is not None and empty_regex.match(value): if empty_regex is not None and empty_regex.match(value):
@@ -1153,6 +1154,7 @@ class IS_EMAIL(Validator):
return (value, None) return (value, None)
return (value, translate(self.error_message)) return (value, translate(self.error_message))
class IS_LIST_OF_EMAILS(object): class IS_LIST_OF_EMAILS(object):
""" """
Example: Example:
@@ -1166,7 +1168,8 @@ class IS_LIST_OF_EMAILS(object):
) )
""" """
split_emails = re.compile('[^,;\s]+') split_emails = re.compile('[^,;\s]+')
def __init__(self, error_message = 'Invalid emails: %s'):
def __init__(self, error_message='Invalid emails: %s'):
self.error_message = error_message self.error_message = error_message
def __call__(self, value): def __call__(self, value):
@@ -1182,7 +1185,7 @@ class IS_LIST_OF_EMAILS(object):
return (value, return (value,
translate(self.error_message) % ', '.join(bad_emails)) translate(self.error_message) % ', '.join(bad_emails))
def formatter(self,value,row=None): def formatter(self, value, row=None):
return ', '.join(value or []) return ', '.join(value or [])
@@ -1334,7 +1337,7 @@ label_split_regex = re.compile(u'[\u002e\u3002\uff0e\uff61]')
def escape_unicode(string): def escape_unicode(string):
''' """
Converts a unicode string into US-ASCII, using a simple conversion scheme. Converts a unicode string into US-ASCII, using a simple conversion scheme.
Each unicode character that does not have a US-ASCII equivalent is Each unicode character that does not have a US-ASCII equivalent is
converted into a URL escaped form based on its hexadecimal value. converted into a URL escaped form based on its hexadecimal value.
@@ -1348,7 +1351,7 @@ def escape_unicode(string):
string: the US-ASCII escaped form of the inputted string string: the US-ASCII escaped form of the inputted string
@author: Jonathan Benn @author: Jonathan Benn
''' """
returnValue = StringIO() returnValue = StringIO()
for character in string: for character in string:
@@ -1363,7 +1366,7 @@ def escape_unicode(string):
def unicode_to_ascii_authority(authority): def unicode_to_ascii_authority(authority):
''' """
Follows the steps in RFC 3490, Section 4 to convert a unicode authority Follows the steps in RFC 3490, Section 4 to convert a unicode authority
string into its ASCII equivalent. string into its ASCII equivalent.
For example, u'www.Alliancefran\xe7aise.nu' will be converted into For example, u'www.Alliancefran\xe7aise.nu' will be converted into
@@ -1382,19 +1385,19 @@ def unicode_to_ascii_authority(authority):
authority authority
@author: Jonathan Benn @author: Jonathan Benn
''' """
#RFC 3490, Section 4, Step 1 # RFC 3490, Section 4, Step 1
#The encodings.idna Python module assumes that AllowUnassigned == True # The encodings.idna Python module assumes that AllowUnassigned == True
#RFC 3490, Section 4, Step 2 # RFC 3490, Section 4, Step 2
labels = label_split_regex.split(authority) labels = label_split_regex.split(authority)
#RFC 3490, Section 4, Step 3 # RFC 3490, Section 4, Step 3
#The encodings.idna Python module assumes that UseSTD3ASCIIRules == False # The encodings.idna Python module assumes that UseSTD3ASCIIRules == False
#RFC 3490, Section 4, Step 4 # RFC 3490, Section 4, Step 4
#We use the ToASCII operation because we are about to put the authority # We use the ToASCII operation because we are about to put the authority
#into an IDN-unaware slot # into an IDN-unaware slot
asciiLabels = [] asciiLabels = []
try: try:
import encodings.idna import encodings.idna
@@ -1402,18 +1405,18 @@ def unicode_to_ascii_authority(authority):
if label: if label:
asciiLabels.append(encodings.idna.ToASCII(label)) asciiLabels.append(encodings.idna.ToASCII(label))
else: else:
#encodings.idna.ToASCII does not accept an empty string, but # encodings.idna.ToASCII does not accept an empty string, but
#it is necessary for us to allow for empty labels so that we # it is necessary for us to allow for empty labels so that we
#don't modify the URL # don't modify the URL
asciiLabels.append('') asciiLabels.append('')
except: except:
asciiLabels = [str(label) for label in labels] asciiLabels = [str(label) for label in labels]
#RFC 3490, Section 4, Step 5 # RFC 3490, Section 4, Step 5
return str(reduce(lambda x, y: x + unichr(0x002E) + y, asciiLabels)) return str(reduce(lambda x, y: x + unichr(0x002E) + y, asciiLabels))
def unicode_to_ascii_url(url, prepend_scheme): def unicode_to_ascii_url(url, prepend_scheme):
''' """
Converts the inputed unicode url into a US-ASCII equivalent. This function Converts the inputed unicode url into a US-ASCII equivalent. This function
goes a little beyond RFC 3490, which is limited in scope to the domain name goes a little beyond RFC 3490, which is limited in scope to the domain name
(authority) only. Here, the functionality is expanded to what was observed (authority) only. Here, the functionality is expanded to what was observed
@@ -1443,23 +1446,23 @@ def unicode_to_ascii_url(url, prepend_scheme):
string: a US-ASCII equivalent of the inputed url string: a US-ASCII equivalent of the inputed url
@author: Jonathan Benn @author: Jonathan Benn
''' """
#convert the authority component of the URL into an ASCII punycode string, # convert the authority component of the URL into an ASCII punycode string,
#but encode the rest using the regular URI character encoding # but encode the rest using the regular URI character encoding
groups = url_split_regex.match(url).groups() groups = url_split_regex.match(url).groups()
#If no authority was found # If no authority was found
if not groups[3]: if not groups[3]:
#Try appending a scheme to see if that fixes the problem # Try appending a scheme to see if that fixes the problem
scheme_to_prepend = prepend_scheme or 'http' scheme_to_prepend = prepend_scheme or 'http'
groups = url_split_regex.match( groups = url_split_regex.match(
unicode(scheme_to_prepend) + u'://' + url).groups() unicode(scheme_to_prepend) + u'://' + url).groups()
#if we still can't find the authority # if we still can't find the authority
if not groups[3]: if not groups[3]:
raise Exception('No authority component found, ' + raise Exception('No authority component found, ' +
'could not decode unicode to US-ASCII') 'could not decode unicode to US-ASCII')
#We're here if we found an authority, let's rebuild the URL # We're here if we found an authority, let's rebuild the URL
scheme = groups[1] scheme = groups[1]
authority = groups[3] authority = groups[3]
path = groups[4] or '' path = groups[4] or ''
@@ -2047,12 +2050,12 @@ class IS_URL(Validator):
try: try:
asciiValue = unicode_to_ascii_url(value, self.prepend_scheme) asciiValue = unicode_to_ascii_url(value, self.prepend_scheme)
except Exception: except Exception:
#If we are not able to convert the unicode url into a # If we are not able to convert the unicode url into a
# US-ASCII URL, then the URL is not valid # US-ASCII URL, then the URL is not valid
return (value, translate(self.error_message)) return (value, translate(self.error_message))
methodResult = subMethod(asciiValue) methodResult = subMethod(asciiValue)
#if the validation of the US-ASCII version of the value failed # if the validation of the US-ASCII version of the value failed
if not methodResult[1] is None: if not methodResult[1] is None:
# then return the original input value, not the US-ASCII version # then return the original input value, not the US-ASCII version
return (value, methodResult[1]) return (value, methodResult[1])
@@ -2120,7 +2123,7 @@ class IS_TIME(Validator):
if not value.group('s') is None: if not value.group('s') is None:
s = int(value.group('s')) s = int(value.group('s'))
if value.group('d') == 'pm' and 0 < h < 12: if value.group('d') == 'pm' and 0 < h < 12:
h = h + 12 h += 12
if value.group('d') == 'am' and h == 12: if value.group('d') == 'am' and h == 12:
h = 0 h = 0
if not (h in range(24) and m in range(60) and s if not (h in range(24) and m in range(60) and s
@@ -2134,18 +2137,23 @@ class IS_TIME(Validator):
pass pass
return (ivalue, translate(self.error_message)) return (ivalue, translate(self.error_message))
# A UTC class. # A UTC class.
class UTC(datetime.tzinfo): class UTC(datetime.tzinfo):
"""UTC""" """UTC"""
ZERO = datetime.timedelta(0) ZERO = datetime.timedelta(0)
def utcoffset(self, dt): def utcoffset(self, dt):
return UTC.ZERO return UTC.ZERO
def tzname(self, dt): def tzname(self, dt):
return "UTC" return "UTC"
def dst(self, dt): def dst(self, dt):
return UTC.ZERO return UTC.ZERO
utc = UTC() utc = UTC()
class IS_DATE(Validator): class IS_DATE(Validator):
""" """
Examples: Examples:
@@ -2159,7 +2167,7 @@ class IS_DATE(Validator):
def __init__(self, format='%Y-%m-%d', def __init__(self, format='%Y-%m-%d',
error_message='Enter date as %(format)s', error_message='Enter date as %(format)s',
timezone = None): timezone=None):
self.format = translate(format) self.format = translate(format)
self.error_message = str(error_message) self.error_message = str(error_message)
self.timezone = timezone self.timezone = timezone
@@ -2402,7 +2410,7 @@ class IS_LIST_OF(Validator):
new_value = [] new_value = []
other = self.other other = self.other
if self.other: if self.other:
if not isinstance(other, (list,tuple)): if not isinstance(other, (list, tuple)):
other = [other] other = [other]
for item in ivalue: for item in ivalue:
v = item v = item
@@ -2625,7 +2633,7 @@ class IS_EMPTY_OR(Validator):
return self.other.formatter(value) return self.other.formatter(value)
return value return value
IS_NULL_OR = IS_EMPTY_OR # for backward compatibility IS_NULL_OR = IS_EMPTY_OR # for backward compatibility
class CLEANUP(Validator): class CLEANUP(Validator):
@@ -2735,6 +2743,7 @@ class LazyCrypt(object):
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
class CRYPT(object): class CRYPT(object):
""" """
Examples: Examples:
@@ -2852,7 +2861,7 @@ otherset = frozenset(
def calc_entropy(string): def calc_entropy(string):
" calculates a simple entropy for a given string " """ calculates a simple entropy for a given string """
import math import math
alphabet = 0 # alphabet size alphabet = 0 # alphabet size
other = set() other = set()
@@ -3374,6 +3383,7 @@ class IS_IPV4(Validator):
return (value, None) return (value, None)
return (value, translate(self.error_message)) return (value, translate(self.error_message))
class IS_IPV6(Validator): class IS_IPV6(Validator):
""" """
Checks if field's value is an IP version 6 address. First attempts to Checks if field's value is an IP version 6 address. First attempts to
+18 -17
View File
@@ -143,7 +143,7 @@ class web2pyDialog(object):
root.withdraw() root.withdraw()
self.root = Tkinter.Toplevel(root, bg=bg_color) self.root = Tkinter.Toplevel(root, bg=bg_color)
self.root.resizable(0,0) self.root.resizable(0, 0)
self.root.title(ProgramName) self.root.title(ProgramName)
self.options = options self.options = options
@@ -151,7 +151,7 @@ class web2pyDialog(object):
self.menu = Tkinter.Menu(self.root) self.menu = Tkinter.Menu(self.root)
servermenu = Tkinter.Menu(self.menu, tearoff=0) servermenu = Tkinter.Menu(self.menu, tearoff=0)
httplog = os.path.join(self.options.folder, 'httpserver.log') httplog = os.path.join(self.options.folder, 'httpserver.log')
iconphoto = os.path.join('extras','icons','web2py.gif') iconphoto = os.path.join('extras', 'icons', 'web2py.gif')
if os.path.exists(iconphoto): if os.path.exists(iconphoto):
img = Tkinter.PhotoImage(file=iconphoto) img = Tkinter.PhotoImage(file=iconphoto)
self.root.tk.call('wm', 'iconphoto', self.root._w, img) self.root.tk.call('wm', 'iconphoto', self.root._w, img)
@@ -206,7 +206,7 @@ class web2pyDialog(object):
self.logoarea.grid(row=0, column=0, columnspan=4, sticky=sticky) self.logoarea.grid(row=0, column=0, columnspan=4, sticky=sticky)
self.logoarea.after(1000, self.update_canvas) self.logoarea.after(1000, self.update_canvas)
logo = os.path.join('extras','icons','splashlogo.gif') logo = os.path.join('extras', 'icons', 'splashlogo.gif')
if os.path.exists(logo): if os.path.exists(logo):
img = Tkinter.PhotoImage(file=logo) img = Tkinter.PhotoImage(file=logo)
pnl = Tkinter.Label(self.logoarea, image=img, background=bg_color, bd=0) pnl = Tkinter.Label(self.logoarea, image=img, background=bg_color, bd=0)
@@ -261,8 +261,8 @@ class web2pyDialog(object):
Tkinter.Label(self.root, Tkinter.Label(self.root,
text='Server Port:', bg=bg_color, text='Server Port:', bg=bg_color,
justify=Tkinter.RIGHT).grid(row=shift, justify=Tkinter.RIGHT).grid(row=shift,
column=1, pady=10, column=1, pady=10,
sticky=sticky) sticky=sticky)
self.port_number = Tkinter.Entry(self.root) self.port_number = Tkinter.Entry(self.root)
self.port_number.insert(Tkinter.END, self.options.port) self.port_number.insert(Tkinter.END, self.options.port)
@@ -272,8 +272,8 @@ class web2pyDialog(object):
Tkinter.Label(self.root, Tkinter.Label(self.root,
text='Choose Password:', bg=bg_color, text='Choose Password:', bg=bg_color,
justify=Tkinter.RIGHT).grid(row=shift + 1, justify=Tkinter.RIGHT).grid(row=shift + 1,
column=1, column=1,
sticky=sticky) sticky=sticky)
self.password = Tkinter.Entry(self.root, show='*') self.password = Tkinter.Entry(self.root, show='*')
self.password.bind('<Return>', lambda e: self.start()) self.password.bind('<Return>', lambda e: self.start())
@@ -328,7 +328,7 @@ class web2pyDialog(object):
if os.path.exists( if os.path.exists(
'applications/%s/models/scheduler.py' % arq)] 'applications/%s/models/scheduler.py' % arq)]
if start: if start:
#the widget takes care of starting the scheduler # the widget takes care of starting the scheduler
if self.options.scheduler and self.options.with_scheduler: if self.options.scheduler and self.options.with_scheduler:
apps = [app.strip() for app apps = [app.strip() for app
in self.options.scheduler.split(',') in self.options.scheduler.split(',')
@@ -336,7 +336,7 @@ class web2pyDialog(object):
for app in apps: for app in apps:
self.try_start_scheduler(app) self.try_start_scheduler(app)
#reset the menu # reset the menu
self.schedmenu.delete(0, len(available_apps)) self.schedmenu.delete(0, len(available_apps))
for arq in available_apps: for arq in available_apps:
if arq not in self.scheduler_processes: if arq not in self.scheduler_processes:
@@ -413,7 +413,7 @@ class web2pyDialog(object):
def connect_pages(self): def connect_pages(self):
""" Connects pages """ """ Connects pages """
#reset the menu # reset the menu
available_apps = [arq for arq in os.listdir('applications/') available_apps = [arq for arq in os.listdir('applications/')
if os.path.exists( if os.path.exists(
'applications/%s/__init__.py' % arq)] 'applications/%s/__init__.py' % arq)]
@@ -939,19 +939,19 @@ def console():
if options.gae: if options.gae:
if not os.path.exists('app.yaml'): if not os.path.exists('app.yaml'):
name = raw_input("Your GAE app name: ") name = raw_input("Your GAE app name: ")
content = open(os.path.join('examples','app.example.yaml'),'rb').read() content = open(os.path.join('examples', 'app.example.yaml'), 'rb').read()
open('app.yaml','wb').write(content.replace("yourappname",name)) open('app.yaml', 'wb').write(content.replace("yourappname", name))
else: else:
print "app.yaml alreday exists in the web2py folder" print "app.yaml alreday exists in the web2py folder"
if not os.path.exists('gaehandler.py'): if not os.path.exists('gaehandler.py'):
content = open(os.path.join('handlers','gaehandler.py'),'rb').read() content = open(os.path.join('handlers', 'gaehandler.py'), 'rb').read()
open('gaehandler.py','wb').write(content) open('gaehandler.py', 'wb').write(content)
else: else:
print "gaehandler.py alreday exists in the web2py folder" print "gaehandler.py alreday exists in the web2py folder"
sys.exit(0) sys.exit(0)
try: try:
options.ips = list(set( # no duplicates options.ips = list(set( # no duplicates
[addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn())
if not is_loopback_ip_address(addrinfo=addrinfo)])) if not is_loopback_ip_address(addrinfo=addrinfo)]))
except socket.gaierror: except socket.gaierror:
@@ -1112,7 +1112,7 @@ def start(cron=True):
if hasattr(options, key): if hasattr(options, key):
setattr(options, key, getattr(options2, key)) setattr(options, key, getattr(options2, key))
logfile0 = os.path.join('extras','examples','logging.example.conf') logfile0 = os.path.join('extras', 'examples', 'logging.example.conf')
if not os.path.exists('logging.conf') and os.path.exists(logfile0): if not os.path.exists('logging.conf') and os.path.exists(logfile0):
import shutil import shutil
sys.stdout.write("Copying logging.conf.example to logging.conf ... ") sys.stdout.write("Copying logging.conf.example to logging.conf ... ")
@@ -1183,7 +1183,7 @@ def start(cron=True):
root = None root = None
if not options.nogui and options.password=='<ask>': if not options.nogui and options.password == '<ask>':
try: try:
import Tkinter import Tkinter
havetk = True havetk = True
@@ -1263,6 +1263,7 @@ end tell
# if the line was not found (under py2exe & when file was modified) # if the line was not found (under py2exe & when file was modified)
import linecache import linecache
py2exe_getline = linecache.getline py2exe_getline = linecache.getline
def getline(filename, lineno, *args, **kwargs): def getline(filename, lineno, *args, **kwargs):
line = py2exe_getline(filename, lineno, *args, **kwargs) line = py2exe_getline(filename, lineno, *args, **kwargs)
if not line: if not line: