text: # -*- coding: utf-8 -*-
  from __future__ import unicode_literals
  
  import re
  import uuid
  from numbers import Number
  
  from jsonlint.compat import string_types, text_type
  
  __all__ = (
      'DataRequired', 'data_required', 'Email', 'email', 'EqualTo', 'equal_to',
      'IPAddress', 'ip_address', 'InputRequired', 'input_required', 'Length',
      'length', 'NumberRange', 'number_range', 'Optional', 'optional',
      'Regexp', 'regexp', 'URL', 'url', 'AnyOf',
      'any_of', 'NoneOf', 'none_of', 'MacAddress', 'mac_address', 'UUID'
  )
  
  
  class ValidationError(ValueError):
      """
      Raised when a validator fails to validate its input.
      """
      def __init__(self, message='', *args, **kwargs):
          ValueError.__init__(self, message, *args, **kwargs)
  
  
  class StopValidation(Exception):
      """
      Causes the validation chain to stop.
  
      If StopValidation is raised, no more validators in the validation chain are
      called. If raised with a message, the message will be added to the errors
      list.
      """
      def __init__(self, message='', *args, **kwargs):
          Exception.__init__(self, message, *args, **kwargs)
  
  
  class EqualTo(object):
      """
      Compares the values of two fields.
  
      :param fieldname:
          The name of the other field to compare to.
      :param message:
          Error message to raise in case of a validation error. Can be
          interpolated with `%(other_label)s` and `%(other_name)s` to provide a
          more helpful error.
      """
      def __init__(self, fieldname, message=None):
          self.fieldname = fieldname
          self.message = message
  
      def __call__(self, form, field):
          try:
              other = form[self.fieldname]
          except KeyError:
              raise ValidationError(field.gettext("Invalid field name '%s'.") % self.fieldname)
          if field.data != other.data:
              d = {
                  'other_label': hasattr(other, 'label') and other.label.text or self.fieldname,
                  'other_name': self.fieldname
              }
              message = self.message
              if message is None:
                  message = field.gettext('Field must be equal to %(other_name)s.')
  
              raise ValidationError(message % d)
  
  
  class Length(object):
      """
      Validates the length of a string.
  
      :param min:
          The minimum required length of the string. If not provided, minimum
          length will not be checked.
      :param max:
          The maximum length of the string. If not provided, maximum length
          will not be checked.
      :param message:
          Error message to raise in case of a validation error. Can be
          interpolated using `%(min)d` and `%(max)d` if desired. Useful defaults
          are provided depending on the existence of min and max.
      """
      def __init__(self, min=-1, max=-1, message=None):
          assert min != -1 or max != -1, 'At least one of `min` or `max` must be specified.'
          assert max == -1 or min <= max, '`min` cannot be more than `max`.'
          self.min = min
          self.max = max
          self.message = message
  
      def __call__(self, form, field):
          l = field.data and len(field.data) or 0
          if l < self.min or self.max != -1 and l > self.max:
              message = self.message
              if message is None:
                  if self.max == -1:
                      message = field.ngettext('Field must be at least %(min)d character long.',
                                               'Field must be at least %(min)d characters long.', self.min)
                  elif self.min == -1:
                      message = field.ngettext('Field cannot be longer than %(max)d character.',
                                               'Field cannot be longer than %(max)d characters.', self.max)
                  else:
                      message = field.gettext('Field must be between %(min)d and %(max)d characters long.')
  
              raise ValidationError(message % dict(min=self.min, max=self.max, length=l))
  
  
  class NumberRange(object):
      """
      Validates that a number is of a minimum and/or maximum value, inclusive.
      This will work with any comparable number type, such as floats and
      decimals, not just integers.
  
      :param min:
          The minimum required value of the number. If not provided, minimum
          value will not be checked.
      :param max:
          The maximum value of the number. If not provided, maximum value
          will not be checked.
      :param message:
          Error message to raise in case of a validation error. Can be
          interpolated using `%(min)s` and `%(max)s` if desired. Useful defaults
          are provided depending on the existence of min and max.
      """
      def __init__(self, min=None, max=None, message=None):
          self.min = min
          self.max = max
          self.message = message
  
      def __call__(self, form, field):
          data = field.data
          if data is None or (self.min is not None and data < self.min) or \
                  (self.max is not None and data > self.max):
              message = self.message
              if message is None:
                  # we use %(min)s interpolation to support floats, None, and
                  # Decimals without throwing a formatting exception.
                  if self.max is None:
                      message = field.gettext('Number must be at least %(min)s.')
                  elif self.min is None:
                      message = field.gettext('Number must be at most %(max)s.')
                  else:
                      message = field.gettext('Number must be between %(min)s and %(max)s.')
  
              raise ValidationError(message % dict(min=self.min, max=self.max))
  
  
  class Optional(object):
      """
      Allows empty input and stops the validation chain from continuing.
  
      If input is empty, also removes prior errors (such as processing errors)
      from the field.
  
      :param strip_whitespace:
          If True (the default) also stop the validation chain on input which
          consists of only whitespace.
      """
      field_flags = ('optional', )
  
      def __init__(self, strip_whitespace=True):
          if strip_whitespace:
              self.string_check = lambda s: s.strip()
          else:
              self.string_check = lambda s: s
  
      def __call__(self, form, field):
          if not field.raw_data or isinstance(field.raw_data[0], string_types) and not self.string_check(field.raw_data[0]):
              field.errors[:] = []
              raise StopValidation()
  
  
  class DataRequired(object):
      """
      Checks the field's data is 'truthy' otherwise stops the validation chain.
  
      This validator checks that the ``data`` attribute on the field is a 'true'
      value (effectively, it does ``if field.data``.) Furthermore, if the data
      is a string type, a string containing only whitespace characters is
      considered false.
  
      If the data is empty, also removes prior errors (such as processing errors)
      from the field.
  
      **NOTE** this validator used to be called `Required` but the way it behaved
      (requiring coerced data, not input data) meant it functioned in a way
      which was not symmetric to the `Optional` validator and furthermore caused
      confusion with certain fields which coerced data to 'falsey' values like
      ``0``, ``Decimal(0)``, ``time(0)`` etc. Unless a very specific reason
      exists, we recommend using the :class:`InputRequired` instead.
  
      :param message:
          Error message to raise in case of a validation error.
      """
      field_flags = ('required', )
  
      def __init__(self, message=None):
          self.message = message
  
      def __call__(self, form, field):
          if not field.data or isinstance(field.data, string_types) and not field.data.strip():
              if self.message is None:
                  message = field.gettext('This field is required.')
              else:
                  message = self.message
  
              field.errors[:] = []
              raise StopValidation(message)
  
  
  class InputRequired(object):
      """
      Validates that input was provided for this field.
  
      Note there is a distinction between this and DataRequired in that
      InputRequired looks that form-input data was provided, and DataRequired
      looks at the post-coercion data.
      """
      field_flags = ('required', )
  
      def __init__(self, message=None):
          self.message = message
  
      def __call__(self, form, field):
          raw_data = field.raw_data
          if not isinstance(raw_data, Number) and not raw_data or (isinstance(raw_data, list) and not field.raw_data[0]):
              if self.message is None:
                  message = field.gettext('This field is required.')
              else:
                  message = self.message
  
              field.errors[:] = []
              raise StopValidation(message)
  
  
  class Regexp(object):
      """
      Validates the field against a user provided regexp.
  
      :param regex:
          The regular expression string to use. Can also be a compiled regular
          expression pattern.
      :param flags:
          The regexp flags to use, for example re.IGNORECASE. Ignored if
          `regex` is not a string.
      :param message:
          Error message to raise in case of a validation error.
      """
      def __init__(self, regex, flags=0, message=None):
          if isinstance(regex, string_types):
              regex = re.compile(regex, flags)
          self.regex = regex
          self.message = message
  
      def __call__(self, form, field, message=None):
          match = self.regex.match(field.data or '')
          if not match:
              if message is None:
                  if self.message is None:
                      message = field.gettext('Invalid input.')
                  else:
                      message = self.message
  
              raise ValidationError(message)
          return match
  
  
  class Email(Regexp):
      """
      Validates an email address. Note that this uses a very primitive regular
      expression and should only be used in instances where you later verify by
      other means, such as email activation or lookups.
  
      :param message:
          Error message to raise in case of a validation error.
      """
      def __init__(self, message=None):
          self.validate_hostname = HostnameValidation(
              require_tld=True,
          )
          super(Email, self).__init__(r'^.+@([^.@][^@]+)$', re.IGNORECASE, message)
  
      def __call__(self, form, field):
          message = self.message
          if message is None:
              message = field.gettext('Invalid email address.')
  
          match = super(Email, self).__call__(form, field, message)
          if not self.validate_hostname(match.group(1)):
              raise ValidationError(message)
  
  
  class IPAddress(object):
      """
      Validates an IP address.
  
      :param ipv4:
          If True, accept IPv4 addresses as valid (default True)
      :param ipv6:
          If True, accept IPv6 addresses as valid (default False)
      :param message:
          Error message to raise in case of a validation error.
      """
      def __init__(self, ipv4=True, ipv6=False, message=None):
          if not ipv4 and not ipv6:
              raise ValueError('IP Address Validator must have at least one of ipv4 or ipv6 enabled.')
          self.ipv4 = ipv4
          self.ipv6 = ipv6
          self.message = message
  
      def __call__(self, form, field):
          value = field.data
          valid = False
          if value:
              valid = (self.ipv4 and self.check_ipv4(value)) or (self.ipv6 and self.check_ipv6(value))
  
          if not valid:
              message = self.message
              if message is None:
                  message = field.gettext('Invalid IP address.')
              raise ValidationError(message)
  
      @classmethod
      def check_ipv4(cls, value):
          parts = value.split('.')
          if len(parts) == 4 and all(x.isdigit() for x in parts):
              numbers = list(int(x) for x in parts)
              return all(num >= 0 and num < 256 for num in numbers)
          return False
  
      @classmethod
      def check_ipv6(cls, value):
          parts = value.split(':')
          if len(parts) > 8:
              return False
  
          num_blank = 0
          for part in parts:
              if not part:
                  num_blank += 1
              else:
                  try:
                      value = int(part, 16)
                  except ValueError:
                      return False
                  else:
                      if value < 0 or value >= 65536:
                          return False
  
          if num_blank < 2:
              return True
          elif num_blank == 2 and not parts[0] and not parts[1]:
              return True
          return False
  
  
  class MacAddress(Regexp):
      """
      Validates a MAC address.
  
      :param message:
          Error message to raise in case of a validation error.
      """
      def __init__(self, message=None):
          pattern = r'^(?:[0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}$'
          super(MacAddress, self).__init__(pattern, message=message)
  
      def __call__(self, form, field):
          message = self.message
          if message is None:
              message = field.gettext('Invalid Mac address.')
  
          super(MacAddress, self).__call__(form, field, message)
  
  
  class URL(Regexp):
      """
      Simple regexp based url validation. Much like the email validator, you
      probably want to validate the url later by other means if the url must
      resolve.
  
      :param require_tld:
          If true, then the domain-name portion of the URL must contain a .tld
          suffix.  Set this to false if you want to allow domains like
          `localhost`.
      :param message:
          Error message to raise in case of a validation error.
      """
      def __init__(self, require_tld=True, message=None):
          regex = r'^[a-z]+://(?P<host>[^/:]+)(?P<port>:[0-9]+)?(?P<path>\/.*)?$'
          super(URL, self).__init__(regex, re.IGNORECASE, message)
          self.validate_hostname = HostnameValidation(
              require_tld=require_tld,
              allow_ip=True,
          )
  
      def __call__(self, form, field):
          message = self.message
          if message is None:
              message = field.gettext('Invalid URL.')
  
          match = super(URL, self).__call__(form, field, message)
          if not self.validate_hostname(match.group('host')):
              raise ValidationError(message)
  
  
  class UUID(object):
      """
      Validates a UUID.
  
      :param message:
          Error message to raise in case of a validation error.
      """
      def __init__(self, message=None):
          self.message = message
  
      def __call__(self, form, field):
          message = self.message
          if message is None:
              message = field.gettext('Invalid UUID.')
          try:
              uuid.UUID(field.data)
          except ValueError:
              raise ValidationError(message)
  
  
  class AnyOf(object):
      """
      Compares the incoming data to a sequence of valid inputs.
  
      :param values:
          A sequence of valid inputs.
      :param message:
          Error message to raise in case of a validation error. `%(values)s`
          contains the list of values.
      :param values_formatter:
          Function used to format the list of values in the error message.
      """
      def __init__(self, values, message=None, values_formatter=None):
          self.values = values
          self.message = message
          if values_formatter is None:
              values_formatter = self.default_values_formatter
          self.values_formatter = values_formatter
  
      def __call__(self, form, field):
          if field.data not in self.values:
              message = self.message
              if message is None:
                  message = field.gettext('Invalid value, must be one of: %(values)s.')
  
              raise ValidationError(message % dict(values=self.values_formatter(self.values)))
  
      @staticmethod
      def default_values_formatter(values):
          return ', '.join(text_type(x) for x in values)
  
  
  class NoneOf(object):
      """
      Compares the incoming data to a sequence of invalid inputs.
  
      :param values:
          A sequence of invalid inputs.
      :param message:
          Error message to raise in case of a validation error. `%(values)s`
          contains the list of values.
      :param values_formatter:
          Function used to format the list of values in the error message.
      """
      def __init__(self, values, message=None, values_formatter=None):
          self.values = values
          self.message = message
          if values_formatter is None:
              values_formatter = self.default_values_formatter
          self.values_formatter = values_formatter
  
      def __call__(self, form, field):
          if field.data in self.values:
              message = self.message
              if message is None:
                  message = field.gettext('Invalid value, can\'t be any of: %(values)s.')
  
              raise ValidationError(message % dict(values=self.values_formatter(self.values)))
  
      @staticmethod
      def default_values_formatter(v):
          return ', '.join(text_type(x) for x in v)
  
  
  class HostnameValidation(object):
      """
      Helper class for checking hostnames for validation.
  
      This is not a validator in and of itself, and as such is not exported.
      """
      hostname_part = re.compile(r'^(xn-|[a-z0-9]+)(-[a-z0-9]+)*$', re.IGNORECASE)
      tld_part = re.compile(r'^([a-z]{2,20}|xn--([a-z0-9]+-)*[a-z0-9]+)$', re.IGNORECASE)
  
      def __init__(self, require_tld=True, allow_ip=False):
          self.require_tld = require_tld
          self.allow_ip = allow_ip
  
      def __call__(self, hostname):
          if self.allow_ip:
              if IPAddress.check_ipv4(hostname) or IPAddress.check_ipv6(hostname):
                  return True
  
          # Encode out IDNA hostnames. This makes further validation easier.
          hostname = hostname.encode('idna')
  
          # Turn back into a string in Python 3x
          if not isinstance(hostname, string_types):
              hostname = hostname.decode('ascii')
  
          # Check that all labels in the hostname are valid
          parts = hostname.split('.')
          for part in parts:
              if not part or len(part) > 63:
                  return False
              if not self.hostname_part.match(part):
                  return False
  
          if self.require_tld:
              if len(parts) < 2 or not self.tld_part.match(parts[-1]):
                  return False
  
          return True
  
  
  email = Email
  equal_to = EqualTo
  ip_address = IPAddress
  mac_address = MacAddress
  length = Length
  number_range = NumberRange
  optional = Optional
  input_required = InputRequired
  data_required = DataRequired
  regexp = Regexp
  url = URL
  any_of = AnyOf
  none_of = NoneOf
  