<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def user_upsert(self, domain, userid, password=None, roles=None, name=None):
 """ Upsert a user in the cluster :param AuthDomain domain: The authentication domain for the user. :param userid: The user ID :param password: The user password :param roles: A list of roles. A role can either be a simple string, or a list of `(role, bucket)` pairs. :param name: Human-readable name :raise: :exc:`couchbase.exceptions.HTTPError` if the request fails. :return: :class:`~.HttpResult` Creating a new read-only admin user :: adm.upsert_user(AuthDomain.Local, 'mark', 's3cr3t', ['ro_admin']) An example of using more complex roles :: adm.upsert_user(AuthDomain.Local, 'mark', 's3cr3t', [('data_reader', '*'), ('data_writer', 'inbox')]) .. warning:: Due to the asynchronous nature of Couchbase management APIs, it may take a few moments for the new user settings to take effect. """

        if not roles or not isinstance(roles, list):
            raise E.ArgumentError("Roles must be a non-empty list")

        if password and domain == AuthDomain.External:
            raise E.ArgumentError("External domains must not have passwords")
        tmplist = []
        for role in roles:
            if isinstance(role, basestring):
                tmplist.append(role)
            else:
                tmplist.append('{0}[{1}]'.format(*role))

        role_string = ','.join(tmplist)
        params = {
            'roles': role_string,
        }

        if password:
            params['password'] = password
        if name:
            params['name'] = name

        form = self._mk_formstr(params)
        path = self._get_management_path(domain, userid)
        return self.http_request(path=path,
                                 method='PUT',
                                 content_type='application/x-www-form-urlencoded',
                                 content=form)