diff --git a/deluge/ui/console/__init__.py b/deluge/ui/console/__init__.py index ca6c26376..5eebf8117 100644 --- a/deluge/ui/console/__init__.py +++ b/deluge/ui/console/__init__.py @@ -22,3 +22,4 @@ # Boston, MA 02110-1301, USA. # UI_PATH = __path__[0] +from main import start diff --git a/deluge/ui/console/colors.py b/deluge/ui/console/colors.py index 572e5af35..4d88508a1 100644 --- a/deluge/ui/console/colors.py +++ b/deluge/ui/console/colors.py @@ -93,6 +93,17 @@ def replace_tabs(line): line = line.replace("\t", " " * tab_length, 1) return line +def strip_colors(line): + """ + Returns a string with the color formatting removed. + + """ + # Remove all the color tags + while line.find("{!") != -1: + line = line[:line.find("{!")] + line[line.find("!}") + 2:] + + return line + def get_line_length(line): """ Returns the string length without the color formatting. @@ -102,8 +113,7 @@ def get_line_length(line): raise BadColorString("Number of {! is not equal to number of !}") # Remove all the color tags - while line.find("{!") != -1: - line = line[:line.find("{!")] + line[line.find("!}") + 2:] + line = strip_colors(line) # Replace tabs with the appropriate amount of spaces line = replace_tabs(line) diff --git a/deluge/ui/console/commands/add.py b/deluge/ui/console/commands/add.py index 0155c2827..1576acea1 100644 --- a/deluge/ui/console/commands/add.py +++ b/deluge/ui/console/commands/add.py @@ -22,6 +22,8 @@ # 51 Franklin Street, Fifth Floor # Boston, MA 02110-1301, USA. # +from twisted.internet import defer + from deluge.ui.console.main import BaseCommand import deluge.ui.console.colors as colors from deluge.ui.client import client @@ -47,6 +49,8 @@ class Command(BaseCommand): if options["path"]: t_options["download_location"] = options["path"] + # Keep a list of deferreds to make a DeferredList + deferreds = [] for arg in args: if not os.path.isfile(arg): self.console.write("{!error!}This is a directory!") @@ -60,7 +64,9 @@ class Command(BaseCommand): def on_fail(result): self.console.write("{!error!}Torrent was not added! %s" % result) - client.core.add_torrent_file(filename, filedump, t_options).addCallback(on_success).addErrback(on_fail) + deferreds.append(client.core.add_torrent_file(filename, filedump, t_options).addCallback(on_success).addErrback(on_fail)) + + return defer.DeferredList(deferreds) def complete(self, line): line = os.path.abspath(os.path.expanduser(line)) diff --git a/deluge/ui/console/commands/config.py b/deluge/ui/console/commands/config.py index 962bf9c14..8b9bd8def 100644 --- a/deluge/ui/console/commands/config.py +++ b/deluge/ui/console/commands/config.py @@ -23,6 +23,8 @@ # Boston, MA 02110-1301, USA. # +from twisted.internet import defer + from deluge.ui.console.main import BaseCommand import deluge.ui.console.colors as colors from deluge.ui.client import client @@ -63,7 +65,7 @@ def simple_eval(source): taken from http://effbot.org/zone/simple-iterator-parser.htm""" src = cStringIO.StringIO(source).readline src = tokenize.generate_tokens(src) - src = (token for token in src if token[0] is not tokenize.NL) + src = (token for token in src if token[0] is not tokenize.NL37) res = atom(src.next, src.next()) if src.next()[0] is not tokenize.ENDMARKER: raise SyntaxError("bogus data after expression") @@ -83,38 +85,47 @@ class Command(BaseCommand): def handle(self, *args, **options): self.console = component.get("ConsoleUI") if options['set']: - self._set_config(*args, **options) + return self._set_config(*args, **options) else: - self._get_config(*args, **options) + return self._get_config(*args, **options) def _get_config(self, *args, **options): - config = component.get("CoreConfig") + deferred = defer.Deferred() + def on_get_config(result): + config = component.get("CoreConfig") + keys = config.keys() + keys.sort() + s = "" + for key in keys: + if args and key not in args: + continue + color = "{!white,black,bold!}" + value = config[key] + if type(value) in colors.type_color: + color = colors.type_color[type(value)] - keys = config.keys() - keys.sort() - s = "" - for key in keys: - if args and key not in args: - continue - color = "{!white,black,bold!}" - value = config[key] - if type(value) in colors.type_color: - color = colors.type_color[type(value)] + # We need to format dicts for printing + if isinstance(value, dict): + import pprint + value = pprint.pformat(value, 2, 80) + new_value = [] + for line in value.splitlines(): + new_value.append("%s%s" % (color, line)) + value = "\n".join(new_value) - # We need to format dicts for printing - if isinstance(value, dict): - import pprint - value = pprint.pformat(value, 2, 80) - new_value = [] - for line in value.splitlines(): - new_value.append("%s%s" % (color, line)) - value = "\n".join(new_value) + s += " %s: %s%s\n" % (key, color, value) - s += " %s: %s%s\n" % (key, color, value) + self.console.write(s) + deferred.callback(True) + return config - self.console.write(s) + # We need to ensure the config dict has been received first + component.get("CoreConfig").start_defer.addCallback(on_get_config) + + return deferred def _set_config(self, *args, **options): + deferred = defer.Deferred() config = component.get("CoreConfig") key = args[0] if key not in config.keys(): @@ -132,7 +143,10 @@ class Command(BaseCommand): def on_set_config(result): self.console.write("{!success!}Configuration value successfully updated.") + deferred.callback(True) + client.core.set_config({key: val}).addCallback(on_set_config) + return deferred def complete(self, text): return [ k for k in component.get("CoreConfig").keys() if k.startswith(text) ] diff --git a/deluge/ui/console/commands/connect.py b/deluge/ui/console/commands/connect.py index 62a7d7e08..4ab8e167f 100644 --- a/deluge/ui/console/commands/connect.py +++ b/deluge/ui/console/commands/connect.py @@ -42,3 +42,4 @@ class Command(BaseCommand): d.addCallback(on_connect) d.addErrback(on_connect_fail) + return d diff --git a/deluge/ui/console/commands/debug.py b/deluge/ui/console/commands/debug.py index 6719bef38..97bf55209 100644 --- a/deluge/ui/console/commands/debug.py +++ b/deluge/ui/console/commands/debug.py @@ -22,6 +22,9 @@ # 51 Franklin Street, Fifth Floor # Boston, MA 02110-1301, USA. # + +from twisted.internet import defer + from deluge.ui.console.main import BaseCommand from deluge.ui.client import client import deluge.ui.console.colors as colors @@ -39,5 +42,7 @@ class Command(BaseCommand): else: component.get("ConsoleUI").write("{!error!}%s" % usage) + return defer.succeed(True) + def complete(self, text): return [x for x in ['on', 'off'] if x.startswith(text)] diff --git a/deluge/ui/console/commands/halt.py b/deluge/ui/console/commands/halt.py index 250d752ca..ef188b580 100644 --- a/deluge/ui/console/commands/halt.py +++ b/deluge/ui/console/commands/halt.py @@ -38,4 +38,4 @@ class Command(BaseCommand): def on_shutdown_fail(reason): self.write("{!error!}Unable to shutdown daemon: %s" % reason) - client.daemon.shutdown().addCallback(on_shutdown).addErrback(on_shutdown_fail) + return client.daemon.shutdown().addCallback(on_shutdown).addErrback(on_shutdown_fail) diff --git a/deluge/ui/console/commands/help.py b/deluge/ui/console/commands/help.py index 96b3cde7c..75dcaea7f 100644 --- a/deluge/ui/console/commands/help.py +++ b/deluge/ui/console/commands/help.py @@ -1,4 +1,3 @@ -# # help.py # # Copyright (C) 2008-2009 Ido Abramovich @@ -22,6 +21,9 @@ # 51 Franklin Street, Fifth Floor # Boston, MA 02110-1301, USA. # + +from twisted.internet import defer + from deluge.ui.console.main import BaseCommand import deluge.ui.console.colors as colors import deluge.component as component @@ -34,15 +36,16 @@ class Command(BaseCommand): def handle(self, *args, **options): self.console = component.get("ConsoleUI") self._commands = self.console._commands + deferred = defer.succeed(True) if args: if len(args) > 1: self.console.write(usage) - return + return deferred try: cmd = self._commands[args[0]] except KeyError: self.console.write("{!error!}Unknown command %r" % args[0]) - return + return deferred try: parser = cmd.create_parser() self.console.write(parser.format_help()) @@ -55,5 +58,7 @@ class Command(BaseCommand): self.console.write(" ") self.console.write('For help on a specific command, use " --help"') + return deferred + def complete(self, line): return [x for x in component.get("ConsoleUI")._commands if x.startswith(line)] diff --git a/deluge/ui/console/commands/info.py b/deluge/ui/console/commands/info.py index 3d1d9810d..6d8b29654 100644 --- a/deluge/ui/console/commands/info.py +++ b/deluge/ui/console/commands/info.py @@ -110,6 +110,7 @@ class Command(BaseCommand): d = client.core.get_torrents_status({"id": torrent_ids}, status_keys) d.addCallback(on_torrents_status) d.addErrback(on_torrents_status_fail) + return d def show_info(self, torrent_id, status, verbose=False): """ diff --git a/deluge/ui/console/commands/pause.py b/deluge/ui/console/commands/pause.py index 8a06f6392..f2751acce 100644 --- a/deluge/ui/console/commands/pause.py +++ b/deluge/ui/console/commands/pause.py @@ -45,7 +45,7 @@ class Command(BaseCommand): torrent_ids.extend(self.console.match_torrent(arg)) if torrent_ids: - client.core.pause_torrent(torrent_ids) + return client.core.pause_torrent(torrent_ids) def complete(self, line): # We use the ConsoleUI torrent tab complete method diff --git a/deluge/ui/console/commands/quit.py b/deluge/ui/console/commands/quit.py index b11bbdd0b..a82786bee 100644 --- a/deluge/ui/console/commands/quit.py +++ b/deluge/ui/console/commands/quit.py @@ -33,6 +33,6 @@ class Command(BaseCommand): if client.connected(): def on_disconnect(result): reactor.stop() - client.disconnect().addCallback(on_disconnect) + return client.disconnect().addCallback(on_disconnect) else: reactor.stop() diff --git a/deluge/ui/console/commands/resume.py b/deluge/ui/console/commands/resume.py index c6863fa8d..d6bf1a166 100644 --- a/deluge/ui/console/commands/resume.py +++ b/deluge/ui/console/commands/resume.py @@ -22,6 +22,7 @@ # 51 Franklin Street, Fifth Floor # Boston, MA 02110-1301, USA. # + from deluge.ui.console.main import BaseCommand from deluge.ui.client import client import deluge.ui.console.colors as colors @@ -45,7 +46,7 @@ class Command(BaseCommand): torrent_ids.extend(self.console.match_torrent(arg)) if torrent_ids: - client.core.resume_torrent(torrent_ids) + return client.core.resume_torrent(torrent_ids) def complete(self, line): # We use the ConsoleUI torrent tab complete method diff --git a/deluge/ui/console/commands/rm.py b/deluge/ui/console/commands/rm.py index c796309d2..528798a33 100644 --- a/deluge/ui/console/commands/rm.py +++ b/deluge/ui/console/commands/rm.py @@ -49,7 +49,7 @@ class Command(BaseCommand): for arg in args: torrent_ids.extend(self.console.match_torrent(arg)) - client.core.remove_torrent(torrent_ids, options['remove_data']) + return client.core.remove_torrent(torrent_ids, options['remove_data']) def complete(self, line): # We use the ConsoleUI torrent tab complete method diff --git a/deluge/ui/console/main.py b/deluge/ui/console/main.py index 6df200800..36678e415 100644 --- a/deluge/ui/console/main.py +++ b/deluge/ui/console/main.py @@ -36,9 +36,30 @@ import deluge.common from deluge.ui.coreconfig import CoreConfig from deluge.ui.console.statusbars import StatusBars from deluge.ui.console.eventlog import EventLog -import deluge.ui.console.screen as screen -import deluge.ui.console.colors as colors +import screen +import colors from deluge.log import LOG as log +from deluge.ui.ui import _UI + +class Console(_UI): + + help = """Starts the Deluge console interface""" + + def __init__(self): + super(Console, self).__init__("console") + cmds = load_commands(os.path.join(UI_PATH, 'commands')) + + group = optparse.OptionGroup(self.parser, "Console Commands", + "\n".join(cmds.keys())) + self.parser.add_option_group(group) + + def start(self): + super(Console, self).start() + + ConsoleUI(self.args) + +def start(): + Console().start() class OptionParser(optparse.OptionParser): """subclass from optparse.OptionParser so exit() won't exit.""" @@ -108,27 +129,42 @@ class ConsoleUI(component.Component): # Load all the commands self._commands = load_commands(os.path.join(UI_PATH, 'commands')) - # Try to connect to the localhost daemon - def on_connect(result): - component.start() - client.connect().addCallback(on_connect) - # Set the interactive flag to indicate where we should print the output self.interactive = True if args: + args = args[0] self.interactive = False - # If we have args, lets process them and quit - #allow multiple commands split by ";" - for arg in args.split(";"): - self.do_command(arg) - sys.exit(0) + + # Try to connect to the localhost daemon + def on_connect(result): + component.start() + if not self.interactive: + def on_started(result): + deferreds = [] + # If we have args, lets process them and quit + # allow multiple commands split by ";" + for arg in args.split(";"): + deferreds.append(self.do_command(arg.strip())) + + def on_complete(result): + self.do_command("quit") + + dl = defer.DeferredList(deferreds).addCallback(on_complete) + + # We need to wait for the rpcs in start() to finish before processing + # any of the commands. + self.started_deferred.addCallback(on_started) + + client.connect().addCallback(on_connect) self.coreconfig = CoreConfig() - - # We use the curses.wrapper function to prevent the console from getting - # messed up if an uncaught exception is experienced. - import curses.wrapper - curses.wrapper(self.run) + if self.interactive: + # We use the curses.wrapper function to prevent the console from getting + # messed up if an uncaught exception is experienced. + import curses.wrapper + curses.wrapper(self.run) + else: + reactor.run() def run(self, stdscr): """ @@ -157,12 +193,16 @@ class ConsoleUI(component.Component): reactor.run() def start(self): + # This gets fired once we have received the torrents list from the core + self.started_deferred = defer.Deferred() + # Maintain a list of (torrent_id, name) for use in tab completion self.torrents = [] def on_session_state(result): def on_torrents_status(torrents): for torrent_id, status in torrents.items(): self.torrents.append((torrent_id, status["name"])) + self.started_deferred.callback(True) client.core.get_torrents_status({"id": result}, ["name"]).addCallback(on_torrents_status) client.core.get_session_state().addCallback(on_session_state) @@ -184,7 +224,7 @@ class ConsoleUI(component.Component): if self.interactive: self.screen.add_line(line) else: - print(line) + print(colors.strip_colors(line)) def do_command(self, cmd): """ @@ -215,12 +255,15 @@ class ConsoleUI(component.Component): options, args = parser.parse_args(args) if not getattr(options, '_exit', False): try: - self._commands[cmd].handle(*args, **options.__dict__) + ret = self._commands[cmd].handle(*args, **options.__dict__) except Exception, e: self.write("{!error!}" + str(e)) log.exception(e) import traceback self.write("%s" % traceback.format_exc()) + return defer.succeed(True) + else: + return ret def tab_completer(self, line, cursor, second_hit): """