def test_when_disconnected(self): controller = Mock() controller.msg.side_effect = stem.SocketClosed('kaboom!') interpreter = ControlInterpreter(controller) # we should be able to run non-tor commands self.assertTrue('Interpreter commands include:' in interpreter.run_command('/help')) # ... but tor commands should provide exceptions self.assertRaises(stem.SocketClosed, interpreter.run_command, 'GETINFO version')
def run_command(self, command, config): """ Runs the given command. Requests starting with a '/' are special commands to the interpreter, and anything else is sent to the control port. :param stem.control.Controller controller: tor control connection :param str command: command to be processed :returns: **list** out output lines, each line being a list of (msg, format) tuples :raises: **stem.SocketClosed** if the control connection has been severed """ if not self._controller.is_alive(): raise stem.SocketClosed() # Commands fall into three categories: # # * Interpretor commands. These start with a '/'. # # * Controller commands stem knows how to handle. We use our Controller's # methods for these to take advantage of caching and present nicer # output. # # * Other tor commands. We pass these directly on to the control port. cmd, arg = command.strip(), '' if ' ' in cmd: cmd, arg = cmd.split(' ', 1) output = '' if cmd.startswith('/'): cmd = cmd.lower() if cmd == '/quit': raise stem.SocketClosed() elif cmd == '/events': output = self.do_events(arg) elif cmd == '/info': output = self.do_info(arg) elif cmd == '/python': output = self.do_python(arg) elif cmd == '/help': output = self.do_help(arg) else: output = format("'%s' isn't a recognized command" % command, *ERROR_OUTPUT) else: cmd = cmd.upper() # makes commands uppercase to match the spec if cmd.replace('+', '') in ('LOADCONF', 'POSTDESCRIPTOR'): # provides a notice that multi-line controller input isn't yet implemented output = format(msg('msg.multiline_unimplemented_notice'), *ERROR_OUTPUT) elif cmd == 'QUIT': self._controller.msg(command) raise stem.SocketClosed() else: is_tor_command = cmd in config.get('help.usage', {}) and cmd.lower() != 'events' if self._run_python_commands and not is_tor_command: self.is_multiline_context = code.InteractiveConsole.push(self, command) return else: try: output = format(self._controller.msg(command).raw_content().strip(), *STANDARD_OUTPUT) except stem.ControllerError as exc: if isinstance(exc, stem.SocketClosed): raise exc else: output = format(str(exc), *ERROR_OUTPUT) output += '\n' # give ourselves an extra line before the next prompt return output