Source code for validation.string

import re

# The typing module provides an abstract base class that we can check compiled
# regular expressions against.  Unfortunately this wasn't available until
# python 3.5.  For older versions of python, we fall back to using the private
# ``_pattern_type`` class in ``re``.
try:
    from typing.re import Pattern as _pattern_type
except ImportError:  # pragma: no cover
    _pattern_type = re._pattern_type  # pylint: disable=no-member

import six

from .core import _validate_bool
from .number import _validate_int
from .common import make_optional_argument_default


_undefined = make_optional_argument_default()


def _validate_text(
    value,
    min_length=None, max_length=None,
    pattern=None,
    required=True,
):
    if value is None:
        if required:
            raise TypeError("required value is None")
        return

    if not isinstance(value, six.text_type):
        raise TypeError((
            "expected unicode string, but value is of type {cls!r}"
        ).format(cls=value.__class__.__name__))

    if min_length is not None and len(value) < min_length:
        raise ValueError((
            "expected at least {min} characters, but string is only "
            "{length} characters long"
        ).format(length=len(value), min=min_length))

    if max_length is not None and len(value) > max_length:
        raise ValueError((
            "expected at most {max} characters, but string is {length} "
            "characters long"
        ).format(length=len(value), max=max_length))

    if pattern is not None:
        # Unfortunately `fullmatch` is not available in python2.
        match = pattern.match(value)

        if not (
            match is not None and
            match.start() == 0 and
            match.end() == len(value)
        ):
            raise ValueError(
                "string did not match pattern"
            )


class _text_validator(object):
    def __init__(self, min_length, max_length, pattern, required):
        _validate_int(min_length, min_value=0, required=False)
        _validate_int(max_length, min_value=0, required=False)
        if (
            min_length is not None and max_length is not None and
            min_length > max_length
        ):
            raise ValueError((
                'minimum length {min!r} is greater than maximum length {max!r}'
            ).format(min=min_length, max=max_length))

        self.__min_length = min_length
        self.__max_length = max_length

        _validate_bool(required)
        self.__required = required

        if pattern is None:
            compiled_pattern = pattern
        elif isinstance(pattern, six.string_types):
            # Note that we are a little more permissive about non-unicode
            # patterns in python2 than we are about non-unicode arguments.
            # Users will probably written the pattern argument inline.
            compiled_pattern = re.compile(pattern)
        elif isinstance(pattern, _pattern_type):
            compiled_pattern = pattern
        else:
            raise TypeError((
                "expected compiled regex or string, "
                "but pattern is of type {cls!r}"
            ).format(cls=pattern.__class__.__name__))

        self.__pattern = pattern
        self.__compiled_pattern = compiled_pattern

    def __call__(self, value):
        _validate_text(
            value,
            min_length=self.__min_length, max_length=self.__max_length,
            pattern=self.__compiled_pattern, required=self.__required,
        )

    def __repr__(self):
        args = []
        if self.__min_length is not None:
            args.append('min_length={min_length!r}'.format(
                min_length=self.__min_length,
            ))

        if self.__max_length is not None:
            args.append('max_length={max_length!r}'.format(
                max_length=self.__max_length,
            ))

        if self.__pattern is not None:
            args.append('pattern={pattern!r}'.format(
                pattern=self.__pattern,
            ))

        if not self.__required:
            args.append('required={required!r}'.format(
                required=self.__required,
            ))

        return 'validate_text({args})'.format(args=', '.join(args))


[docs]def validate_text( value=_undefined, min_length=None, max_length=None, pattern=None, required=True, ): """ Checks that the target value is a valid human readable string value. In python 2 this will strictly enforce the use of ``unicode``. ``str``s are not accepted as there is no way to tell if they are the result of decoding a byte-string containing only ``latin-1`` characters or if they are still encoded. In python 3 things are much simpler. Patterns are python regular expressions and must match the entire string. A simple example that uses the pattern parameter to validate a string describing a date: .. code:: python def parse_date(string): validate_text(string, pattern='[0-9]{4}-[0-9]{2}-[0-9]{2}') # Do something ... :param unicode value: The string to be validated. :param int min_length: The minimum length of the string. :param int max_length: The maximum acceptable length for the string. By default, the length is not checked. :param str|re.Pattern pattern: Regular expression to check the value against. :param bool required: Whether the value can be `None`. Defaults to `True`. :raises TypeError: If the value is not a unicode string , or if it was marked as `required` but `None` was passed in. :raises ValueError: If the value was longer or shorter than expected, or did not match the pattern. """ validate = _text_validator( min_length=min_length, max_length=max_length, pattern=pattern, required=required, ) if value is not _undefined: validate(value) else: return validate
def _validate_bytes(value, min_length, max_length, required): if value is None: if required: raise TypeError("required value is None") return if not isinstance(value, six.binary_type): raise TypeError(( "expected byte string, but value is of type {cls!r}" ).format(cls=value.__class__.__name__)) if min_length is not None and len(value) < min_length: raise ValueError(( "expected at least {min} bytes, but bytestring contains only " "{length}" ).format(length=len(value), min=min_length)) if max_length is not None and len(value) > max_length: raise ValueError(( "expected at most {max} bytes, but bytestring contains {length}" ).format(length=len(value), max=max_length)) class _bytes_validator(object): def __init__(self, min_length, max_length, required): _validate_int(min_length, min_value=0, required=False) _validate_int(max_length, min_value=0, required=False) if ( min_length is not None and max_length is not None and min_length > max_length ): raise ValueError(( 'minimum length {min!r} is greater than maximum length {max!r}' ).format(min=min_length, max=max_length)) self.__min_length = min_length self.__max_length = max_length _validate_bool(required) self.__required = required def __call__(self, value): _validate_bytes( value, min_length=self.__min_length, max_length=self.__max_length, required=self.__required, ) def __repr__(self): args = [] if self.__min_length is not None: args.append('min_length={min_length!r}'.format( min_length=self.__min_length, )) if self.__max_length is not None: args.append('max_length={max_length!r}'.format( max_length=self.__max_length, )) if not self.__required: args.append('required={required!r}'.format( required=self.__required, )) return 'validate_bytes({args})'.format(args=', '.join(args))
[docs]def validate_bytes( value=_undefined, min_length=None, max_length=None, required=True, ): """ Checks that the supplied value is a valid byte-string. In python 3 will accepts `bytes`, in python 2 `str`. Should not be used for validating human readable strings, Please use :func:`validate_text` instead. :param bytes value: The string to be validated. :param int min_length: The minimum length of the string. :param int max_length: The maximum acceptable length for the string. By default, the length is not checked. :param bool required: Whether the value can be `None`. Defaults to `True`. :raises TypeError: If the value is not a byte-string, or if it was marked as `required` but `None` was passed in. :raises ValueError: If the value was longer or shorter than expected. """ validate = _bytes_validator( min_length=min_length, max_length=max_length, required=required, ) if value is not _undefined: validate(value) else: return validate