From b58b7da18fe7e9bd911b03a00b432266a6d2b9d5 Mon Sep 17 00:00:00 2001 From: mdipierro Date: Sun, 24 Feb 2019 01:51:18 -0800 Subject: [PATCH] validatos no longer depend on T --- gluon/compileapp.py | 12 +- gluon/languages.py | 6 +- gluon/tests/test_appadmin.py | 4 +- gluon/tests/test_authapi.py | 4 +- gluon/tests/test_cache.py | 2 +- gluon/tests/test_dal.py | 2 +- gluon/tests/test_languages.py | 6 +- gluon/tests/test_scheduler.py | 4 +- gluon/tests/test_serializers.py | 4 +- gluon/tests/test_sqlhtml.py | 6 +- gluon/tests/test_tools.py | 8 +- gluon/tests/test_validators.py | 3 +- gluon/validators.py | 198 ++++++++++++++++---------------- 13 files changed, 129 insertions(+), 130 deletions(-) diff --git a/gluon/compileapp.py b/gluon/compileapp.py index 14bc56f8..c055f523 100644 --- a/gluon/compileapp.py +++ b/gluon/compileapp.py @@ -24,8 +24,9 @@ from gluon.template import parse_template from gluon.restricted import restricted, compile2 from gluon.fileutils import mktree, listdir, read_file, write_file 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.validators import Validator from pydal.base import BaseAdapter from gluon.sqlhtml import SQLFORM, SQLTABLE 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) ] - t = environment['T'] = translator(os.path.join(request.folder, 'languages'), - request.env.http_accept_language) + T = environment['T'] = TranslatorFactory(os.path.join(request.folder, 'languages'), + request.env.http_accept_language) c = environment['cache'] = Cache(request) + # configure the validator to use the t translator + Validator.translator = T + if store_current: current.globalenv = environment current.request = request current.response = response current.session = session - current.T = t + current.T = T current.cache = c if is_jython: # jython hack diff --git a/gluon/languages.py b/gluon/languages.py index c8c8ed1e..2ed0ed61 100644 --- a/gluon/languages.py +++ b/gluon/languages.py @@ -457,7 +457,7 @@ def pickle_lazyT(c): copyreg.pickle(lazyT, pickle_lazyT) -class translator(object): +class TranslatorFactory(object): """ This class is instantiated by gluon.compileapp.build_environment as the T object @@ -742,8 +742,8 @@ class translator(object): try: otherT = self.otherTs[index] except KeyError: - otherT = self.otherTs[index] = translator(self.langpath, - self.http_accept_language) + otherT = self.otherTs[index] = TranslatorFactory(self.langpath, + self.http_accept_language) if language: otherT.force(language) return otherT diff --git a/gluon/tests/test_appadmin.py b/gluon/tests/test_appadmin.py index c84732cd..3e722c0a 100644 --- a/gluon/tests/test_appadmin.py +++ b/gluon/tests/test_appadmin.py @@ -11,7 +11,7 @@ import unittest 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 import fileutils from gluon.dal import DAL, Field, Table @@ -50,7 +50,7 @@ class TestAppAdmin(unittest.TestCase): request.env.remote_addr = '127.0.0.1' response = Response() session = Session() - T = translator('', 'en') + T = TranslatorFactory('', 'en') session.connect(request, response) current.request = request current.response = response diff --git a/gluon/tests/test_authapi.py b/gluon/tests/test_authapi.py index cc92778e..05fe1073 100644 --- a/gluon/tests/test_authapi.py +++ b/gluon/tests/test_authapi.py @@ -5,7 +5,7 @@ import os import unittest 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.authapi import AuthAPI from gluon.storage import Storage @@ -24,7 +24,7 @@ class TestAuthAPI(unittest.TestCase): self.request.folder = 'applications/admin' self.response = Response() self.session = Session() - T = translator('', 'en') + T = TranslatorFactory('', 'en') self.session.connect(self.request, self.response) from gluon.globals import current self.current = current diff --git a/gluon/tests/test_cache.py b/gluon/tests/test_cache.py index b02bdaee..508cf377 100644 --- a/gluon/tests/test_cache.py +++ b/gluon/tests/test_cache.py @@ -120,7 +120,7 @@ class TestCache(unittest.TestCase): s = Storage({'application': 'admin', 'folder': 'applications/admin'}) cache = Cache(s) - db = DAL(check_reserved=['all']) + db = DAL('sqlite:memory', check_reserved=['all']) db.define_table('t_a', Field('f_a')) db.t_a.insert(f_a='test') db.commit() diff --git a/gluon/tests/test_dal.py b/gluon/tests/test_dal.py index 75ee08ce..e6baaad2 100644 --- a/gluon/tests/test_dal.py +++ b/gluon/tests/test_dal.py @@ -32,7 +32,7 @@ class TestDALSubclass(unittest.TestCase): def testSerialization(self): 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.t_a.insert(f_a='test') a = db(db.t_a.id > 0).select(cacheable=True) diff --git a/gluon/tests/test_languages.py b/gluon/tests/test_languages.py index e34fad9a..ea33efd8 100644 --- a/gluon/tests/test_languages.py +++ b/gluon/tests/test_languages.py @@ -88,7 +88,7 @@ class TestTranslations(unittest.TestCase): pass 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')), 'Hello World') self.assertEqual(str(T('Hello World## comment')), @@ -340,7 +340,7 @@ class TestMessages(unittest.TestCase): pass 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.update({'email_sent':'Email sent', 'test': "รค"}) self.assertEqual(to_unicode(messages.email_sent, 'utf-8'), 'Email sent') @@ -359,7 +359,7 @@ class TestHTMLTag(unittest.TestCase): pass 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")) self.assertEqual(elem.flatten(), "Complete") elem = SPAN(T("Cannot be empty", language="ru")) diff --git a/gluon/tests/test_scheduler.py b/gluon/tests/test_scheduler.py index 8d1a8be4..06fc50fb 100644 --- a/gluon/tests/test_scheduler.py +++ b/gluon/tests/test_scheduler.py @@ -11,7 +11,7 @@ import datetime import sys from gluon.storage import Storage -from gluon.languages import translator +from gluon.languages import TranslatorFactory from gluon.scheduler import JobGraph, Scheduler, CronParser from gluon.dal import DAL @@ -25,7 +25,7 @@ class BaseTestScheduler(unittest.TestCase): 'folder': 'applications/welcome', 'controller': 'default'}) current.request = s - T = translator('', 'en') + T = TranslatorFactory('', 'en') current.T = T self.db = DAL('sqlite://dummy2.db', check_reserved=['all']) diff --git a/gluon/tests/test_serializers.py b/gluon/tests/test_serializers.py index a7f2a44c..90157698 100644 --- a/gluon/tests/test_serializers.py +++ b/gluon/tests/test_serializers.py @@ -13,7 +13,7 @@ import decimal from gluon.serializers import * from gluon.storage import Storage # careful with the import path 'cause of isinstance() checks -from gluon.languages import translator +from gluon.languages import TranslatorFactory from gluon.html import SPAN @@ -46,7 +46,7 @@ class TestSerializers(unittest.TestCase): obj = {'a': decimal.Decimal('4.312312312312')} self.assertEqual(json(obj), u'{"a": "4.312312312312"}') # lazyT translated - T = translator('', 'en') + T = TranslatorFactory('', 'en') lazy_translation = T('abc') self.assertEqual(json(lazy_translation), u'"abc"') # html helpers are xml()ed before too diff --git a/gluon/tests/test_sqlhtml.py b/gluon/tests/test_sqlhtml.py index e5cc4e09..703b10eb 100644 --- a/gluon/tests/test_sqlhtml.py +++ b/gluon/tests/test_sqlhtml.py @@ -18,7 +18,7 @@ from pydal.objects import Table from gluon.tools import Auth, Mail from gluon.globals import Request, Response, Session from gluon.storage import Storage -from gluon.languages import translator +from gluon.languages import TranslatorFactory from gluon.http import HTTP from gluon.validators import * @@ -254,7 +254,7 @@ class TestSQLFORM(unittest.TestCase): request.folder = 'applications/admin' response = Response() session = Session() - T = translator('', 'en') + T = TranslatorFactory('', 'en') session.connect(request, response) from gluon.globals import current current.request = request @@ -355,7 +355,7 @@ class TestSQLTABLE(unittest.TestCase): request.folder = 'applications/admin' response = Response() session = Session() - T = translator('', 'en') + T = TranslatorFactory('', 'en') session.connect(request, response) from gluon.globals import current current.request = request diff --git a/gluon/tests/test_tools.py b/gluon/tests/test_tools.py index decb6aa2..3f619cb2 100644 --- a/gluon/tests/test_tools.py +++ b/gluon/tests/test_tools.py @@ -21,7 +21,7 @@ from gluon.tools import Auth, Mail, Recaptcha2, prettydate, Expose from gluon._compat import PY2, to_bytes from gluon.globals import Request, Response, Session from gluon.storage import Storage -from gluon.languages import translator +from gluon.languages import TranslatorFactory from gluon.http import HTTP from gluon import SPAN, H3, TABLE, TR, TD, A, URL, current @@ -271,7 +271,7 @@ class TestAuthJWT(unittest.TestCase): # request.folder = 'applications/admin' # response = Response() # session = Session() -# T = translator('', 'en') +# T = TranslatorFactory('', 'en') # session.connect(request, response) # from gluon.globals import current # current.request = request @@ -434,7 +434,7 @@ class TestAuthJWT(unittest.TestCase): # # request.folder = 'applications/admin' # # response = Response() # # session = Session() -# # T = translator('', 'en') +# # T = TranslatorFactory('', 'en') # # session.connect(request, response) # # from gluon.globals import current # # current.request = request @@ -495,7 +495,7 @@ class TestAuth(unittest.TestCase): self.request.folder = 'applications/admin' self.response = Response() self.session = Session() - T = translator('', 'en') + T = TranslatorFactory('', 'en') self.session.connect(self.request, self.response) from gluon.globals import current self.current = current diff --git a/gluon/tests/test_validators.py b/gluon/tests/test_validators.py index 6f07dc73..dde4985d 100644 --- a/gluon/tests/test_validators.py +++ b/gluon/tests/test_validators.py @@ -20,8 +20,7 @@ class TestValidators(unittest.TestCase): def test_MISC(self): """ Test miscelaneous utility functions and some general behavior guarantees """ - from gluon.validators import translate, options_sorter, Validator, UTC - self.assertEqual(translate(None), None) + from gluon.validators import options_sorter, Validator, UTC 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) diff --git a/gluon/validators.py b/gluon/validators.py index 2906c667..0d05e14c 100644 --- a/gluon/validators.py +++ b/gluon/validators.py @@ -12,6 +12,7 @@ Validators """ import os import re +import math import datetime import time import cgi @@ -19,17 +20,19 @@ import json import struct import decimal import unicodedata +import encodings.idna -from gluon._compat import StringIO, integer_types, basestring, unicodeT, urllib_unquote, 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 +from pydal._compat import StringIO, integer_types, basestring, unicodeT, urllib_unquote, \ + unichr, to_bytes, PY2, to_unicode, to_native, string_types, urlparse +from pydal.objects import Field, FieldVirtual, FieldMethod, Table 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+$') -JSONErrors = (NameError, TypeError, ValueError, AttributeError, - KeyError) +JSONErrors = (NameError, TypeError, ValueError, AttributeError, KeyError) __all__ = [ 'ANY_OF', @@ -70,25 +73,19 @@ __all__ = [ '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): 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): """ @@ -128,6 +125,8 @@ class Validator(object): Notice that default error messages are not translated. """ + translator = staticmethod(lambda text: text) + def formatter(self, value): """ For some validators returns a formatted version (matching the validator) @@ -135,9 +134,15 @@ class Validator(object): """ return value - def __call__(self, value): + def validate(self, value): raise NotImplementedError + def __call__(self, value): + try: + return self.validate(value), None + except ValidationError as e: + return value, e.message + class IS_MATCH(Validator): """ @@ -210,7 +215,7 @@ class IS_MATCH(Validator): match = self.regex.search(value.encode('utf8')) if match is not 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): @@ -239,7 +244,7 @@ class IS_EQUAL_TO(Validator): def __call__(self, value): if value == self.expression: return (value, None) - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) class IS_EXPR(Validator): @@ -273,7 +278,7 @@ class IS_EXPR(Validator): exec('__ret__=' + self.expression, self.environment) if self.environment['__ret__']: return (value, None) - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) class IS_LENGTH(Validator): @@ -352,8 +357,8 @@ class IS_LENGTH(Validator): return (value, None) elif self.minsize <= len(str(value)) <= self.maxsize: return (str(value), None) - return (value, translate(self.error_message) - % dict(min=self.minsize, max=self.maxsize)) + return (value, translator(self.error_message) % + dict(min=self.minsize, max=self.maxsize)) class IS_JSON(Validator): @@ -383,7 +388,7 @@ class IS_JSON(Validator): else: return (json.loads(value), None) except JSONErrors: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) def formatter(self, value): if value is None: @@ -473,11 +478,11 @@ class IS_IN_SET(Validator): thestrset = [str(x) for x in self.theset] failures = [x for x in values if not str(x) in thestrset] if failures and self.theset: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) if self.multiple: if isinstance(self.multiple, (tuple, list)) and \ 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 (value, None) @@ -515,7 +520,6 @@ class IS_IN_DB(Validator): delimiter=None, auto_add=False, ): - from pydal.objects import Table if hasattr(dbset, 'define_table'): self.dbset = dbset() else: @@ -636,13 +640,13 @@ class IS_IN_DB(Validator): if self.auto_add: value = str(self.maybe_add(table, self.fieldnames[0], value)) else: - return (values, translate(self.error_message)) + return (values, translator(self.error_message)) new_values.append(value) values = new_values if isinstance(self.multiple, (tuple, list)) and \ 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 not [v for v in values if v not in self.theset]: return (values, None) @@ -664,12 +668,12 @@ class IS_IN_DB(Validator): elif self.auto_add: value = self.maybe_add(table, self.fieldnames[0], value) else: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) try: value = int(value) except TypeError: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) if self.theset: if str(value) in self.theset: @@ -683,7 +687,7 @@ class IS_IN_DB(Validator): return self._and(value) else: return (value, None) - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) class IS_NOT_IN_DB(Validator): @@ -704,8 +708,6 @@ class IS_NOT_IN_DB(Validator): allowed_override=[], ignore_common_filters=False, ): - - from pydal.objects import Table if isinstance(field, Table): field = field._id @@ -725,7 +727,7 @@ class IS_NOT_IN_DB(Validator): def __call__(self, value): value = to_native(str(value)) if not value.strip(): - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) if value in self.allowed_override: return (value, None) (tablename, fieldname) = str(self.field).split('.') @@ -738,11 +740,11 @@ class IS_NOT_IN_DB(Validator): fields = [table[f] for f in id] 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): - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) else: row = subset.select(table._id, field, limitby=(0, 1), orderby_on_limitby=False).first() if row and str(row[table._id]) != str(id): - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) 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' if type(maximum) in integer_types: 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): @@ -1043,7 +1045,7 @@ class IS_NOT_EMPTY(Validator): def __call__(self, value): value, empty = is_empty(value, empty_regex=self.empty_regex) if empty: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) return (value, None) @@ -1199,7 +1201,7 @@ class IS_EMAIL(Validator): def __call__(self, 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) @@ -1222,7 +1224,7 @@ class IS_EMAIL(Validator): if (not self.banned or not self.banned.match(domain)) \ and (not self.forced or self.forced.match(domain)): return (value, None) - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) class IS_LIST_OF_EMAILS(object): @@ -1253,7 +1255,7 @@ class IS_LIST_OF_EMAILS(object): return (value, None) else: return (value, - translate(self.error_message) % ', '.join(bad_emails)) + translator(self.error_message) % ', '.join(bad_emails)) def formatter(self, value, row=None): 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 # into an IDN-unaware slot asciiLabels = [] - import encodings.idna for label in labels: if 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 not value or self.GENERIC_URL.search(value): - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) if '#' in value: url, fragment_part = value.split('#') @@ -1625,7 +1626,7 @@ class IS_GENERIC_URL(Validator): try: components = urlparse.urlparse(urllib_unquote(value))._asdict() except ValueError: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) # Clean up the scheme before we check it scheme = components['scheme'] @@ -1653,7 +1654,7 @@ class IS_GENERIC_URL(Validator): else: return (value, None) # else the URL is not valid - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) # Sources (obtained 2017-Nov-11): # http://data.iana.org/TLD/tlds-alpha-by-domain.txt @@ -2089,7 +2090,7 @@ class IS_HTTP_URL(Validator): except: pass # else the HTTP URL is not valid - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) class IS_URL(Validator): @@ -2239,7 +2240,7 @@ class IS_URL(Validator): except Exception as e: # If we are not able to convert the unicode url into a # US-ASCII URL, then the URL is not valid - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) methodResult = subMethod(asciiValue) # if the validation of the US-ASCII version of the value failed if not methodResult[1] is None: @@ -2321,7 +2322,7 @@ class IS_TIME(Validator): pass except ValueError: pass - return (ivalue, translate(self.error_message)) + return (ivalue, translator(self.error_message)) # A UTC class. @@ -2352,7 +2353,7 @@ class IS_DATE(Validator): def __init__(self, format='%Y-%m-%d', error_message='Enter date as %(format)s'): - self.format = translate(format) + self.format = translator(format) self.error_message = str(error_message) self.extremes = {} @@ -2367,7 +2368,7 @@ class IS_DATE(Validator): return (value, None) except: 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): if value is None: @@ -2416,7 +2417,7 @@ class IS_DATETIME(Validator): def __init__(self, format='%Y-%m-%d %H:%M:%S', error_message='Enter date and time as %(format)s', timezone=None): - self.format = translate(format) + self.format = translator(format) self.error_message = str(error_message) self.extremes = {} self.timezone = timezone @@ -2435,7 +2436,7 @@ class IS_DATETIME(Validator): return (value, None) except: 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): if value is None: @@ -2503,9 +2504,9 @@ class IS_DATE_IN_RANGE(IS_DATE): if msg is not None: return (value, msg) 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: - return (ovalue, translate(self.error_message) % self.extremes) + return (ovalue, translator(self.error_message) % self.extremes) return (value, None) @@ -2559,9 +2560,9 @@ class IS_DATETIME_IN_RANGE(IS_DATETIME): if msg is not None: return (value, msg) 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: - return (ovalue, translate(self.error_message) % self.extremes) + return (ovalue, translator(self.error_message) % self.extremes) return (value, None) @@ -2579,11 +2580,11 @@ class IS_LIST_OF(Validator): ivalue = [ivalue] ivalue = [i for i in ivalue if str(i).strip()] if self.minimum is not None and len(ivalue) < self.minimum: - return (ivalue, translate(self.error_message or - 'Minimum length is %(min)s') % dict(min=self.minimum, max=self.maximum)) + return (ivalue, translator(self.error_message or 'Minimum length is %(min)s') % + dict(min=self.minimum, max=self.maximum)) if self.maximum is not None and len(ivalue) > self.maximum: - return (ivalue, translate(self.error_message or - 'Maximum length is %(max)s') % dict(min=self.minimum, max=self.maximum)) + return (ivalue, translator(self.error_message or 'Maximum length is %(max)s') % + dict(min=self.minimum, max=self.maximum)) new_value = [] other = self.other if self.other: @@ -2721,7 +2722,7 @@ class IS_SLUG(Validator): def __call__(self, value): 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) @@ -2750,7 +2751,7 @@ class ANY_OF(Validator): if error is None: break 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 def formatter(self, value): @@ -3038,7 +3039,7 @@ class CRYPT(object): def __call__(self, value): v = value and str(value)[:self.max_length] if not v or len(v) < self.min_length: - return ('', translate(self.error_message)) + return ('', translator(self.error_message)) if isinstance(value, LazyCrypt): return (value, None) return (LazyCrypt(self, value), None) @@ -3056,7 +3057,6 @@ otherset = frozenset( def calc_entropy(string): """ calculates a simple entropy for a given string """ - import math alphabet = 0 # alphabet size other = set() seen = set() @@ -3149,49 +3149,49 @@ class IS_STRONG(object): if self.entropy is not None: entropy = calc_entropy(value) 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)) if isinstance(self.min, int) and self.min > 0: 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 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): all_special = [ch in value for ch in self.specials] if self.special > 0: 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)) elif self.special is 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) if self.invalid: all_invalid = [ch in value for ch in self.invalid] 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) if isinstance(self.upper, int): all_upper = re.findall("[A-Z]", value) if self.upper > 0: 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)) elif self.upper is 0: if len(all_upper) > 0: failures.append( - translate("May not include any uppercase letters")) + translator("May not include any uppercase letters")) if isinstance(self.lower, int): all_lower = re.findall("[a-z]", value) if self.lower > 0: 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)) elif self.lower is 0: if len(all_lower) > 0: failures.append( - translate("May not include any lowercase letters")) + translator("May not include any lowercase letters")) if isinstance(self.number, int): all_number = re.findall("[0-9]", value) if self.number > 0: @@ -3199,20 +3199,19 @@ class IS_STRONG(object): if self.number > 1: numbers = "numbers" 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)) elif self.number is 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: return (value, None) if not self.error_message: if self.estring: - return (value, '|'.join(failures)) - from gluon.html import XML - return (value, XML('
'.join(failures))) + return (value, '|'.join(map(str, failures))) + return (value, ', '.join(failures)) else: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) class IS_IMAGE(Validator): @@ -3304,7 +3303,7 @@ class IS_IMAGE(Validator): value.file.seek(0) return (value, None) except Exception as e: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) def __bmp(self, stream): if stream.read(2) == b'BM': @@ -3397,7 +3396,7 @@ class IS_UPLOAD_FILENAME(Validator): try: string = value.filename except: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) if self.case == 1: string = string.lower() elif self.case == 2: @@ -3409,9 +3408,9 @@ class IS_UPLOAD_FILENAME(Validator): if dot == -1: dot = len(string) 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:]): - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) else: return (value, None) @@ -3581,7 +3580,7 @@ class IS_IPV4(Validator): ok = False if ok: return (value, None) - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) class IS_IPV6(Validator): @@ -3674,13 +3673,11 @@ class IS_IPV6(Validator): self.error_message = error_message def __call__(self, value): - from gluon._compat import ipaddress - try: ip = ipaddress.IPv6Address(to_unicode(value)) ok = True except ipaddress.AddressValueError: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) if self.subnets: # iterate through self.subnets to see if value is a member @@ -3691,7 +3688,7 @@ class IS_IPV6(Validator): try: ipnet = ipaddress.IPv6Network(to_unicode(network)) except (ipaddress.NetmaskValueError, ipaddress.AddressValueError): - return (value, translate('invalid subnet provided')) + return (value, translator('invalid subnet provided')) if ip in ipnet: ok = True @@ -3722,7 +3719,7 @@ class IS_IPV6(Validator): if ok: return (value, None) - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) class IS_IPADDRESS(Validator): @@ -3897,7 +3894,6 @@ class IS_IPADDRESS(Validator): self.error_message = error_message def __call__(self, value): - from gluon._compat import ipaddress IPAddress = ipaddress.ip_address IPv6Address = ipaddress.IPv6Address IPv4Address = ipaddress.IPv4Address @@ -3905,12 +3901,12 @@ class IS_IPADDRESS(Validator): try: ip = IPAddress(to_unicode(value)) except ValueError: - return (value, translate(self.error_message)) + return (value, translator(self.error_message)) 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): - retval = (value, translate(self.error_message)) + retval = (value, translator(self.error_message)) elif self.is_ipv4 or isinstance(ip, IPv4Address): retval = IS_IPV4( minip=self.minip, @@ -3934,6 +3930,6 @@ class IS_IPADDRESS(Validator): error_message=self.error_message )(value) else: - retval = (value, translate(self.error_message)) + retval = (value, translator(self.error_message)) return retval