<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 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)