diff --git a/ChangeLog b/ChangeLog index 83d3bfa1c..54111bc71 100644 --- a/ChangeLog +++ b/ChangeLog @@ -10,7 +10,6 @@ * #1247: Fix deluge-gtk from hanging on shutdown * #995: Rewrote tracker_icons * Make the distinction between adding to the session new unmanaged torrents and torrents loaded from state. This will break backwards compatability. - * Pass a copy of an event instead of passing the event arguments to the event handlers. This will break backwards compatability. ==== GtkUI ==== * Fix uncaught exception when closing deluge in classic mode diff --git a/deluge/core/eventmanager.py b/deluge/core/eventmanager.py index 1199813d8..198507007 100644 --- a/deluge/core/eventmanager.py +++ b/deluge/core/eventmanager.py @@ -55,7 +55,7 @@ class EventManager(component.Component): if event.name in self.handlers: for handler in self.handlers[event.name]: #log.debug("Running handler %s for event %s with args: %s", event.name, handler, event.args) - handler(event.copy()) + handler(*event.args) def register_event_handler(self, event, handler): """ diff --git a/deluge/core/rpcserver.py b/deluge/core/rpcserver.py index 2858b3b30..ef2e3342f 100644 --- a/deluge/core/rpcserver.py +++ b/deluge/core/rpcserver.py @@ -486,8 +486,7 @@ class RPCServer(component.Component): # Find sessions interested in this event for session_id, interest in self.factory.interested_events.iteritems(): if event.name in interest: - log.debug("Emit Event: %s %s", event.name, zip(event.__slots__, - event.args)) + log.debug("Emit Event: %s %s", event.name, event.args) # This session is interested so send a RPC_EVENT self.factory.session_protocols[session_id].sendData( (RPC_EVENT, event.name, event.args) diff --git a/deluge/event.py b/deluge/event.py index a4fc8b4a3..14e13940b 100644 --- a/deluge/event.py +++ b/deluge/event.py @@ -2,7 +2,6 @@ # event.py # # Copyright (C) 2009 Andrew Resch -# Copyright (C) 2010 Pedro Algarvio # # Deluge is free software. # @@ -48,8 +47,6 @@ class DelugeEventMetaClass(type): """ This metaclass simply keeps a list of all events classes created. """ - __slots__ = () - def __init__(cls, name, bases, dct): super(DelugeEventMetaClass, cls).__init__(name, bases, dct) if name != "DelugeEvent": @@ -65,26 +62,23 @@ class DelugeEvent(object): :type args: list """ - __slots__ = () __metaclass__ = DelugeEventMetaClass def _get_name(self): return self.__class__.__name__ - name = property(fget=_get_name) def _get_args(self): - return [getattr(self, arg) for arg in self.__slots__] - args = property(fget=_get_args) + if not hasattr(self, "_args"): + return [] + return self._args - def copy(self): - return self.__class__(*self.args) + name = property(fget=_get_name) + args = property(fget=_get_args) class TorrentAddedEvent(DelugeEvent): """ Emitted when a new torrent is successfully added to the session. """ - __slots__ = ('torrent_id', 'from_state') - def __init__(self, torrent_id, from_state): """ :param torrent_id: the torrent_id of the torrent that was added @@ -92,41 +86,34 @@ class TorrentAddedEvent(DelugeEvent): :param from_state: was the torrent loaded from state? Or is it a new torrent. :type from_state: bool """ - self.torrent_id = torrent_id - self.from_state = from_state + self._args = [torrent_id, from_state] class TorrentRemovedEvent(DelugeEvent): """ Emitted when a torrent has been removed from the session. """ - __slots__ = ('torrent_id',) - def __init__(self, torrent_id): """ :param torrent_id: the torrent_id :type torrent_id: string """ - self.torrent_id = torrent_id + self._args = [torrent_id] class PreTorrentRemovedEvent(DelugeEvent): """ Emitted when a torrent is about to be removed from the session. """ - __slots__ = ('torrent_id',) - def __init__(self, torrent_id): """ :param torrent_id: the torrent_id :type torrent_id: string """ - self.torrent_id = torrent_id + self._args = [torrent_id] class TorrentStateChangedEvent(DelugeEvent): """ Emitted when a torrent changes state. """ - __slots__ = ('torrent_id', 'state') - def __init__(self, torrent_id, state): """ :param torrent_id: the torrent_id @@ -134,20 +121,18 @@ class TorrentStateChangedEvent(DelugeEvent): :param state: the new state :type state: string """ - self.torrent_id = torrent_id - self.state = state + self._args = [torrent_id, state] class TorrentQueueChangedEvent(DelugeEvent): """ Emitted when the queue order has changed. """ + pass class TorrentFolderRenamedEvent(DelugeEvent): """ Emitted when a folder within a torrent has been renamed. """ - __slots__ = ('torrent_id', 'old', 'new') - def __init__(self, torrent_id, old, new): """ :param torrent_id: the torrent_id @@ -157,54 +142,44 @@ class TorrentFolderRenamedEvent(DelugeEvent): :param new: the new folder name :type new: string """ - self.torrent_id = torrent_id - self.old = old - self.new = new + self._args = [torrent_id, old, new] class TorrentFileRenamedEvent(DelugeEvent): """ Emitted when a file within a torrent has been renamed. """ - __slots__ = ('torrent_id', 'index', 'filename') - - def __init__(self, torrent_id, index, filename): + def __init__(self, torrent_id, index, name): """ :param torrent_id: the torrent_id :type torrent_id: string :param index: the index of the file :type index: int - :param filename: the new filename - :type filename: string + :param name: the new filename + :type name: string """ - self.torrent_id = torrent_id - self.index = index - self.filename = filename + self._args = [torrent_id, index, name] class TorrentFinishedEvent(DelugeEvent): """ Emitted when a torrent finishes downloading. """ - __slots__ = ('torrent_id',) - def __init__(self, torrent_id): """ :param torrent_id: the torrent_id :type torrent_id: string """ - self.torrent_id = torrent_id + self._args = [torrent_id] class TorrentResumedEvent(DelugeEvent): """ Emitted when a torrent resumes from a paused state. """ - __slots__ = ('torrent_id',) - def __init__(self, torrent_id): """ :param torrent_id: the torrent_id :type torrent_id: string """ - self.torrent_id = torrent_id + self._args = [torrent_id] class TorrentFileCompletedEvent(DelugeEvent): """ @@ -213,8 +188,6 @@ class TorrentFileCompletedEvent(DelugeEvent): This will only work with libtorrent 0.15 or greater. """ - __slots__ = ('torrent_id', 'index') - def __init__(self, torrent_id, index): """ :param torrent_id: the torrent_id @@ -222,75 +195,60 @@ class TorrentFileCompletedEvent(DelugeEvent): :param index: the file index :type index: int """ - self.torrent_id = torrent_id - self.index = index + self._args = [torrent_id, index] class NewVersionAvailableEvent(DelugeEvent): """ Emitted when a more recent version of Deluge is available. """ - __slots__ = ('new_release',) - def __init__(self, new_release): """ :param new_release: the new version that is available :type new_release: string """ - self.new_release = new_release + self._args = [new_release] class SessionStartedEvent(DelugeEvent): """ Emitted when a session has started. This typically only happens once when the daemon is initially started. """ + pass class SessionPausedEvent(DelugeEvent): """ Emitted when the session has been paused. """ + pass class SessionResumedEvent(DelugeEvent): """ Emitted when the session has been resumed. """ + pass class ConfigValueChangedEvent(DelugeEvent): """ Emitted when a config value changes in the Core. """ - __slots__ = ('key', 'value') - def __init__(self, key, value): """ :param key: the key that changed :type key: string :param value: the new value of the `:param:key` """ - self.key = key - self.value = value + self._args = [key, value] class PluginEnabledEvent(DelugeEvent): """ Emitted when a plugin is enabled in the Core. """ - __slots__ = ('plugin_name',) - - def __init__(self, plugin_name): - """ - :param plugin_name: the plugin name - :type plugin_name: string - """ - self.plugin_name = plugin_name + def __init__(self, name): + self._args = [name] class PluginDisabledEvent(DelugeEvent): """ Emitted when a plugin is disabled in the Core. """ - __slots__ = ('plugin_name',) - - def __init__(self, plugin_name): - """ - :param plugin_name: the plugin name - :type plugin_name: string - """ - self.plugin_name = plugin_name + def __init__(self, name): + self._args = [name] diff --git a/deluge/ui/client.py b/deluge/ui/client.py index 9852b2998..1a45e7d19 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -45,7 +45,7 @@ import zlib import deluge.common import deluge.component as component -from deluge.event import known_events +from deluge.log import LOG as log if deluge.common.windows_check(): import win32api @@ -166,14 +166,13 @@ class DelugeRPCProtocol(Protocol): message_type = request[0] if message_type == RPC_EVENT: - event_name = request[1] + event = request[1] #log.debug("Received RPCEvent: %s", event) # A RPCEvent was received from the daemon so run any handlers # associated with it. - if event_name in self.factory.event_handlers: - event = known_events[event_name](*request[2]) - for handler in self.factory.event_handlers[event_name]: - reactor.callLater(0, handler, event.copy()) + if event in self.factory.event_handlers: + for handler in self.factory.event_handlers[event]: + reactor.callLater(0, handler, *request[2]) continue request_id = request[1] diff --git a/deluge/ui/console/eventlog.py b/deluge/ui/console/eventlog.py index 351186dd8..162bb3ff2 100644 --- a/deluge/ui/console/eventlog.py +++ b/deluge/ui/console/eventlog.py @@ -62,53 +62,53 @@ class EventLog(component.Component): client.register_event_handler("PluginEnabledEvent", self.on_plugin_enabled_event) client.register_event_handler("PluginDisabledEvent", self.on_plugin_disabled_event) - def on_torrent_added_event(self, event): + def on_torrent_added_event(self, torrent_id, from_state): def on_torrent_status(status): self.console.write(self.prefix + "TorrentAdded(from_state=%s): {!info!}%s (%s)" % ( - event.from_state, status["name"], event.torrent_id) + from_state, status["name"], torrent_id) ) - client.core.get_torrent_status(event.torrent_id, ["name"]).addCallback(on_torrent_status) + client.core.get_torrent_status(torrent_id, ["name"]).addCallback(on_torrent_status) - def on_torrent_removed_event(self, event): + def on_torrent_removed_event(self, torrent_id): self.console.write(self.prefix + "TorrentRemoved: {!info!}%s (%s)" % - (self.console.get_torrent_name(event.torrent_id), event.torrent_id)) + (self.console.get_torrent_name(torrent_id), torrent_id)) - def on_torrent_state_changed_event(self, event): + def on_torrent_state_changed_event(self, torrent_id, state): # Modify the state string color - if event.state in colors.state_color: - state = colors.state_color[event.state] + event.state + if state in colors.state_color: + state = colors.state_color[state] + state self.console.write(self.prefix + "TorrentStateChanged: %s {!info!}%s (%s)" % - (state, self.console.get_torrent_name(event.torrent_id), event.torrent_id)) + (state, self.console.get_torrent_name(torrent_id), torrent_id)) - def on_torrent_paused_event(self, event): + def on_torrent_paused_event(self, torrent_id): self.console.write(self.prefix + "TorrentPaused: {!info!}%s (%s)" % - (self.console.get_torrent_name(event.torrent_id), event.torrent_id)) + (self.console.get_torrent_name(torrent_id), torrent_id)) - def on_torrent_finished_event(self, event): + def on_torrent_finished_event(self, torrent_id): self.console.write(self.prefix + "TorrentFinished: {!info!}%s (%s)" % - (self.console.get_torrent_name(event.torrent_id), event.torrent_id)) + (self.console.get_torrent_name(torrent_id), torrent_id)) - def on_new_version_available_event(self, event): + def on_new_version_available_event(self, version): self.console.write(self.prefix + "NewVersionAvailable: {!info!}%s" % - (event.new_release)) + (version)) - def on_session_paused_event(self, event): + def on_session_paused_event(self): self.console.write(self.prefix + "SessionPaused") - def on_session_resumed_event(self, event): + def on_session_resumed_event(self): self.console.write(self.prefix + "SessionResumed") - def on_config_value_changed_event(self, event): + def on_config_value_changed_event(self, key, value): color = "{!white,black,bold!}" - if type(event.value) in colors.type_color: - color = colors.type_color[type(event.value)] + if type(value) in colors.type_color: + color = colors.type_color[type(value)] self.console.write(self.prefix + "ConfigValueChanged: {!input!}%s: %s%s" % - (event.key, color, event.value)) + (key, color, value)) - def on_plugin_enabled_event(self, event): - self.console.write(self.prefix + "PluginEnabled: {!info!}%s" % event.plugin_name) + def on_plugin_enabled_event(self, name): + self.console.write(self.prefix + "PluginEnabled: {!info!}%s" % name) - def on_plugin_disabled_event(self, event): - self.console.write(self.prefix + "PluginDisabled: {!info!}%s" % event.plugin_name) + def on_plugin_disabled_event(self, name): + self.console.write(self.prefix + "PluginDisabled: {!info!}%s" % name) diff --git a/deluge/ui/console/main.py b/deluge/ui/console/main.py index 8114dbb8a..7d5e1a91a 100644 --- a/deluge/ui/console/main.py +++ b/deluge/ui/console/main.py @@ -34,28 +34,24 @@ # # -import os -import sys -import logging +import os, sys import optparse import shlex import locale from twisted.internet import defer, reactor +from deluge.ui.console import UI_PATH import deluge.component as component from deluge.ui.client import client import deluge.common from deluge.ui.coreconfig import CoreConfig -from deluge.ui.sessionproxy import SessionProxy from deluge.ui.console.statusbars import StatusBars from deluge.ui.console.eventlog import EventLog -#import screen +import screen import colors +from deluge.log import LOG as log from deluge.ui.ui import _UI -from deluge.ui.console import UI_PATH - -log = logging.getLogger(__name__) class Console(_UI): @@ -63,62 +59,16 @@ class Console(_UI): def __init__(self): super(Console, self).__init__("console") - group = optparse.OptionGroup(self.parser, "Console Options","These options control how " - "the console connects to the daemon. These options will be " - "used if you pass a command, or if you have autoconnect " - "enabled for the console ui.") + cmds = load_commands(os.path.join(UI_PATH, 'commands')) - group.add_option("-d","--daemon",dest="daemon_addr", - action="store",type="str",default="127.0.0.1", - help="Set the address of the daemon to connect to." - " [default: %default]") - group.add_option("-p","--port",dest="daemon_port", - help="Set the port to connect to the daemon on. [default: %default]", - action="store",type="int",default=58846) - group.add_option("-u","--username",dest="daemon_user", - help="Set the username to connect to the daemon with. [default: %default]", - action="store",type="string") - group.add_option("-P","--password",dest="daemon_pass", - help="Set the password to connect to the daemon with. [default: %default]", - action="store",type="string") + group = optparse.OptionGroup(self.parser, "Console Commands", + "\n".join(cmds.keys())) self.parser.add_option_group(group) - self.cmds = load_commands(os.path.join(UI_PATH, 'commands')) - class CommandOptionGroup(optparse.OptionGroup): - def __init__(self, parser, title, description=None, cmds = None): - optparse.OptionGroup.__init__(self,parser,title,description) - self.cmds = cmds - - def format_help(self, formatter): - result = formatter.format_heading(self.title) - formatter.indent() - if self.description: - result += "%s\n"%formatter.format_description(self.description) - for cname in self.cmds: - cmd = self.cmds[cname] - if cmd.interactive_only or cname in cmd.aliases: continue - allnames = [cname] - allnames.extend(cmd.aliases) - cname = "/".join(allnames) - result += formatter.format_heading(" - ".join([cname,cmd.__doc__])) - formatter.indent() - result += "%*s%s\n" % (formatter.current_indent, "", cmd.usage) - formatter.dedent() - formatter.dedent() - return result - cmd_group = CommandOptionGroup(self.parser, "Console Commands", - description="The following commands can be issued at the " - "command line. Commands should be quoted, so, for example, " - "to pause torrent with id 'abc' you would run: '%s " - "\"pause abc\"'"%os.path.basename(sys.argv[0]), - cmds=self.cmds) - self.parser.add_option_group(cmd_group) - def start(self): super(Console, self).start() - ConsoleUI(self.args,self.cmds,(self.options.daemon_addr, - self.options.daemon_port,self.options.daemon_user, - self.options.daemon_pass)) + + ConsoleUI(self.args) def start(): Console().start() @@ -139,11 +89,9 @@ class OptionParser(optparse.OptionParser): """ raise Exception(msg) - class BaseCommand(object): usage = 'usage' - interactive_only = False option_list = tuple() aliases = [] @@ -171,7 +119,6 @@ class BaseCommand(object): epilog = self.epilog, option_list = self.option_list) - def load_commands(command_dir, exclude=[]): def get_command(name): return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, {}, ['Command']), 'Command')() @@ -192,13 +139,11 @@ def load_commands(command_dir, exclude=[]): except OSError, e: return {} - class ConsoleUI(component.Component): - def __init__(self, args=None, cmds = None, daemon = None): + def __init__(self, args=None): component.Component.__init__(self, "ConsoleUI", 2) - # keep track of events for the log view - self.events = [] + self.batch_write = False try: locale.setlocale(locale.LC_ALL, '') @@ -207,10 +152,8 @@ class ConsoleUI(component.Component): self.encoding = sys.getdefaultencoding() log.debug("Using encoding: %s", self.encoding) - - - # start up the session proxy - self.sessionproxy = SessionProxy() + # Load all the commands + self._commands = load_commands(os.path.join(UI_PATH, 'commands')) client.set_disconnect_callback(self.on_client_disconnect) @@ -219,18 +162,30 @@ class ConsoleUI(component.Component): if args: args = args[0] self.interactive = False - if not cmds: - print "Sorry, couldn't find any commands" - return - else: - self._commands = cmds - from commander import Commander - cmdr = Commander(cmds) - if daemon: - cmdr.exec_args(args,*daemon) - else: - cmdr.exec_args(args,None,None,None,None) - + + # Try to connect to the localhost daemon + def on_connect(result): + def on_started(result): + 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(defer.maybeDeferred(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) + component.start().addCallback(on_started) + + d = client.connect() + d.addCallback(on_connect) self.coreconfig = CoreConfig() if self.interactive and not deluge.common.windows_check(): @@ -239,13 +194,8 @@ class ConsoleUI(component.Component): import curses.wrapper curses.wrapper(self.run) elif self.interactive and deluge.common.windows_check(): - print """\nDeluge-console does not run in interactive mode on Windows. \n -Please use commands from the command line, eg:\n - deluge-console.exe help - deluge-console.exe info - deluge-console.exe "add --help" - deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent" - """ + print "You cannot run the deluge-console in interactive mode in Windows.\ + Please use commands from the command line, eg: deluge-console config;help;exit" else: reactor.run() @@ -260,9 +210,8 @@ Please use commands from the command line, eg:\n # We want to do an interactive session, so start up the curses screen and # pass it the function that handles commands colors.init_colors() + self.screen = screen.Screen(stdscr, self.do_command, self.tab_completer, self.encoding) self.statusbars = StatusBars() - from modes.connectionmanager import ConnectionManager - self.screen = ConnectionManager(stdscr, self.encoding) self.eventlog = EventLog() self.screen.topbar = "{!status!}Deluge " + deluge.common.get_version() + " Console" @@ -276,22 +225,202 @@ Please use commands from the command line, eg:\n # Start the twisted mainloop 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 = [] - if not self.interactive: - self.started_deferred = defer.Deferred() - 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) + 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) + client.core.get_torrents_status({"id": result}, ["name"]).addCallback(on_torrents_status) + client.core.get_session_state().addCallback(on_session_state) + + # Register some event handlers to keep the torrent list up-to-date + client.register_event_handler("TorrentAddedEvent", self.on_torrent_added_event) + client.register_event_handler("TorrentRemovedEvent", self.on_torrent_removed_event) + + def update(self): + pass + + def set_batch_write(self, batch): + """ + When this is set the screen is not refreshed after a `:meth:write` until + this is set to False. + + :param batch: set True to prevent screen refreshes after a `:meth:write` + :type batch: bool + + """ + self.batch_write = batch + if not batch and self.interactive: + self.screen.refresh() + + def write(self, line): + """ + Writes a line out depending on if we're in interactive mode or not. + + :param line: str, the line to print + + """ + if self.interactive: + self.screen.add_line(line, not self.batch_write) + else: + print(colors.strip_colors(line)) + + def do_command(self, cmd): + """ + Processes a command. + + :param cmd: str, the command string + + """ + if not cmd: + return + cmd, _, line = cmd.partition(' ') + try: + parser = self._commands[cmd].create_parser() + except KeyError: + self.write("{!error!}Unknown command: %s" % cmd) + return + args = self._commands[cmd].split(line) + + # Do a little hack here to print 'command --help' properly + parser._print_help = parser.print_help + def print_help(f=None): + if self.interactive: + self.write(parser.format_help()) + else: + parser._print_help(f) + parser.print_help = print_help + + # Only these commands can be run when not connected to a daemon + not_connected_cmds = ["help", "connect", "quit"] + aliases = [] + for c in not_connected_cmds: + aliases.extend(self._commands[c].aliases) + not_connected_cmds.extend(aliases) + + if not client.connected() and cmd not in not_connected_cmds: + self.write("{!error!}Not connected to a daemon, please use the connect command first.") + return + + try: + options, args = parser.parse_args(args) + except Exception, e: + self.write("{!error!}Error parsing options: %s" % e) + return + + if not getattr(options, '_exit', False): + try: + 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): + """ + Called when the user hits 'tab' and will autocomplete or show options. + If a command is already supplied in the line, this function will call the + complete method of the command. + + :param line: str, the current input string + :param cursor: int, the cursor position in the line + :param second_hit: bool, if this is the second time in a row the tab key + has been pressed + + :returns: 2-tuple (string, cursor position) + + """ + # First check to see if there is no space, this will mean that it's a + # command that needs to be completed. + if " " not in line: + possible_matches = [] + # Iterate through the commands looking for ones that startwith the + # line. + for cmd in self._commands: + if cmd.startswith(line): + possible_matches.append(cmd + " ") + + line_prefix = "" + else: + cmd = line.split(" ")[0] + if cmd in self._commands: + # Call the command's complete method to get 'er done + possible_matches = self._commands[cmd].complete(line.split(" ")[-1]) + line_prefix = " ".join(line.split(" ")[:-1]) + " " + else: + # This is a bogus command + return (line, cursor) + + # No matches, so just return what we got passed + if len(possible_matches) == 0: + return (line, cursor) + # If we only have 1 possible match, then just modify the line and + # return it, else we need to print out the matches without modifying + # the line. + elif len(possible_matches) == 1: + new_line = line_prefix + possible_matches[0] + return (new_line, len(new_line)) + else: + if second_hit: + # Only print these out if it's a second_hit + self.write(" ") + for match in possible_matches: + self.write(match) + else: + p = " ".join(line.split(" ")[:-1]) + new_line = " ".join([p, os.path.commonprefix(possible_matches)]) + if len(new_line) > len(line): + line = new_line + cursor = len(line) + return (line, cursor) + + def tab_complete_torrent(self, line): + """ + Completes torrent_ids or names. + + :param line: str, the string to complete + + :returns: list of matches + + """ + + possible_matches = [] + + # Find all possible matches + for torrent_id, torrent_name in self.torrents: + if torrent_id.startswith(line): + possible_matches.append(torrent_id + " ") + if torrent_name.startswith(line): + possible_matches.append(torrent_name + " ") + + return possible_matches + + def get_torrent_name(self, torrent_id): + """ + Gets a torrent name from the torrents list. + + :param torrent_id: str, the torrent_id + + :returns: the name of the torrent or None + """ + + for tid, name in self.torrents: + if torrent_id == tid: + return name + + return None - def match_torrent(self, string): """ Returns a list of torrent_id matches for the string. It will search both @@ -310,33 +439,15 @@ Please use commands from the command line, eg:\n return ret + def on_torrent_added_event(self, event): + def on_torrent_status(status): + self.torrents.append((event.torrent_id, status["name"])) + client.core.get_torrent_status(event.torrent_id, ["name"]).addCallback(on_torrent_status) - def get_torrent_name(self, torrent_id): - if self.interactive and hasattr(self.screen,"get_torrent_name"): - return self.screen.get_torrent_name(torrent_id) - - for tid, name in self.torrents: - if torrent_id == tid: - return name - - return None - - - def set_batch_write(self, batch): - # only kept for legacy reasons, don't actually do anything - pass - - def set_mode(self, mode): - reactor.removeReader(self.screen) - self.screen = mode - self.statusbars.screen = self.screen - reactor.addReader(self.screen) + def on_torrent_removed_event(self, event): + for index, (tid, name) in enumerate(self.torrents): + if event.torrent_id == tid: + del self.torrents[index] def on_client_disconnect(self): component.stop() - - def write(self, s): - if self.interactive: - self.events.append(s) - else: - print colors.strip_colors(s) diff --git a/deluge/ui/coreconfig.py b/deluge/ui/coreconfig.py index fe002e64a..6120dcfa8 100644 --- a/deluge/ui/coreconfig.py +++ b/deluge/ui/coreconfig.py @@ -45,8 +45,8 @@ class CoreConfig(component.Component): log.debug("CoreConfig init..") component.Component.__init__(self, "CoreConfig") self.config = {} - def on_configvaluechanged_event(event): - self.config[event.key] = event.value + def on_configvaluechanged_event(key, value): + self.config[key] = value client.register_event_handler("ConfigValueChangedEvent", on_configvaluechanged_event) def start(self): diff --git a/deluge/ui/gtkui/files_tab.py b/deluge/ui/gtkui/files_tab.py index b182618f7..10b15bda8 100644 --- a/deluge/ui/gtkui/files_tab.py +++ b/deluge/ui/gtkui/files_tab.py @@ -627,34 +627,34 @@ class FilesTab(Tab): def _on_filename_editing_canceled(self, renderer): self._editing_index = None - def _on_torrentfilerenamed_event(self, event): - log.debug("index: %s name: %s", event.index, event.filename) + def _on_torrentfilerenamed_event(self, torrent_id, index, name): + log.debug("index: %s name: %s", index, name) - if event.torrent_id not in self.files_list: + if torrent_id not in self.files_list: return - old_name = self.files_list[event.torrent_id][event.index]["path"] - self.files_list[event.torrent_id][event.index]["path"] = event.filename + old_name = self.files_list[torrent_id][index]["path"] + self.files_list[torrent_id][index]["path"] = name # We need to update the filename displayed if we're currently viewing # this torrents files. - if event.torrent_id == self.torrent_id: + if torrent_id == self.torrent_id: old_name_len = len(old_name.split("/")) - name_len = len(event.filename.split("/")) + name_len = len(name.split("/")) if old_name_len != name_len: # The parent path list changes depending on which way the file # is moving in the tree if old_name_len < name_len: parent_path = [o for o in old_name.split("/")[:-1]] else: - parent_path = [o for o in event.filename.split("/")[:-1]] + parent_path = [o for o in name.split("/")[:-1]] # Find the iter to the parent folder we need to add a new folder # to. def find_parent(model, path, itr, user_data): if model[itr][0] == parent_path[0] + "/": if len(parent_path) == 1: # This is the parent iter - to_create = event.filename.split("/")[len(old_name.split("/")[:-1]):-1] + to_create = name.split("/")[len(old_name.split("/")[:-1]):-1] parent_iter = itr for tc in to_create: @@ -673,8 +673,8 @@ class FilesTab(Tab): # Find the iter for the file that needs to be moved def get_file_iter(model, path, itr, user_data): - if model[itr][5] == event.index: - model[itr][0] = event.filename.split("/")[-1] + if model[itr][5] == index: + model[itr][0] = name.split("/")[-1] t = self.treestore.append( parent_iter, self.treestore.get(itr, @@ -693,7 +693,7 @@ class FilesTab(Tab): if parent_path: self.treestore.foreach(find_parent, None) else: - new_folders = event.filename.split("/")[:-1] + new_folders = name.split("/")[:-1] parent_iter = None for f in new_folders: parent_iter = self.treestore.append(parent_iter, @@ -707,8 +707,8 @@ class FilesTab(Tab): else: # This is just changing a filename without any folder changes def set_file_name(model, path, itr, user_data): - if model[itr][5] == event.index: - model[itr][0] = os.path.split(event.filename)[-1] + if model[itr][5] == index: + model[itr][0] = os.path.split(name)[-1] return True self.treestore.foreach(set_file_name, None) @@ -754,40 +754,40 @@ class FilesTab(Tab): self.treestore.remove(itr) itr = parent - def _on_torrentfolderrenamed_event(self, event): + def _on_torrentfolderrenamed_event(self, torrent_id, old_folder, new_folder): log.debug("on_torrent_folder_renamed_signal") - log.debug("old_folder: %s new_folder: %s", event.old, event.new) + log.debug("old_folder: %s new_folder: %s", old_folder, new_folder) - if event.torrent_id not in self.files_list: + if torrent_id not in self.files_list: return - if event.old[-1] != "/": - event.old += "/" - if event.new[-1] != "/": - event.new += "/" + if old_folder[-1] != "/": + old_folder += "/" + if new_folder[-1] != "/": + new_folder += "/" - for fd in self.files_list[event.torrent_id]: - if fd["path"].startswith(event.old): - fd["path"] = fd["path"].replace(event.old, event.new, 1) + for fd in self.files_list[torrent_id]: + if fd["path"].startswith(old_folder): + fd["path"] = fd["path"].replace(old_folder, new_folder, 1) - if event.torrent_id == self.torrent_id: + if torrent_id == self.torrent_id: - old_split = event.old.split("/") + old_split = old_folder.split("/") try: old_split.remove("") except: pass - new_split = event.new.split("/") + new_split = new_folder.split("/") try: new_split.remove("") except: pass - old_folder_iter = self.get_iter_at_path(event.old) + old_folder_iter = self.get_iter_at_path(old_folder) old_folder_iter_parent = self.treestore.iter_parent(old_folder_iter) - new_folder_iter = self.get_iter_at_path(event.new) + new_folder_iter = self.get_iter_at_path(new_folder) if len(new_split) == len(old_split): # These are at the same tree depth, so it's a simple rename self.treestore[old_folder_iter][0] = new_split[-1] + "/" @@ -807,9 +807,9 @@ class FilesTab(Tab): # and if so, we delete it self.remove_childless_folders(old_folder_iter_parent) - def _on_torrentremoved_event(self, event): - if event.torrent_id in self.files_list: - del self.files_list[event.torrent_id] + def _on_torrentremoved_event(self, torrent_id): + if torrent_id in self.files_list: + del self.files_list[torrent_id] def _on_drag_data_get_data(self, treeview, context, selection, target_id, etime): paths = self.listview.get_selection().get_selected_rows()[1] diff --git a/deluge/ui/gtkui/mainwindow.py b/deluge/ui/gtkui/mainwindow.py index bc582c439..12fa5a96d 100644 --- a/deluge/ui/gtkui/mainwindow.py +++ b/deluge/ui/gtkui/mainwindow.py @@ -247,11 +247,11 @@ class MainWindow(component.Component): else: self.window.set_title("Deluge") - def on_newversionavailable_event(self, event): + def on_newversionavailable_event(self, new_version): if self.config["show_new_releases"]: from deluge.ui.gtkui.new_release_dialog import NewReleaseDialog - reactor.callLater(5.0, NewReleaseDialog().show, event.new_release) + reactor.callLater(5.0, NewReleaseDialog().show, new_version) - def on_torrentfinished_event(self, event): + def on_torrentfinished_event(self, torrent_id): from deluge.ui.gtkui.notification import Notification - Notification().notify(event.torrent_id) + Notification().notify(torrent_id) diff --git a/deluge/ui/gtkui/menubar.py b/deluge/ui/gtkui/menubar.py index ce80e5463..901e6b3c6 100644 --- a/deluge/ui/gtkui/menubar.py +++ b/deluge/ui/gtkui/menubar.py @@ -230,11 +230,11 @@ class MenuBar(component.Component): return sep ### Callbacks ### - def on_torrentstatechanged_event(self, event): - if event.state == "Paused": + def on_torrentstatechanged_event(self, torrent_id, state): + if state == "Paused": self.update_menu() - def on_torrentresumed_event(self, event): + def on_torrentresumed_event(self, torrent_id): self.update_menu() def on_sessionpaused_event(self): diff --git a/deluge/ui/gtkui/pluginmanager.py b/deluge/ui/gtkui/pluginmanager.py index 8c4ffdc88..b5169977d 100644 --- a/deluge/ui/gtkui/pluginmanager.py +++ b/deluge/ui/gtkui/pluginmanager.py @@ -91,11 +91,11 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, for plugin in enabled_plugins: self.enable_plugin(plugin) - def _on_plugin_enabled_event(self, event): - self.enable_plugin(event.plugin_name) + def _on_plugin_enabled_event(self, name): + self.enable_plugin(name) - def _on_plugin_disabled_event(self, event): - self.disable_plugin(event.plugin_name) + def _on_plugin_disabled_event(self, name): + self.disable_plugin(name) ## Hook functions def run_on_show_prefs(self): diff --git a/deluge/ui/gtkui/statusbar.py b/deluge/ui/gtkui/statusbar.py index 52f8c7fb5..60202a213 100644 --- a/deluge/ui/gtkui/statusbar.py +++ b/deluge/ui/gtkui/statusbar.py @@ -288,14 +288,14 @@ class StatusBar(component.Component): client.core.get_session_status(keys).addCallback(self._on_get_session_status) client.core.get_free_space().addCallback(self._on_get_free_space) - def on_configvaluechanged_event(self, event): + def on_configvaluechanged_event(self, key, value): """ This is called when we receive a ConfigValueChangedEvent from the core. """ - if event.key in self.config_value_changed_dict.keys(): - self.config_value_changed_dict[event.key](event.value) + if key in self.config_value_changed_dict.keys(): + self.config_value_changed_dict[key](value) def _on_max_connections_global(self, max_connections): self.max_connections = max_connections diff --git a/deluge/ui/gtkui/systemtray.py b/deluge/ui/gtkui/systemtray.py index 265846c3f..c4bce4928 100644 --- a/deluge/ui/gtkui/systemtray.py +++ b/deluge/ui/gtkui/systemtray.py @@ -210,12 +210,12 @@ class SystemTray(component.Component): "payload_upload_rate", "payload_download_rate"]).addCallback(self._on_get_session_status) - def config_value_changed(self, event): + def config_value_changed(self, key, value): """This is called when we received a config_value_changed signal from the core.""" - if event.key in self.config_value_changed_dict.keys(): - self.config_value_changed_dict[event.key](event.value) + if key in self.config_value_changed_dict.keys(): + self.config_value_changed_dict[key](value) def _on_max_download_speed(self, max_download_speed): if self.max_download_speed != max_download_speed: diff --git a/deluge/ui/gtkui/torrentview.py b/deluge/ui/gtkui/torrentview.py index 4f71a8aca..659010f05 100644 --- a/deluge/ui/gtkui/torrentview.py +++ b/deluge/ui/gtkui/torrentview.py @@ -521,31 +521,31 @@ class TorrentView(listview.ListView, component.Component): def on_drag_drop(self, widget, drag_context, x, y, timestamp): widget.stop_emission("drag-drop") - def on_torrentadded_event(self, event): - self.add_row(event.torrent_id) - self.mark_dirty(event.torrent_id) + def on_torrentadded_event(self, torrent_id, from_state): + self.add_row(torrent_id) + self.mark_dirty(torrent_id) - def on_torrentremoved_event(self, event): - self.remove_row(event.torrent_id) + def on_torrentremoved_event(self, torrent_id): + self.remove_row(torrent_id) - def on_torrentstatechanged_event(self, event): + def on_torrentstatechanged_event(self, torrent_id, state): # Update the torrents state for row in self.liststore: - if not event.torrent_id == row[self.columns["torrent_id"].column_indices[0]]: + if not torrent_id == row[self.columns["torrent_id"].column_indices[0]]: continue - row[self.get_column_index(_("Progress"))[1]] = event.state + row[self.get_column_index(_("Progress"))[1]] = state - self.mark_dirty(event.torrent_id) + self.mark_dirty(torrent_id) - def on_sessionpaused_event(self, event): + def on_sessionpaused_event(self): self.mark_dirty() self.update() - def on_sessionresumed_event(self, event): + def on_sessionresumed_event(self): self.mark_dirty() self.update() - def on_torrentqueuechanged_event(self, event): + def on_torrentqueuechanged_event(self): self.mark_dirty() self.update() diff --git a/deluge/ui/sessionproxy.py b/deluge/ui/sessionproxy.py index 4fc53d85b..f14a05a7c 100644 --- a/deluge/ui/sessionproxy.py +++ b/deluge/ui/sessionproxy.py @@ -236,21 +236,21 @@ class SessionProxy(component.Component): d = client.core.get_torrents_status(filter_dict, keys, True) return d.addCallback(on_status, None, keys) - def on_torrent_state_changed(self, event): - if event.torrent_id in self.torrents: - self.torrents[event.torrent_id][1]["state"] = event.state - self.cache_times[event.torrent_id]["state"] = time.time() + def on_torrent_state_changed(self, torrent_id, state): + if torrent_id in self.torrents: + self.torrents[torrent_id][1]["state"] = state + self.cache_times[torrent_id]["state"] = time.time() - def on_torrent_added(self, event): - self.torrents[event.torrent_id] = [time.time() - self.cache_time - 1, {}] - self.cache_times[event.torrent_id] = {} + def on_torrent_added(self, torrent_id, from_state): + self.torrents[torrent_id] = [time.time() - self.cache_time - 1, {}] + self.cache_times[torrent_id] = {} def on_status(status): - self.torrents[event.torrent_id][1].update(status) + self.torrents[torrent_id][1].update(status) t = time.time() for key in status: - self.cache_times[event.torrent_id][key] = t - client.core.get_torrent_status(event.torrent_id, []).addCallback(on_status) + self.cache_times[torrent_id][key] = t + client.core.get_torrent_status(torrent_id, []).addCallback(on_status) - def on_torrent_removed(self, event): - del self.torrents[event.torrent_id] - del self.cache_times[event.torrent_id] + def on_torrent_removed(self, torrent_id): + del self.torrents[torrent_id] + del self.cache_times[torrent_id] diff --git a/deluge/ui/web/pluginmanager.py b/deluge/ui/web/pluginmanager.py index ec4c61c58..d247e0729 100644 --- a/deluge/ui/web/pluginmanager.py +++ b/deluge/ui/web/pluginmanager.py @@ -58,26 +58,26 @@ def gather_info(plugin): "debug_scripts": debug_scripts, "script_directories": directories } - + class PluginManager(PluginManagerBase, component.Component): def __init__(self): component.Component.__init__(self, "Web.PluginManager") self.config = ConfigManager("web.conf") PluginManagerBase.__init__(self, "web.conf", "deluge.plugin.web") - + client.register_event_handler("PluginEnabledEvent", self._on_plugin_enabled_event) client.register_event_handler("PluginDisabledEvent", self._on_plugin_disabled_event) - + def _on_get_enabled_plugins(self, plugins): for plugin in plugins: self.enable_plugin(plugin) + + def _on_plugin_enabled_event(self, name): + self.enable_plugin(name) - def _on_plugin_enabled_event(self, event): - self.enable_plugin(event.plugin_name) - - def _on_plugin_disabled_event(self, event): - self.disable_plugin(event.plugin_name) - + def _on_plugin_disabled_event(self, name): + self.disable_plugin(name) + def disable_plugin(self, name): # Get the plugin instance try: @@ -85,31 +85,31 @@ class PluginManager(PluginManagerBase, component.Component): except KeyError: log.info("Plugin has no web ui") return - + info = gather_info(plugin) scripts = component.get("Scripts") for script in info["scripts"]: scripts.remove_script("%s/%s" % (name.lower(), os.path.basename(script).lower())) - + for script in info["debug_scripts"]: scripts.remove_script("%s/%s" % (name.lower(), os.path.basename(script).lower()), "debug") scripts.remove_script("%s/%s" % (name.lower(), os.path.basename(script).lower()), "dev") - + super(PluginManager, self).disable_plugin(name) - + def enable_plugin(self, name): super(PluginManager, self).enable_plugin(name) - + # Get the plugin instance try: plugin = component.get("WebPlugin." + name) except KeyError: log.info("Plugin has no web ui") return - + info = gather_info(plugin) - + scripts = component.get("Scripts") for script in info["scripts"]: log.debug("adding script %s for %s", name, os.path.basename(script)) @@ -127,16 +127,16 @@ class PluginManager(PluginManagerBase, component.Component): # Update the enabled plugins from the core d = client.core.get_enabled_plugins() d.addCallback(self._on_get_enabled_plugins) - + def stop(self): """ Stop the plugin manager """ self.disable_plugins() - + def update(self): pass - + def get_plugin_resources(self, name): # Get the plugin instance try: