text: import json
  import re
  
  from .crypto import CryptoTools
  
  
  class ParameterType:
      def __init__(self, required=False, container_type=None, allowed_values=None):
          self.required = required
          self.container_type = container_type
          self.allowed_values = allowed_values
  
      def parse(self, parameter_name, parameter_value):
          if parameter_value is None:
              if self.required:
                  raise AttributeError('Required parameter with name "%s" is not present' % parameter_name)
              else:
                  parameter_value = ''
          if not self.container_type:
              return parameter_value
  
          if not isinstance(parameter_value, list):
              parameter_value = parameter_value.split()
          if self.allowed_values:
              for val in parameter_value:
                  if val not in self.allowed_values:
                      raise AttributeError('Value "%s" is not allowed for parameter %s. Allowed values are %s'
                                           % (val, parameter_name,
                                              ', '.join(['"%s"' % x for x in sorted(self.allowed_values)])))
          if self.container_type is list:
              return parameter_value
          else:
              return self.container_type(parameter_value)
  
      def serialize(self, parameter_value):
          if self.container_type and parameter_value is not None:
              return list(parameter_value)
          else:
              return parameter_value
  
      def __eq__(self, other):
          if not isinstance(other, ParameterType):
              return False
          return self.required == other.required and \
                 self.container_type == other.container_type and \
                 self.allowed_values == other.allowed_values
  
  
  class Parameters:
      """
  
      """
  
      """
      List of (param_name, ParameterType(...)) 
      """
      parameter_definitions = ()
  
      REQUIRED = ParameterType(required=True)
      OPTIONAL = ParameterType(required=False)
      # ZLIB_DICT = b'openidhttp,,,,'
      ZLIB_DICT = b''
  
      def __init__(self, param_values):
          self.errors = []
          if param_values is not None:
              for parameter_name, parameter_definition in self.parameter_definitions:
                  try:
                      parsed_value = parameter_definition.parse(parameter_name, param_values.get(parameter_name, None))
                      setattr(self, parameter_name, parsed_value)
                  except AttributeError as e:
                      self.errors.append((parameter_name, e))
  
      def check_errors(self):
          if self.errors:
              raise self.errors[0][1]
          return self
  
      def to_dict(self):
          return {
              parameter_name: parameter_definition.serialize(getattr(self, parameter_name, None))
              for parameter_name, parameter_definition in self.parameter_definitions
          }
  
      def pack(self, encrypt=True, ttl=None, not_valid_before=None, key=None, prefix=b''):
          arr = []
          for d, dev in self.parameter_definitions:
              val = getattr(self, d)
              if isinstance(val, set) or isinstance(val, list) or isinstance(val, tuple):
                  val = ' '.join(val)
              arr.append(str(val).replace('\\', '\\\\').replace(',', '\\,'))
          params = ','.join(arr)
          if not encrypt:
              return params
          return CryptoTools.encrypt(params.encode('utf-8'), ttl=ttl,
                                     not_valid_before=not_valid_before, key=key, prefix=prefix,
                                     zlib_dict=Parameters.ZLIB_DICT)
  
      @classmethod
      def unpack(cls, packed_data, decrypt=True, key=None, prefix=b''):
          if decrypt:
              params = CryptoTools.decrypt(packed_data, key=key, expected_prefix=prefix,
                                           zlib_dict=Parameters.ZLIB_DICT).decode('utf-8')
          else:
              params = packed_data
          params = re.split(r'(?<!\\),', params)
          param_dict = {}
          for d,p in zip(cls.parameter_definitions, params):
              val = p.replace('\\,', ',').replace('\\\\', '\\')
              param_dict[d[0]] = val
  
          return cls(param_dict)
  
      def __eq__(self, other):
          if not isinstance(other, Parameters):
              return False
          if self.parameter_definitions != other.parameter_definitions:
              return False
          for d, definition in self.parameter_definitions:
              if getattr(self, d) != getattr(other, d):
                  return False
          return True
  
      def __str__(self):
          return json.dumps(self.to_dict(), sort_keys=True, default=lambda x: str(x))