def new(self, user_id, tokens=None, user_data=None, valid_until=None,
            client_ip=None, encoding='utf-8'):
        """Creates a new authentication ticket.

        Args:
            user_id: User id to store in ticket (stored in plain text)
            tokens: Optional sequence of token strings to store in the ticket
                (stored in plain text).
            user_data: Optional user data to store in the ticket (string like
                object stored in plain text)
            valid_until: Expiration time of ticket as a integer (typically
                time.time() + seconds).
            client_ip: Optional string or ip_address.IPAddress of the client.
            encoding: Optional encoding type that is used when hashing the
                strings passed to the function

        Returns:
            A ticket string that can later be used to identify the user
        """
        if valid_until is None:
            valid_until = int(time.time()) + TicketFactory._DEFAULT_TIMEOUT
        else:
            valid_until = int(valid_until)

        # Make sure we dont have any exclamations in the user_id
        user_id = ulp.quote(user_id)

        # Create a comma seperated list of tokens
        token_str = ''
        if tokens:
            # Escape characters in our tokens
            token_str = ','.join((ulp.quote(t) for t in tokens))

        # Encode our user data (a string)
        user_str = '' if not user_data else ulp.quote(user_data)

        # Get our address
        ip = self._DEFAULT_IP if client_ip is None else ip_address(client_ip)

        # Create our digest
        data0 = bytes([ip.version]) + ip.packed + pack(">I", valid_until)
        data1 = ('\0'.join((user_id, token_str, user_str))).encode(encoding)
        digest = self._hexdigest(data0, data1)

        # digest + timestamp as an eight character hexadecimal + userid
        parts = ('{0}{1:08x}{2}'.format(digest, valid_until, user_id),
                 token_str, user_str)
        return '!'.join(parts)
Creates a new authentication ticket.

        Args:
            user_id: User id to store in ticket (stored in plain text)
            tokens: Optional sequence of token strings to store in the ticket
                (stored in plain text).
            user_data: Optional user data to store in the ticket (string like
                object stored in plain text)
            valid_until: Expiration time of ticket as a integer (typically
                time.time() + seconds).
            client_ip: Optional string or ip_address.IPAddress of the client.
            encoding: Optional encoding type that is used when hashing the
                strings passed to the function

        Returns:
            A ticket string that can later be used to identify the user