'Changes multiple tor configuration options via either a SETCONF or
RESETCONF query. Both behave identically unless our value is None, in which
case SETCONF sets the value to 0 or NULL, and RESETCONF returns it to its
default value. This accepts str, list, or None values in a similar fashion
to :func:`~stem.control.Controller.set_conf`. For example...
my_controller.set_options({
\'Nickname\': \'caerSidi\',
\'ExitPolicy\': [\'accept *:80\', \'accept *:443\', \'reject *:*\'],
\'ContactInfo\': \'caerSidi-exit@someplace.com\',
\'Log\': None,
The params can optionally be a list of key/value tuples, though the only
reason this type of argument would be useful is for hidden service
configuration (those options are order dependent).
:param dict,list params: mapping of configuration options to the values
we\'re setting it to
:param bool reset: issues a RESETCONF, returning **None** values to their
defaults if **True**
:raises:
* :class:`stem.ControllerError` if the call fails
* :class:`stem.InvalidArguments` if configuration options
requested was invalid
* :class:`stem.InvalidRequest` if the configuration setting is
impossible or if there\'s a syntax error in the configuration values'

def set_options(self, params, reset=False):

    start_time = time.time()
    query_comp = [('RESETCONF' if reset else 'SETCONF')]
    if isinstance(params, dict):
        params = list(params.items())
    for (param, value) in params:
        if isinstance(value, str):
            query_comp.append(('%s="%s"' % (param, value.strip())))
        elif value:
            query_comp.extend([('%s="%s"' % (param, val.strip())) for val in value])
        else:
            query_comp.append(param)
    query = '    '.join(query_comp)
    response = self.msg(query)
    stem.response.convert('SINGLELINE', response)
    if response.is_ok():
        log.debug(('%s    (runtime:    %0.4f)' % (query, (time.time() - start_time))))
        if self.is_caching_enabled():
            to_cache = {}
            for (param, value) in params:
                param = param.lower()
                if isinstance(value, (bytes, str_type)):
                    value = [value]
                to_cache[param] = value
                if (param == 'exitpolicy'):
                    self._set_cache({'exitpolicy': None})
            self._set_cache(to_cache, 'getconf')
    else:
        log.debug(('%s    (failed,    code:    %s,    message:    %s)' % (query, response.code, response.message)))
        if (response.code == '552'):
            if response.message.startswith("Unrecognized    option:    Unknown    option    '"):
                key = response.message[37:response.message.find("'", 37)]
                raise stem.InvalidArguments(response.code, response.message, [key])
            raise stem.InvalidRequest(response.code, response.message)
        elif (response.code in ('513', '553')):
            raise stem.InvalidRequest(response.code, response.message)
        else:
            raise stem.ProtocolError(('Returned    unexpected    status    code:    %s' % response.code))
