예제 #1
0
파일: convs.py 프로젝트: motor23/cmstest
class EnumChoice(Converter):
    '''
    In addition to Converter interface it must provide
    :meth:`options` and :meth:`get_label` methods.
    '''

    #: converter for value, before it is tested to be in a set of acceptable
    #: values
    conv = Char()

    #: acceptable choices list::
    #:
    #:     EnumChoice(choices=[(python_value, label), ...])
    choices = ()
    error_required = N_('you must select a value')

    def from_python(self, value):
        conv = self.conv
        return conv.from_python(value)

    def to_python(self, value):
        value = self.conv.accept(value, silent=True)
        if value not in dict(self.choices):
            return None
        return value

    def options(self):
        '''
        Yields `(raw_value, label)` pairs for all acceptable choices.
        '''
        conv = self.conv
        for python_value, label in self.choices:
            yield conv.from_python(python_value), label
예제 #2
0
class PasswordConv(convs.Char):

    error_mismatch = N_('password and confirm mismatch')
    error_required = N_('password required')

    def from_python(self, value):
        return dict([(field.name, None) for field in self.field.fields])

    def get_initial(self):
        return ''

    def to_python(self, value):
        etalon = value[list(value)[0]]
        for field in self.field.fields:
            self.assert_(value[field.name] == etalon, self.error_mismatch)
        if self.required:
            self.assert_(etalon not in (None, ''), self.error_required)
        elif etalon in (None, ''):
            return None
        return etalon
예제 #3
0
파일: convs.py 프로젝트: motor23/cmstest
 def clean_value(self, value):
     value = Char.clean_value(self, value)
     try:
         doc = html.fragment_fromstring(value, create_parent=True)
     except XMLSyntaxError: # pragma: no cover. XXX: seems like this exception is
                            # unreachable with create_parent=True,
                            # maybe it should be removed?
         raise ValidationError(N_(u'Error parsing HTML'))
     self.cleaner(doc)
     clean = html.tostring(doc, encoding='utf-8').decode('utf-8')
     clean = clean.split('>', 1)[1].rsplit('<', 1)[0]
     return self.Markup(clean)
예제 #4
0
파일: convs.py 프로젝트: motor23/cmstest
    def __init__(self, min_length, max_length):
        self.min_length = min_length
        self.max_length = max_length

        self.format_args = dict(min=min_length, max=max_length)

        if min_length == max_length:
            self.message = M_(u'length of value must be exactly %(max)d symbol',
                              u'length of value must be exactly %(max)d symbols',
                              count_field="max",
                              format_args=self.format_args)
        else:
            self.message = N_('length should be between %(min)d and %(max)d symbols')
예제 #5
0
파일: convs.py 프로젝트: motor23/cmstest
class ValidationError(Exception):
    '''
    Error raised from inside of `Converter.to_python` or validator function.

    :param unicode message: error message for current validating
        field, for most cases.
    :param dict by_field: contains {field-name: error message} pairs.
    :param dict format_args: used to format error message.
    '''

    default_message = N_('Something is wrong')

    def __init__(self, message=None, by_field=None, format_args=None):
        if not (message or by_field):
            message = self.default_message
        self.message = message
        self.by_field = by_field or {}
        self.format_args = format_args or {}

    def translate(self, env, message):
        format_args = self.format_args
        if isinstance(message, M_):
            trans = env.ngettext(message.single,
                                 message.plural,
                                 message.count)
            format_args = dict(format_args,
                               **message.format_args)
        else:
            trans = env.gettext(message)
        return trans % format_args

    def fill_errors(self, field):
        form = field.form
        if self.message is not None:
            form.errors[field.input_name] = self.translate(form.env, self.message)
        for name, message in self.by_field.items():
            if name.startswith('.'):
                nm, f = name.lstrip('.'), field
                for i in range(len(name) - len(nm) - 1):
                    f = f.parent
                rel_field = f.get_field(nm)
                name = rel_field.input_name
            else:
                rel_field = form.get_field(name)
            form.errors[name] = self.translate(form.env, message)

    def __repr__(self):
        return "{}({!r}, {!r})".format(self.__class__, self.message,
                                       self.by_field)
예제 #6
0
파일: convs.py 프로젝트: motor23/cmstest
def between(min_value, max_value):
    'Numerical values limit'
    message = N_('value should be between %(min)d and %(max)d') % \
                    dict(min=min_value, max=max_value)

    @validator(message)
    def wrapper(conv, value):
        if value is None:
            # it meens that this value is not required
            return True
        if value < min_value:
            return False
        if value > max_value:
            return False
        return True
    return wrapper
예제 #7
0
파일: convs.py 프로젝트: motor23/cmstest
class Email(Char):

    regex = re.compile(
        # dot-atom
        r"(^[-!#$%&'*+/=?^_`{}|~0-9A-Z]+(\.[-!#$%&'*+/=?^_`{}|~0-9A-Z]+)*"
        # Although quoted variant is allowed by spec it's actually not used
        # except by geeks that are looking for problems. But the characters
        # allowed in quoted string are not safe for HTML and XML, so quoted
        # e-mail can't be expressed in such formats directly which is quite
        # common. We prefer to forbid such valid but unsafe e-mails to avoid
        # security problems. To allow quoted names disable non-text characters
        # replacement and uncomment the following lines of regexp:
        ## quoted-string
        #r'|^"([\001-\010\013\014\016-\037!#-\[\]-\177]|'
        #    r'\\[\001-011\013\014\016-\177])*"'
        r')@(?:[A-Z0-9-]+\.)+[A-Z]{2,6}$', re.IGNORECASE)
    error_regex = N_('incorrect e-mail address')
예제 #8
0
파일: convs.py 프로젝트: motor23/cmstest
class BaseDatetime(CharBased):
    '''
    A base class for `Datetime`, `Date` and `Time` converters.
    '''

    #: format used to convert from string by `strptime`
    #: and to convert to string by `strftime`.
    format = None
    #: format used in error message. By default, generated automatically
    #: based `format` and `replacements` attributes
    readable_format = None
    replacements = (('%H', 'HH'), ('%M', 'MM'), ('%d', 'DD'),
                    ('%m', 'MM'), ('%Y', 'YYYY'))
    #: error message for wrong format.
    error_wrong_format = N_('Wrong format (%(readable_format)s)')
    nontext_replacement = ''

    def __init__(self, *args, **kwargs):
        if not 'readable_format' in kwargs or 'format' in kwargs:
            replacements = self.replacements # XXX make language-dependent
            fmt = kwargs.get('format', self.format)
            for repl in replacements:
                fmt = fmt.replace(*repl)
            kwargs['readable_format'] = fmt
        Converter.__init__(self, *args, **kwargs)

    def from_python(self, value):
        if value is None:
            return ''
        # carefull to years before 1900
        return strftime(value, self.format)

    def to_python(self, value):
        value = self.clean_value(value)
        if not value:
            return None
        try:
            return self.convert_datetime(value)
        except ValueError:
            format_args = dict(readable_format=self.readable_format)
            raise ValidationError(self.error_wrong_format,
                                  format_args=format_args)
예제 #9
0
파일: convs.py 프로젝트: motor23/cmstest
class Int(Converter):
    """
    Converts digital sequences to `int`
    """

    #: Error message for the case value can not be converted to int
    error_notvalid = N_('it is not valid integer')

    def to_python(self, value):
        if value == '':
            return None
        try:
            value = int(value)
        except ValueError:
            raise ValidationError(self.error_notvalid)
        return value

    def from_python(self, value):
        if value is None:
            return ''
        return six.text_type(value)
예제 #10
0
파일: convs.py 프로젝트: motor23/cmstest
class Char(CharBased):

    '''Converts and validates strings'''

    #: Regexp to match input string
    regex = None
    #: Error message for the case self.regex does not match
    error_regex = N_('field should match %(regex)s')
    #: Whether strip value before convertation or not
    strip = True

    @property
    def max_length(self):
        length_validators = [x for x in self.validators
                             if isinstance(x, length)]
        if length_validators:
            return min([x.max_length for x in length_validators])
        return None

    def to_python(self, value):
        # converting
        value = self.clean_value(value)
        if value and self.regex:
            regex = self.regex
            if isinstance(regex, six.string_types):
                regex = re.compile(self.regex, re.U)
            if not regex.match(value):
                error = self.error_regex % {'regex': self.regex}
                raise ValidationError(error)
        return value

    def from_python(self, value):
        if value is None:
            return ''
        if six.PY3 and isinstance(value, bytes):
            raise TypeError() # pragma: no cover, safety check
        return six.text_type(value)
예제 #11
0
파일: convs.py 프로젝트: motor23/cmstest
class Converter(object):
    '''
    :meth:`accept` method takes value from field
    and converts it to python type.

    Subclasses must redefine :meth:`to_python` method to make their
    own convertation and validation.

    :meth:`from_python` method takes value as python
    object and converts it to string or something
    else widget can display.

    Accepts a list of validators as **\*args**.
    '''

    # obsolete parameters from previous versions
    _obsolete = frozenset(['max_length', 'min_length', 'null', 'min', 'max',
                           'multiple', 'initial'])
    #: Flag of whether perform require check after :meth:`to_python` method or not.
    #: The resulting value is checked to be non-empty (`[]`, `None`).
    required = False
    multiple = False

    #: An ordered list of validator functions. Are passed as position args
    #: to the converter::
    #:
    #:     Int(validator1, validator2)
    #:
    #: When a converter is copied, new validators are added to existing ones.
    validators = ()

    #: Values are not accepted by Required validator
    error_required = N_('required field')

    def __init__(self, *args, **kwargs):
        if self._obsolete & set(kwargs):
            raise TypeError(
                    'Obsolete parameters are used: {}'.format(
                                list(self._obsolete & set(kwargs))))
        self.field = weakproxy(kwargs.get('field'))
        self._init_kwargs = kwargs
        self.__dict__.update(kwargs)
        self.validators = tuple(self.validators) + args

    @property
    def env(self):
        '''A shortcut for `form.env`'''
        return self.field.env

    def _is_empty(self, value):
        return value in ('', [], {}, None)

    def accept(self, value, silent=False):
        '''
        Accepts a value from the form, calls :meth:`to_python` method,
        checks `required` condition, applies filters and validators,
        catches ValidationError.

        :param value: a value to be accepted
        :param silent=False: write errors to `form.errors` or not
        '''
        try:
            value = self.to_python(value)
            for v in self.validators:
                value = v(self, value)

            if self.required and self._is_empty(value):
                raise ValidationError(self.error_required)
        except ValidationError as e:
            if not silent:
                e.fill_errors(self.field)
            #NOTE: by default value for field is in python_data,
            #      but this is not true for FieldList where data
            #      is dynamic, so we set value to None for absent value.
            value = self._existing_value
        return value

    def to_python(self, value):
        """
        Converts value and validates it.
        Custom converters should override this
        """
        if value == '':
            return None # XXX is this right?
        return value

    def from_python(self, value):
        """
        Serializes value.
        Custom converters should override this
        """
        if value is None:
            value = ''
        return value

    def __call__(self, *args, **kwargs):
        '''
        Creates current object's copy with extra constructor arguments
        (including validators) passed.
        '''
        kwargs = dict(self._init_kwargs, **kwargs)
        kwargs.setdefault('field', self.field)
        validators = kwargs.pop('validators', self.validators)
        validators = tuple(validators) + args
        return self.__class__(*validators, **kwargs)

    def assert_(self, expression, msg):
        'Shortcut for assertions of certain type'
        if not expression:
            raise ValidationError(msg)

    @property
    def _existing_value(self):
        if self.field is not None:
            return self.field.parent.python_data.get(self.field.name)
        return [] if self.multiple else None

    def __repr__(self):
        args = ', '.join([k+'='+repr(v) for
                          k, v in self._init_kwargs.items()
                          if k!='parent'])
        return '{}({})'.format(self.__class__.__name__, args)
예제 #12
0
파일: auth.py 프로젝트: motor23/cmstest
class LoginForm(Form):
    fields = (Field('login', convs.Char(), label=N_('Username')),
              Field('password',
                    convs.Char(),
                    widget=widgets.PasswordInput(),
                    label=N_(u'Password')))