validatos no longer depend on T

This commit is contained in:
mdipierro
2019-02-24 01:51:18 -08:00
parent afaf57f6da
commit b58b7da18f
13 changed files with 129 additions and 130 deletions
+8 -4
View File
@@ -24,8 +24,9 @@ from gluon.template import parse_template
from gluon.restricted import restricted, compile2 from gluon.restricted import restricted, compile2
from gluon.fileutils import mktree, listdir, read_file, write_file from gluon.fileutils import mktree, listdir, read_file, write_file
from gluon.myregex import regex_expose, regex_longcomments from gluon.myregex import regex_expose, regex_longcomments
from gluon.languages import translator from gluon.languages import TranslatorFactory
from gluon.dal import DAL, Field from gluon.dal import DAL, Field
from gluon.validators import Validator
from pydal.base import BaseAdapter from pydal.base import BaseAdapter
from gluon.sqlhtml import SQLFORM, SQLTABLE from gluon.sqlhtml import SQLFORM, SQLTABLE
from gluon.cache import Cache from gluon.cache import Cache
@@ -423,16 +424,19 @@ def build_environment(request, response, session, store_current=True):
r'^%s/%s/\w+\.py$' % (request.controller, request.function) r'^%s/%s/\w+\.py$' % (request.controller, request.function)
] ]
t = environment['T'] = translator(os.path.join(request.folder, 'languages'), T = environment['T'] = TranslatorFactory(os.path.join(request.folder, 'languages'),
request.env.http_accept_language) request.env.http_accept_language)
c = environment['cache'] = Cache(request) c = environment['cache'] = Cache(request)
# configure the validator to use the t translator
Validator.translator = T
if store_current: if store_current:
current.globalenv = environment current.globalenv = environment
current.request = request current.request = request
current.response = response current.response = response
current.session = session current.session = session
current.T = t current.T = T
current.cache = c current.cache = c
if is_jython: # jython hack if is_jython: # jython hack
+3 -3
View File
@@ -457,7 +457,7 @@ def pickle_lazyT(c):
copyreg.pickle(lazyT, pickle_lazyT) copyreg.pickle(lazyT, pickle_lazyT)
class translator(object): class TranslatorFactory(object):
""" """
This class is instantiated by gluon.compileapp.build_environment This class is instantiated by gluon.compileapp.build_environment
as the T object as the T object
@@ -742,8 +742,8 @@ class translator(object):
try: try:
otherT = self.otherTs[index] otherT = self.otherTs[index]
except KeyError: except KeyError:
otherT = self.otherTs[index] = translator(self.langpath, otherT = self.otherTs[index] = TranslatorFactory(self.langpath,
self.http_accept_language) self.http_accept_language)
if language: if language:
otherT.force(language) otherT.force(language)
return otherT return otherT
+2 -2
View File
@@ -11,7 +11,7 @@ import unittest
from gluon.compileapp import run_controller_in, run_view_in, compile_application, remove_compiled_application from gluon.compileapp import run_controller_in, run_view_in, compile_application, remove_compiled_application
from gluon.languages import translator from gluon.languages import TranslatorFactory
from gluon.storage import Storage, List from gluon.storage import Storage, List
from gluon import fileutils from gluon import fileutils
from gluon.dal import DAL, Field, Table from gluon.dal import DAL, Field, Table
@@ -50,7 +50,7 @@ class TestAppAdmin(unittest.TestCase):
request.env.remote_addr = '127.0.0.1' request.env.remote_addr = '127.0.0.1'
response = Response() response = Response()
session = Session() session = Session()
T = translator('', 'en') T = TranslatorFactory('', 'en')
session.connect(request, response) session.connect(request, response)
current.request = request current.request = request
current.response = response current.response = response
+2 -2
View File
@@ -5,7 +5,7 @@
import os import os
import unittest import unittest
from gluon.globals import Request, Response, Session from gluon.globals import Request, Response, Session
from gluon.languages import translator from gluon.languages import TranslatorFactory
from gluon.dal import DAL, Field from gluon.dal import DAL, Field
from gluon.authapi import AuthAPI from gluon.authapi import AuthAPI
from gluon.storage import Storage from gluon.storage import Storage
@@ -24,7 +24,7 @@ class TestAuthAPI(unittest.TestCase):
self.request.folder = 'applications/admin' self.request.folder = 'applications/admin'
self.response = Response() self.response = Response()
self.session = Session() self.session = Session()
T = translator('', 'en') T = TranslatorFactory('', 'en')
self.session.connect(self.request, self.response) self.session.connect(self.request, self.response)
from gluon.globals import current from gluon.globals import current
self.current = current self.current = current
+1 -1
View File
@@ -120,7 +120,7 @@ class TestCache(unittest.TestCase):
s = Storage({'application': 'admin', s = Storage({'application': 'admin',
'folder': 'applications/admin'}) 'folder': 'applications/admin'})
cache = Cache(s) cache = Cache(s)
db = DAL(check_reserved=['all']) db = DAL('sqlite:memory', check_reserved=['all'])
db.define_table('t_a', Field('f_a')) db.define_table('t_a', Field('f_a'))
db.t_a.insert(f_a='test') db.t_a.insert(f_a='test')
db.commit() db.commit()
+1 -1
View File
@@ -32,7 +32,7 @@ class TestDALSubclass(unittest.TestCase):
def testSerialization(self): def testSerialization(self):
from gluon._compat import pickle from gluon._compat import pickle
db = DAL(check_reserved=['all']) db = DAL('sqlite:memory', check_reserved=['all'])
db.define_table('t_a', Field('f_a')) db.define_table('t_a', Field('f_a'))
db.t_a.insert(f_a='test') db.t_a.insert(f_a='test')
a = db(db.t_a.id > 0).select(cacheable=True) a = db(db.t_a.id > 0).select(cacheable=True)
+3 -3
View File
@@ -88,7 +88,7 @@ class TestTranslations(unittest.TestCase):
pass pass
def test_plain(self): def test_plain(self):
T = languages.translator(self.langpath, self.http_accept_language) T = languages.TranslatorFactory(self.langpath, self.http_accept_language)
self.assertEqual(str(T('Hello World')), self.assertEqual(str(T('Hello World')),
'Hello World') 'Hello World')
self.assertEqual(str(T('Hello World## comment')), self.assertEqual(str(T('Hello World## comment')),
@@ -340,7 +340,7 @@ class TestMessages(unittest.TestCase):
pass pass
def test_decode(self): def test_decode(self):
T = languages.translator(self.langpath, self.http_accept_language) T = languages.TranslatorFactory(self.langpath, self.http_accept_language)
messages = Messages(T) messages = Messages(T)
messages.update({'email_sent':'Email sent', 'test': "ä"}) messages.update({'email_sent':'Email sent', 'test': "ä"})
self.assertEqual(to_unicode(messages.email_sent, 'utf-8'), 'Email sent') self.assertEqual(to_unicode(messages.email_sent, 'utf-8'), 'Email sent')
@@ -359,7 +359,7 @@ class TestHTMLTag(unittest.TestCase):
pass pass
def test_decode(self): def test_decode(self):
T = languages.translator(self.langpath, self.http_accept_language) T = languages.TranslatorFactory(self.langpath, self.http_accept_language)
elem = SPAN(T("Complete")) elem = SPAN(T("Complete"))
self.assertEqual(elem.flatten(), "Complete") self.assertEqual(elem.flatten(), "Complete")
elem = SPAN(T("Cannot be empty", language="ru")) elem = SPAN(T("Cannot be empty", language="ru"))
+2 -2
View File
@@ -11,7 +11,7 @@ import datetime
import sys import sys
from gluon.storage import Storage from gluon.storage import Storage
from gluon.languages import translator from gluon.languages import TranslatorFactory
from gluon.scheduler import JobGraph, Scheduler, CronParser from gluon.scheduler import JobGraph, Scheduler, CronParser
from gluon.dal import DAL from gluon.dal import DAL
@@ -25,7 +25,7 @@ class BaseTestScheduler(unittest.TestCase):
'folder': 'applications/welcome', 'folder': 'applications/welcome',
'controller': 'default'}) 'controller': 'default'})
current.request = s current.request = s
T = translator('', 'en') T = TranslatorFactory('', 'en')
current.T = T current.T = T
self.db = DAL('sqlite://dummy2.db', check_reserved=['all']) self.db = DAL('sqlite://dummy2.db', check_reserved=['all'])
+2 -2
View File
@@ -13,7 +13,7 @@ import decimal
from gluon.serializers import * from gluon.serializers import *
from gluon.storage import Storage from gluon.storage import Storage
# careful with the import path 'cause of isinstance() checks # careful with the import path 'cause of isinstance() checks
from gluon.languages import translator from gluon.languages import TranslatorFactory
from gluon.html import SPAN from gluon.html import SPAN
@@ -46,7 +46,7 @@ class TestSerializers(unittest.TestCase):
obj = {'a': decimal.Decimal('4.312312312312')} obj = {'a': decimal.Decimal('4.312312312312')}
self.assertEqual(json(obj), u'{"a": "4.312312312312"}') self.assertEqual(json(obj), u'{"a": "4.312312312312"}')
# lazyT translated # lazyT translated
T = translator('', 'en') T = TranslatorFactory('', 'en')
lazy_translation = T('abc') lazy_translation = T('abc')
self.assertEqual(json(lazy_translation), u'"abc"') self.assertEqual(json(lazy_translation), u'"abc"')
# html helpers are xml()ed before too # html helpers are xml()ed before too
+3 -3
View File
@@ -18,7 +18,7 @@ from pydal.objects import Table
from gluon.tools import Auth, Mail from gluon.tools import Auth, Mail
from gluon.globals import Request, Response, Session from gluon.globals import Request, Response, Session
from gluon.storage import Storage from gluon.storage import Storage
from gluon.languages import translator from gluon.languages import TranslatorFactory
from gluon.http import HTTP from gluon.http import HTTP
from gluon.validators import * from gluon.validators import *
@@ -254,7 +254,7 @@ class TestSQLFORM(unittest.TestCase):
request.folder = 'applications/admin' request.folder = 'applications/admin'
response = Response() response = Response()
session = Session() session = Session()
T = translator('', 'en') T = TranslatorFactory('', 'en')
session.connect(request, response) session.connect(request, response)
from gluon.globals import current from gluon.globals import current
current.request = request current.request = request
@@ -355,7 +355,7 @@ class TestSQLTABLE(unittest.TestCase):
request.folder = 'applications/admin' request.folder = 'applications/admin'
response = Response() response = Response()
session = Session() session = Session()
T = translator('', 'en') T = TranslatorFactory('', 'en')
session.connect(request, response) session.connect(request, response)
from gluon.globals import current from gluon.globals import current
current.request = request current.request = request
+4 -4
View File
@@ -21,7 +21,7 @@ from gluon.tools import Auth, Mail, Recaptcha2, prettydate, Expose
from gluon._compat import PY2, to_bytes from gluon._compat import PY2, to_bytes
from gluon.globals import Request, Response, Session from gluon.globals import Request, Response, Session
from gluon.storage import Storage from gluon.storage import Storage
from gluon.languages import translator from gluon.languages import TranslatorFactory
from gluon.http import HTTP from gluon.http import HTTP
from gluon import SPAN, H3, TABLE, TR, TD, A, URL, current from gluon import SPAN, H3, TABLE, TR, TD, A, URL, current
@@ -271,7 +271,7 @@ class TestAuthJWT(unittest.TestCase):
# request.folder = 'applications/admin' # request.folder = 'applications/admin'
# response = Response() # response = Response()
# session = Session() # session = Session()
# T = translator('', 'en') # T = TranslatorFactory('', 'en')
# session.connect(request, response) # session.connect(request, response)
# from gluon.globals import current # from gluon.globals import current
# current.request = request # current.request = request
@@ -434,7 +434,7 @@ class TestAuthJWT(unittest.TestCase):
# # request.folder = 'applications/admin' # # request.folder = 'applications/admin'
# # response = Response() # # response = Response()
# # session = Session() # # session = Session()
# # T = translator('', 'en') # # T = TranslatorFactory('', 'en')
# # session.connect(request, response) # # session.connect(request, response)
# # from gluon.globals import current # # from gluon.globals import current
# # current.request = request # # current.request = request
@@ -495,7 +495,7 @@ class TestAuth(unittest.TestCase):
self.request.folder = 'applications/admin' self.request.folder = 'applications/admin'
self.response = Response() self.response = Response()
self.session = Session() self.session = Session()
T = translator('', 'en') T = TranslatorFactory('', 'en')
self.session.connect(self.request, self.response) self.session.connect(self.request, self.response)
from gluon.globals import current from gluon.globals import current
self.current = current self.current = current
+1 -2
View File
@@ -20,8 +20,7 @@ class TestValidators(unittest.TestCase):
def test_MISC(self): def test_MISC(self):
""" Test miscelaneous utility functions and some general behavior guarantees """ """ Test miscelaneous utility functions and some general behavior guarantees """
from gluon.validators import translate, options_sorter, Validator, UTC from gluon.validators import options_sorter, Validator, UTC
self.assertEqual(translate(None), None)
self.assertEqual(options_sorter(('a', 'a'), ('a', 'a')), -1) self.assertEqual(options_sorter(('a', 'a'), ('a', 'a')), -1)
self.assertEqual(options_sorter(('A', 'A'), ('a', 'a')), -1) self.assertEqual(options_sorter(('A', 'A'), ('a', 'a')), -1)
self.assertEqual(options_sorter(('b', 'b'), ('a', 'a')), 1) self.assertEqual(options_sorter(('b', 'b'), ('a', 'a')), 1)
+97 -101
View File
@@ -12,6 +12,7 @@ Validators
""" """
import os import os
import re import re
import math
import datetime import datetime
import time import time
import cgi import cgi
@@ -19,17 +20,19 @@ import json
import struct import struct
import decimal import decimal
import unicodedata import unicodedata
import encodings.idna
from gluon._compat import StringIO, integer_types, basestring, unicodeT, urllib_unquote, unichr, to_bytes, PY2, \ from pydal._compat import StringIO, integer_types, basestring, unicodeT, urllib_unquote, \
to_unicode, to_native, string_types, urlparse unichr, to_bytes, PY2, to_unicode, to_native, string_types, urlparse
from gluon.utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE from pydal.objects import Field, FieldVirtual, FieldMethod, Table
from pydal.objects import Field, FieldVirtual, FieldMethod
from functools import reduce from functools import reduce
from gluon.utils import simple_hash, web2py_uuid, DIGEST_ALG_BY_SIZE
from gluon._compat import ipaddress
regex_isint = re.compile('^[+-]?\d+$') regex_isint = re.compile('^[+-]?\d+$')
JSONErrors = (NameError, TypeError, ValueError, AttributeError, JSONErrors = (NameError, TypeError, ValueError, AttributeError, KeyError)
KeyError)
__all__ = [ __all__ = [
'ANY_OF', 'ANY_OF',
@@ -70,25 +73,19 @@ __all__ = [
'IS_JSON', 'IS_JSON',
] ]
try:
from gluon.globals import current
have_current = True
except ImportError:
have_current = False
def translate(text):
if text is None:
return None
elif isinstance(text, (str, unicodeT)) and have_current:
if hasattr(current, 'T'):
return str(current.T(text))
return str(text)
def options_sorter(x, y): def options_sorter(x, y):
return (str(x[1]).upper() > str(y[1]).upper() and 1) or -1 return (str(x[1]).upper() > str(y[1]).upper() and 1) or -1
def translator(text):
if text is None:
return None
text = Validator.translator(text)
return str(text)
class ValidationError(Exception):
def __init__(self, error):
self.error = error
class Validator(object): class Validator(object):
""" """
@@ -128,6 +125,8 @@ class Validator(object):
Notice that default error messages are not translated. Notice that default error messages are not translated.
""" """
translator = staticmethod(lambda text: text)
def formatter(self, value): def formatter(self, value):
""" """
For some validators returns a formatted version (matching the validator) For some validators returns a formatted version (matching the validator)
@@ -135,9 +134,15 @@ class Validator(object):
""" """
return value return value
def __call__(self, value): def validate(self, value):
raise NotImplementedError raise NotImplementedError
def __call__(self, value):
try:
return self.validate(value), None
except ValidationError as e:
return value, e.message
class IS_MATCH(Validator): class IS_MATCH(Validator):
""" """
@@ -210,7 +215,7 @@ class IS_MATCH(Validator):
match = self.regex.search(value.encode('utf8')) match = self.regex.search(value.encode('utf8'))
if match is not None: if match is not None:
return (self.extract and match.group() or value, None) return (self.extract and match.group() or value, None)
return (value, translate(self.error_message)) return (value, translator(self.error_message))
class IS_EQUAL_TO(Validator): class IS_EQUAL_TO(Validator):
@@ -239,7 +244,7 @@ class IS_EQUAL_TO(Validator):
def __call__(self, value): def __call__(self, value):
if value == self.expression: if value == self.expression:
return (value, None) return (value, None)
return (value, translate(self.error_message)) return (value, translator(self.error_message))
class IS_EXPR(Validator): class IS_EXPR(Validator):
@@ -273,7 +278,7 @@ class IS_EXPR(Validator):
exec('__ret__=' + self.expression, self.environment) exec('__ret__=' + self.expression, self.environment)
if self.environment['__ret__']: if self.environment['__ret__']:
return (value, None) return (value, None)
return (value, translate(self.error_message)) return (value, translator(self.error_message))
class IS_LENGTH(Validator): class IS_LENGTH(Validator):
@@ -352,8 +357,8 @@ class IS_LENGTH(Validator):
return (value, None) return (value, None)
elif self.minsize <= len(str(value)) <= self.maxsize: elif self.minsize <= len(str(value)) <= self.maxsize:
return (str(value), None) return (str(value), None)
return (value, translate(self.error_message) return (value, translator(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):
@@ -383,7 +388,7 @@ class IS_JSON(Validator):
else: else:
return (json.loads(value), None) return (json.loads(value), None)
except JSONErrors: except JSONErrors:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
def formatter(self, value): def formatter(self, value):
if value is None: if value is None:
@@ -473,11 +478,11 @@ class IS_IN_SET(Validator):
thestrset = [str(x) for x in self.theset] thestrset = [str(x) for x in self.theset]
failures = [x for x in values if not str(x) in thestrset] failures = [x for x in values if not str(x) in thestrset]
if failures and self.theset: if failures and self.theset:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
if self.multiple: if self.multiple:
if isinstance(self.multiple, (tuple, list)) and \ if isinstance(self.multiple, (tuple, list)) and \
not self.multiple[0] <= len(values) < self.multiple[1]: not self.multiple[0] <= len(values) < self.multiple[1]:
return (values, translate(self.error_message)) return (values, translator(self.error_message))
return (values, None) return (values, None)
return (value, None) return (value, None)
@@ -515,7 +520,6 @@ class IS_IN_DB(Validator):
delimiter=None, delimiter=None,
auto_add=False, auto_add=False,
): ):
from pydal.objects import Table
if hasattr(dbset, 'define_table'): if hasattr(dbset, 'define_table'):
self.dbset = dbset() self.dbset = dbset()
else: else:
@@ -636,13 +640,13 @@ class IS_IN_DB(Validator):
if self.auto_add: if self.auto_add:
value = str(self.maybe_add(table, self.fieldnames[0], value)) value = str(self.maybe_add(table, self.fieldnames[0], value))
else: else:
return (values, translate(self.error_message)) return (values, translator(self.error_message))
new_values.append(value) new_values.append(value)
values = new_values values = new_values
if isinstance(self.multiple, (tuple, list)) and \ if isinstance(self.multiple, (tuple, list)) and \
not self.multiple[0] <= len(values) < self.multiple[1]: not self.multiple[0] <= len(values) < self.multiple[1]:
return (values, translate(self.error_message)) return (values, translator(self.error_message))
if self.theset: if self.theset:
if not [v for v in values if v not in self.theset]: if not [v for v in values if v not in self.theset]:
return (values, None) return (values, None)
@@ -664,12 +668,12 @@ class IS_IN_DB(Validator):
elif self.auto_add: elif self.auto_add:
value = self.maybe_add(table, self.fieldnames[0], value) value = self.maybe_add(table, self.fieldnames[0], value)
else: else:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
try: try:
value = int(value) value = int(value)
except TypeError: except TypeError:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
if self.theset: if self.theset:
if str(value) in self.theset: if str(value) in self.theset:
@@ -683,7 +687,7 @@ class IS_IN_DB(Validator):
return self._and(value) return self._and(value)
else: else:
return (value, None) return (value, None)
return (value, translate(self.error_message)) return (value, translator(self.error_message))
class IS_NOT_IN_DB(Validator): class IS_NOT_IN_DB(Validator):
@@ -704,8 +708,6 @@ class IS_NOT_IN_DB(Validator):
allowed_override=[], allowed_override=[],
ignore_common_filters=False, ignore_common_filters=False,
): ):
from pydal.objects import Table
if isinstance(field, Table): if isinstance(field, Table):
field = field._id field = field._id
@@ -725,7 +727,7 @@ class IS_NOT_IN_DB(Validator):
def __call__(self, value): def __call__(self, value):
value = to_native(str(value)) value = to_native(str(value))
if not value.strip(): if not value.strip():
return (value, translate(self.error_message)) return (value, translator(self.error_message))
if value in self.allowed_override: if value in self.allowed_override:
return (value, None) return (value, None)
(tablename, fieldname) = str(self.field).split('.') (tablename, fieldname) = str(self.field).split('.')
@@ -738,11 +740,11 @@ class IS_NOT_IN_DB(Validator):
fields = [table[f] for f in id] fields = [table[f] for f in id]
row = subset.select(*fields, **dict(limitby=(0, 1), orderby_on_limitby=False)).first() row = subset.select(*fields, **dict(limitby=(0, 1), orderby_on_limitby=False)).first()
if row and any(str(row[f]) != str(id[f]) for f in id): if row and any(str(row[f]) != str(id[f]) for f in id):
return (value, translate(self.error_message)) return (value, translator(self.error_message))
else: else:
row = subset.select(table._id, field, limitby=(0, 1), orderby_on_limitby=False).first() row = subset.select(table._id, field, limitby=(0, 1), orderby_on_limitby=False).first()
if row and str(row[table._id]) != str(id): if row and str(row[table._id]) != str(id):
return (value, translate(self.error_message)) return (value, translator(self.error_message))
return (value, None) return (value, None)
@@ -758,7 +760,7 @@ def range_error_message(error_message, what_to_enter, minimum, maximum):
error_message += ' less than or equal to %(max)g' error_message += ' less than or equal to %(max)g'
if type(maximum) in integer_types: if type(maximum) in integer_types:
maximum -= 1 maximum -= 1
return translate(error_message) % dict(min=minimum, max=maximum) return translator(error_message) % dict(min=minimum, max=maximum)
class IS_INT_IN_RANGE(Validator): class IS_INT_IN_RANGE(Validator):
@@ -1043,7 +1045,7 @@ class IS_NOT_EMPTY(Validator):
def __call__(self, value): def __call__(self, value):
value, empty = is_empty(value, empty_regex=self.empty_regex) value, empty = is_empty(value, empty_regex=self.empty_regex)
if empty: if empty:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
return (value, None) return (value, None)
@@ -1199,7 +1201,7 @@ class IS_EMAIL(Validator):
def __call__(self, value): def __call__(self, value):
if not(isinstance(value, (basestring, unicodeT))) or not value or '@' not in value: if not(isinstance(value, (basestring, unicodeT))) or not value or '@' not in value:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
body, domain = value.rsplit('@', 1) body, domain = value.rsplit('@', 1)
@@ -1222,7 +1224,7 @@ class IS_EMAIL(Validator):
if (not self.banned or not self.banned.match(domain)) \ if (not self.banned or not self.banned.match(domain)) \
and (not self.forced or self.forced.match(domain)): and (not self.forced or self.forced.match(domain)):
return (value, None) return (value, None)
return (value, translate(self.error_message)) return (value, translator(self.error_message))
class IS_LIST_OF_EMAILS(object): class IS_LIST_OF_EMAILS(object):
@@ -1253,7 +1255,7 @@ class IS_LIST_OF_EMAILS(object):
return (value, None) return (value, None)
else: else:
return (value, return (value,
translate(self.error_message) % ', '.join(bad_emails)) translator(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 [])
@@ -1456,7 +1458,6 @@ def unicode_to_ascii_authority(authority):
# 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 = []
import encodings.idna
for label in labels: for label in labels:
if label: if label:
asciiLabels.append(to_native(encodings.idna.ToASCII(label))) asciiLabels.append(to_native(encodings.idna.ToASCII(label)))
@@ -1613,7 +1614,7 @@ class IS_GENERIC_URL(Validator):
# if we dont have anything or the URL misuses the '%' character # if we dont have anything or the URL misuses the '%' character
if not value or self.GENERIC_URL.search(value): if not value or self.GENERIC_URL.search(value):
return (value, translate(self.error_message)) return (value, translator(self.error_message))
if '#' in value: if '#' in value:
url, fragment_part = value.split('#') url, fragment_part = value.split('#')
@@ -1625,7 +1626,7 @@ class IS_GENERIC_URL(Validator):
try: try:
components = urlparse.urlparse(urllib_unquote(value))._asdict() components = urlparse.urlparse(urllib_unquote(value))._asdict()
except ValueError: except ValueError:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
# Clean up the scheme before we check it # Clean up the scheme before we check it
scheme = components['scheme'] scheme = components['scheme']
@@ -1653,7 +1654,7 @@ class IS_GENERIC_URL(Validator):
else: else:
return (value, None) return (value, None)
# else the URL is not valid # else the URL is not valid
return (value, translate(self.error_message)) return (value, translator(self.error_message))
# Sources (obtained 2017-Nov-11): # Sources (obtained 2017-Nov-11):
# http://data.iana.org/TLD/tlds-alpha-by-domain.txt # http://data.iana.org/TLD/tlds-alpha-by-domain.txt
@@ -2089,7 +2090,7 @@ class IS_HTTP_URL(Validator):
except: except:
pass pass
# else the HTTP URL is not valid # else the HTTP URL is not valid
return (value, translate(self.error_message)) return (value, translator(self.error_message))
class IS_URL(Validator): class IS_URL(Validator):
@@ -2239,7 +2240,7 @@ class IS_URL(Validator):
except Exception as e: except Exception as e:
# 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, translator(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:
@@ -2321,7 +2322,7 @@ class IS_TIME(Validator):
pass pass
except ValueError: except ValueError:
pass pass
return (ivalue, translate(self.error_message)) return (ivalue, translator(self.error_message))
# A UTC class. # A UTC class.
@@ -2352,7 +2353,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'):
self.format = translate(format) self.format = translator(format)
self.error_message = str(error_message) self.error_message = str(error_message)
self.extremes = {} self.extremes = {}
@@ -2367,7 +2368,7 @@ class IS_DATE(Validator):
return (value, None) return (value, None)
except: except:
self.extremes.update(IS_DATETIME.nice(self.format)) self.extremes.update(IS_DATETIME.nice(self.format))
return (ovalue, translate(self.error_message) % self.extremes) return (ovalue, translator(self.error_message) % self.extremes)
def formatter(self, value): def formatter(self, value):
if value is None: if value is None:
@@ -2416,7 +2417,7 @@ class IS_DATETIME(Validator):
def __init__(self, format='%Y-%m-%d %H:%M:%S', def __init__(self, format='%Y-%m-%d %H:%M:%S',
error_message='Enter date and time as %(format)s', error_message='Enter date and time as %(format)s',
timezone=None): timezone=None):
self.format = translate(format) self.format = translator(format)
self.error_message = str(error_message) self.error_message = str(error_message)
self.extremes = {} self.extremes = {}
self.timezone = timezone self.timezone = timezone
@@ -2435,7 +2436,7 @@ class IS_DATETIME(Validator):
return (value, None) return (value, None)
except: except:
self.extremes.update(IS_DATETIME.nice(self.format)) self.extremes.update(IS_DATETIME.nice(self.format))
return (ovalue, translate(self.error_message) % self.extremes) return (ovalue, translator(self.error_message) % self.extremes)
def formatter(self, value): def formatter(self, value):
if value is None: if value is None:
@@ -2503,9 +2504,9 @@ class IS_DATE_IN_RANGE(IS_DATE):
if msg is not None: if msg is not None:
return (value, msg) return (value, msg)
if self.minimum and self.minimum > value: if self.minimum and self.minimum > value:
return (ovalue, translate(self.error_message) % self.extremes) return (ovalue, translator(self.error_message) % self.extremes)
if self.maximum and value > self.maximum: if self.maximum and value > self.maximum:
return (ovalue, translate(self.error_message) % self.extremes) return (ovalue, translator(self.error_message) % self.extremes)
return (value, None) return (value, None)
@@ -2559,9 +2560,9 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME):
if msg is not None: if msg is not None:
return (value, msg) return (value, msg)
if self.minimum and self.minimum > value: if self.minimum and self.minimum > value:
return (ovalue, translate(self.error_message) % self.extremes) return (ovalue, translator(self.error_message) % self.extremes)
if self.maximum and value > self.maximum: if self.maximum and value > self.maximum:
return (ovalue, translate(self.error_message) % self.extremes) return (ovalue, translator(self.error_message) % self.extremes)
return (value, None) return (value, None)
@@ -2579,11 +2580,11 @@ class IS_LIST_OF(Validator):
ivalue = [ivalue] ivalue = [ivalue]
ivalue = [i for i in ivalue if str(i).strip()] ivalue = [i for i in ivalue if str(i).strip()]
if self.minimum is not None and len(ivalue) < self.minimum: if self.minimum is not None and len(ivalue) < self.minimum:
return (ivalue, translate(self.error_message or return (ivalue, translator(self.error_message or 'Minimum length is %(min)s') %
'Minimum length is %(min)s') % dict(min=self.minimum, max=self.maximum)) dict(min=self.minimum, max=self.maximum))
if self.maximum is not None and len(ivalue) > self.maximum: if self.maximum is not None and len(ivalue) > self.maximum:
return (ivalue, translate(self.error_message or return (ivalue, translator(self.error_message or 'Maximum length is %(max)s') %
'Maximum length is %(max)s') % dict(min=self.minimum, max=self.maximum)) dict(min=self.minimum, max=self.maximum))
new_value = [] new_value = []
other = self.other other = self.other
if self.other: if self.other:
@@ -2721,7 +2722,7 @@ class IS_SLUG(Validator):
def __call__(self, value): def __call__(self, value):
if self.check and value != urlify(value, self.maxlen, self.keep_underscores): if self.check and value != urlify(value, self.maxlen, self.keep_underscores):
return (value, translate(self.error_message)) return (value, translator(self.error_message))
return (urlify(value, self.maxlen, self.keep_underscores), None) return (urlify(value, self.maxlen, self.keep_underscores), None)
@@ -2750,7 +2751,7 @@ class ANY_OF(Validator):
if error is None: if error is None:
break break
if error is not None and self.error_message is not None: if error is not None and self.error_message is not None:
error = translate(self.error_message) error = translator(self.error_message)
return value, error return value, error
def formatter(self, value): def formatter(self, value):
@@ -3038,7 +3039,7 @@ class CRYPT(object):
def __call__(self, value): def __call__(self, value):
v = value and str(value)[:self.max_length] v = value and str(value)[:self.max_length]
if not v or len(v) < self.min_length: if not v or len(v) < self.min_length:
return ('', translate(self.error_message)) return ('', translator(self.error_message))
if isinstance(value, LazyCrypt): if isinstance(value, LazyCrypt):
return (value, None) return (value, None)
return (LazyCrypt(self, value), None) return (LazyCrypt(self, value), None)
@@ -3056,7 +3057,6 @@ 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
alphabet = 0 # alphabet size alphabet = 0 # alphabet size
other = set() other = set()
seen = set() seen = set()
@@ -3149,49 +3149,49 @@ class IS_STRONG(object):
if self.entropy is not None: if self.entropy is not None:
entropy = calc_entropy(value) entropy = calc_entropy(value)
if entropy < self.entropy: if entropy < self.entropy:
failures.append(translate("Entropy (%(have)s) less than required (%(need)s)") failures.append(translator("Entropy (%(have)s) less than required (%(need)s)")
% dict(have=entropy, need=self.entropy)) % dict(have=entropy, need=self.entropy))
if isinstance(self.min, int) and self.min > 0: if isinstance(self.min, int) and self.min > 0:
if not len(value) >= self.min: if not len(value) >= self.min:
failures.append(translate("Minimum length is %s") % self.min) failures.append(translator("Minimum length is %s") % self.min)
if isinstance(self.max, int) and self.max > 0: if isinstance(self.max, int) and self.max > 0:
if not len(value) <= self.max: if not len(value) <= self.max:
failures.append(translate("Maximum length is %s") % self.max) failures.append(translator("Maximum length is %s") % self.max)
if isinstance(self.special, int): if isinstance(self.special, int):
all_special = [ch in value for ch in self.specials] all_special = [ch in value for ch in self.specials]
if self.special > 0: if self.special > 0:
if not all_special.count(True) >= self.special: if not all_special.count(True) >= self.special:
failures.append(translate("Must include at least %s of the following: %s") failures.append(translator("Must include at least %s of the following: %s")
% (self.special, self.specials)) % (self.special, self.specials))
elif self.special is 0: elif self.special is 0:
if len(all_special) > 0: if len(all_special) > 0:
failures.append(translate("May not contain any of the following: %s") failures.append(translator("May not contain any of the following: %s")
% self.specials) % self.specials)
if self.invalid: if self.invalid:
all_invalid = [ch in value for ch in self.invalid] all_invalid = [ch in value for ch in self.invalid]
if all_invalid.count(True) > 0: if all_invalid.count(True) > 0:
failures.append(translate("May not contain any of the following: %s") failures.append(translator("May not contain any of the following: %s")
% self.invalid) % self.invalid)
if isinstance(self.upper, int): if isinstance(self.upper, int):
all_upper = re.findall("[A-Z]", value) all_upper = re.findall("[A-Z]", value)
if self.upper > 0: if self.upper > 0:
if not len(all_upper) >= self.upper: if not len(all_upper) >= self.upper:
failures.append(translate("Must include at least %s uppercase") failures.append(translator("Must include at least %s uppercase")
% str(self.upper)) % str(self.upper))
elif self.upper is 0: elif self.upper is 0:
if len(all_upper) > 0: if len(all_upper) > 0:
failures.append( failures.append(
translate("May not include any uppercase letters")) translator("May not include any uppercase letters"))
if isinstance(self.lower, int): if isinstance(self.lower, int):
all_lower = re.findall("[a-z]", value) all_lower = re.findall("[a-z]", value)
if self.lower > 0: if self.lower > 0:
if not len(all_lower) >= self.lower: if not len(all_lower) >= self.lower:
failures.append(translate("Must include at least %s lowercase") failures.append(translator("Must include at least %s lowercase")
% str(self.lower)) % str(self.lower))
elif self.lower is 0: elif self.lower is 0:
if len(all_lower) > 0: if len(all_lower) > 0:
failures.append( failures.append(
translate("May not include any lowercase letters")) translator("May not include any lowercase letters"))
if isinstance(self.number, int): if isinstance(self.number, int):
all_number = re.findall("[0-9]", value) all_number = re.findall("[0-9]", value)
if self.number > 0: if self.number > 0:
@@ -3199,20 +3199,19 @@ class IS_STRONG(object):
if self.number > 1: if self.number > 1:
numbers = "numbers" numbers = "numbers"
if not len(all_number) >= self.number: if not len(all_number) >= self.number:
failures.append(translate("Must include at least %s %s") failures.append(translator("Must include at least %s %s")
% (str(self.number), numbers)) % (str(self.number), numbers))
elif self.number is 0: elif self.number is 0:
if len(all_number) > 0: if len(all_number) > 0:
failures.append(translate("May not include any numbers")) failures.append(translator("May not include any numbers"))
if len(failures) == 0: if len(failures) == 0:
return (value, None) return (value, None)
if not self.error_message: if not self.error_message:
if self.estring: if self.estring:
return (value, '|'.join(failures)) return (value, '|'.join(map(str, failures)))
from gluon.html import XML return (value, ', '.join(failures))
return (value, XML('<br />'.join(failures)))
else: else:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
class IS_IMAGE(Validator): class IS_IMAGE(Validator):
@@ -3304,7 +3303,7 @@ class IS_IMAGE(Validator):
value.file.seek(0) value.file.seek(0)
return (value, None) return (value, None)
except Exception as e: except Exception as e:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
def __bmp(self, stream): def __bmp(self, stream):
if stream.read(2) == b'BM': if stream.read(2) == b'BM':
@@ -3397,7 +3396,7 @@ class IS_UPLOAD_FILENAME(Validator):
try: try:
string = value.filename string = value.filename
except: except:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
if self.case == 1: if self.case == 1:
string = string.lower() string = string.lower()
elif self.case == 2: elif self.case == 2:
@@ -3409,9 +3408,9 @@ class IS_UPLOAD_FILENAME(Validator):
if dot == -1: if dot == -1:
dot = len(string) dot = len(string)
if self.filename and not self.filename.match(string[:dot]): if self.filename and not self.filename.match(string[:dot]):
return (value, translate(self.error_message)) return (value, translator(self.error_message))
elif self.extension and not self.extension.match(string[dot + 1:]): elif self.extension and not self.extension.match(string[dot + 1:]):
return (value, translate(self.error_message)) return (value, translator(self.error_message))
else: else:
return (value, None) return (value, None)
@@ -3581,7 +3580,7 @@ class IS_IPV4(Validator):
ok = False ok = False
if ok: if ok:
return (value, None) return (value, None)
return (value, translate(self.error_message)) return (value, translator(self.error_message))
class IS_IPV6(Validator): class IS_IPV6(Validator):
@@ -3674,13 +3673,11 @@ class IS_IPV6(Validator):
self.error_message = error_message self.error_message = error_message
def __call__(self, value): def __call__(self, value):
from gluon._compat import ipaddress
try: try:
ip = ipaddress.IPv6Address(to_unicode(value)) ip = ipaddress.IPv6Address(to_unicode(value))
ok = True ok = True
except ipaddress.AddressValueError: except ipaddress.AddressValueError:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
if self.subnets: if self.subnets:
# iterate through self.subnets to see if value is a member # iterate through self.subnets to see if value is a member
@@ -3691,7 +3688,7 @@ class IS_IPV6(Validator):
try: try:
ipnet = ipaddress.IPv6Network(to_unicode(network)) ipnet = ipaddress.IPv6Network(to_unicode(network))
except (ipaddress.NetmaskValueError, ipaddress.AddressValueError): except (ipaddress.NetmaskValueError, ipaddress.AddressValueError):
return (value, translate('invalid subnet provided')) return (value, translator('invalid subnet provided'))
if ip in ipnet: if ip in ipnet:
ok = True ok = True
@@ -3722,7 +3719,7 @@ class IS_IPV6(Validator):
if ok: if ok:
return (value, None) return (value, None)
return (value, translate(self.error_message)) return (value, translator(self.error_message))
class IS_IPADDRESS(Validator): class IS_IPADDRESS(Validator):
@@ -3897,7 +3894,6 @@ class IS_IPADDRESS(Validator):
self.error_message = error_message self.error_message = error_message
def __call__(self, value): def __call__(self, value):
from gluon._compat import ipaddress
IPAddress = ipaddress.ip_address IPAddress = ipaddress.ip_address
IPv6Address = ipaddress.IPv6Address IPv6Address = ipaddress.IPv6Address
IPv4Address = ipaddress.IPv4Address IPv4Address = ipaddress.IPv4Address
@@ -3905,12 +3901,12 @@ class IS_IPADDRESS(Validator):
try: try:
ip = IPAddress(to_unicode(value)) ip = IPAddress(to_unicode(value))
except ValueError: except ValueError:
return (value, translate(self.error_message)) return (value, translator(self.error_message))
if self.is_ipv4 and isinstance(ip, IPv6Address): if self.is_ipv4 and isinstance(ip, IPv6Address):
retval = (value, translate(self.error_message)) retval = (value, translator(self.error_message))
elif self.is_ipv6 and isinstance(ip, IPv4Address): elif self.is_ipv6 and isinstance(ip, IPv4Address):
retval = (value, translate(self.error_message)) retval = (value, translator(self.error_message))
elif self.is_ipv4 or isinstance(ip, IPv4Address): elif self.is_ipv4 or isinstance(ip, IPv4Address):
retval = IS_IPV4( retval = IS_IPV4(
minip=self.minip, minip=self.minip,
@@ -3934,6 +3930,6 @@ class IS_IPADDRESS(Validator):
error_message=self.error_message error_message=self.error_message
)(value) )(value)
else: else:
retval = (value, translate(self.error_message)) retval = (value, translator(self.error_message))
return retval return retval