From 04d14df8c7e87b9ae0056f87f9e8c019b4ebb2b6 Mon Sep 17 00:00:00 2001 From: Andrew Resch Date: Sat, 20 Dec 2008 04:27:59 +0000 Subject: [PATCH] Re-org Core and start using RPCServer --- deluge/component.py | 2 +- deluge/core/alertmanager.py | 1 + deluge/core/core.py | 538 ++++++++---------- deluge/core/daemon.py | 65 ++- deluge/core/filtermanager.py | 11 +- deluge/core/rpcserver.py | 55 +- deluge/plugins/blocklist/blocklist/core.py | 26 +- deluge/plugins/blocklist/blocklist/gtkui.py | 10 +- deluge/plugins/blocklist/blocklist/webui.py | 13 +- deluge/plugins/corepluginbase.py | 13 +- deluge/plugins/label/label/core.py | 24 +- .../plugins/label/label/gtkui/label_config.py | 4 +- .../plugins/label/label/gtkui/sidebar_menu.py | 8 +- deluge/plugins/label/label/gtkui/submenu.py | 4 +- deluge/plugins/label/label/webui.py | 16 +- deluge/ui/client.py | 21 +- deluge/ui/console/commands/halt.py | 2 +- deluge/ui/gtkui/connectionmanager.py | 2 +- deluge/ui/gtkui/gtkui.py | 2 +- deluge/ui/gtkui/menubar.py | 4 +- deluge/ui/gtkui/systemtray.py | 4 +- 21 files changed, 419 insertions(+), 406 deletions(-) diff --git a/deluge/component.py b/deluge/component.py index bb3e3b399..e775e2a2d 100644 --- a/deluge/component.py +++ b/deluge/component.py @@ -32,7 +32,7 @@ COMPONENT_STATE = [ "Paused" ] -class Component: +class Component(object): def __init__(self, name, interval=1000, depend=None): # Register with the ComponentRegistry register(name, self, depend) diff --git a/deluge/core/alertmanager.py b/deluge/core/alertmanager.py index e49e96f2a..ef0d1b504 100644 --- a/deluge/core/alertmanager.py +++ b/deluge/core/alertmanager.py @@ -42,6 +42,7 @@ class AlertManager(component.Component): log.debug("AlertManager initialized..") component.Component.__init__(self, "AlertManager", interval=50) self.session = session + self.session.set_alert_mask( lt.alert.category_t.error_notification | lt.alert.category_t.port_mapping_notification | diff --git a/deluge/core/core.py b/deluge/core/core.py index 2ffce9f0e..c3ab85670 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -22,22 +22,14 @@ # Boston, MA 02110-1301, USA. # - -import gettext -import locale -import pkg_resources -import sys import glob import shutil import os import os.path -import signal -import deluge.SimpleXMLRPCServer as SimpleXMLRPCServer -from SocketServer import ThreadingMixIn -import deluge.xmlrpclib as xmlrpclib -import gobject import threading -import socket +import pkg_resources + +import gobject try: import deluge.libtorrent as lt @@ -57,7 +49,7 @@ from deluge.core.filtermanager import FilterManager from deluge.core.preferencesmanager import PreferencesManager from deluge.core.autoadd import AutoAdd from deluge.core.authmanager import AuthManager -from deluge.core.rpcserver import BasicAuthXMLRPCRequestHandler +from deluge.core.rpcserver import BasicAuthXMLRPCRequestHandler, export from deluge.log import LOG as log @@ -71,104 +63,23 @@ STATUS_KEYS = ['active_time', 'compact', 'distributed_copies', 'download_payload 'total_seeds', 'total_size', 'total_uploaded', 'total_wanted', 'tracker', 'tracker_host', 'tracker_status', 'trackers', 'upload_payload_rate'] -class Core( - ThreadingMixIn, - SimpleXMLRPCServer.SimpleXMLRPCServer, - component.Component): - def __init__(self, port): +class Core(component.Component): + def __init__(self): log.debug("Core init..") component.Component.__init__(self, "Core") - self.client_address = None - - self.prefmanager = PreferencesManager() - - # Get config - self.config = deluge.configmanager.ConfigManager("core.conf") - - if port == None: - port = self.config["daemon_port"] - - if self.config["allow_remote"]: - hostname = "" - else: - hostname = "127.0.0.1" - - # Setup the xmlrpc server - try: - log.info("Starting XMLRPC server on port %s", port) - SimpleXMLRPCServer.SimpleXMLRPCServer.__init__( - self, (hostname, port), - requestHandler=BasicAuthXMLRPCRequestHandler, - logRequests=False, allow_none=True) - except: - log.info("Daemon already running or port not available..") - sys.exit(0) - - self.register_multicall_functions() - - # Register all export_* functions - for func in dir(self): - if func.startswith("export_"): - self.register_function(getattr(self, "%s" % func), func[7:]) - - self.register_introspection_functions() - - # Initialize gettext - try: - locale.setlocale(locale.LC_ALL, '') - if hasattr(locale, "bindtextdomain"): - locale.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n")) - if hasattr(locale, "textdomain"): - locale.textdomain("deluge") - gettext.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n")) - gettext.textdomain("deluge") - gettext.install("deluge", pkg_resources.resource_filename("deluge", "i18n")) - except Exception, e: - log.error("Unable to initialize gettext/locale: %s", e) - - # Setup signals - signal.signal(signal.SIGINT, self._shutdown) - signal.signal(signal.SIGTERM, self._shutdown) - if not deluge.common.windows_check(): - signal.signal(signal.SIGHUP, self._shutdown) - else: - from win32api import SetConsoleCtrlHandler - from win32con import CTRL_CLOSE_EVENT - from win32con import CTRL_SHUTDOWN_EVENT - result = 0 - def win_handler(ctrl_type): - log.debug("ctrl_type: %s", ctrl_type) - if ctrl_type == CTRL_CLOSE_EVENT or ctrl_type == CTRL_SHUTDOWN_EVENT: - self._shutdown() - result = 1 - return result - SetConsoleCtrlHandler(win_handler) - - def get_request(self): - """Get the request and client address from the socket. - We override this so that we can get the ip address of the client. - """ - request, client_address = self.socket.accept() - self.client_address = client_address[0] - return (request, client_address) - - def run(self): - """Starts the core""" - - # Create the client fingerprint - version = [] - for value in deluge.common.get_version().split("."): - version.append(int(value.split("-")[0])) - while len(version) < 4: - version.append(0) - fingerprint = lt.fingerprint("DE", *version) # Start the libtorrent session log.debug("Starting libtorrent session..") - self.session = lt.session(fingerprint, flags=0) + + # Create the client fingerprint + version = [int(value.split("-")[0]) for value in deluge.common.get_version().split(".")] + while len(version) < 4: + version.append(0) + + self.session = lt.session(lt.fingerprint("DE", *version), flags=0) # Load the session state if available - self.load_session_state() + self.__load_session_state() # Load the GeoIP DB for country look-ups if available geoip_db = pkg_resources.resource_filename("deluge", os.path.join("data", "GeoIP.dat")) @@ -180,105 +91,56 @@ class Core( self.settings.user_agent = "Deluge %s" % deluge.common.get_version() # Set session settings - self.settings.lazy_bitfields = 1 self.settings.send_redundant_have = True self.session.set_settings(self.settings) # Create an ip filter self.ip_filter = lt.ip_filter() + # This keeps track of the timer to set the ip filter.. We do this a few # seconds aftering adding a rule so that 'batch' adding of rules isn't slow. - self._set_ip_filter_timer = None + self.__set_ip_filter_timer = None # Load metadata extension self.session.add_extension(lt.create_metadata_plugin) self.session.add_extension(lt.create_ut_metadata_plugin) self.session.add_extension(lt.create_smart_ban_plugin) - # Start the AlertManager - self.alerts = AlertManager(self.session) - - # Start the SignalManager - self.signals = SignalManager() - - # Load plugins - self.plugins = PluginManager(self) - - # Start the TorrentManager - self.torrents = TorrentManager(self.session, self.alerts) - - # Start the FilterManager + # Create the components + self.preferencesmanager = PreferencesManager() + self.alertmanager = AlertManager(self.session) + self.signalmanager = SignalManager() + self.pluginmanager = PluginManager(self) + self.torrentmanager = TorrentManager(self.session, self.alertmanager) self.filtermanager = FilterManager(self) - - # Create the AutoAdd component self.autoadd = AutoAdd() - - # Start the AuthManager self.authmanager = AuthManager() # New release check information self.new_release = None - component.start("PreferencesManager") - component.start() + # Get the core config + self.config = deluge.configmanager.ConfigManager("core.conf") - self._should_shutdown = False + def start(self): + """Starts the core""" + # New release check information + self.__new_release = None - self.listen_thread = threading.Thread(target=self.handle_thread) - self.listen_thread.setDaemon(False) - self.listen_thread.start() - gobject.threads_init() + def stop(self): + # Save the DHT state if necessary + if self.config["dht"]: + self.__save_dht_state() + # Save the libtorrent session state + self.__save_session_state() - self.loop = gobject.MainLoop() - try: - self.loop.run() - except KeyboardInterrupt: - self._shutdown() - - def handle_thread(self): - try: - while not self._should_shutdown: - self.handle_request() - self._should_shutdown = False - - except Exception, e: - log.debug("handle_thread: %s", e) + # Make sure the config file has been saved + self.config.save() def shutdown(self): pass - def _shutdown(self, *data): - """This is called by a thread from shutdown()""" - log.info("Shutting down core..") - self._should_shutdown = True - - # Save the DHT state if necessary - if self.config["dht"]: - self.save_dht_state() - - # Save the libtorrent session state - self.save_session_state() - - # Shutdown the socket - try: - self.socket.shutdown(socket.SHUT_RDWR) - except Exception, e: - log.debug("exception in socket shutdown: %s", e) - log.debug("Joining listen thread to make sure it shutdowns cleanly..") - # Join the listen thread for a maximum of 1 second - self.listen_thread.join(1.0) - - # Start shutting down the components - component.shutdown() - - # Make sure the config file has been saved - self.config.save() - del self.config - del deluge.configmanager - del self.session - self.loop.quit() - - def save_session_state(self): + def __save_session_state(self): """Saves the libtorrent session state""" try: open(deluge.common.get_default_config_dir("session.state"), "wb").write( @@ -286,7 +148,7 @@ class Core( except Exception, e: log.warning("Failed to save lt state: %s", e) - def load_session_state(self): + def __load_session_state(self): """Loads the libtorrent session state""" try: self.session.load_state(lt.bdecode( @@ -294,7 +156,7 @@ class Core( except Exception, e: log.warning("Failed to load lt state: %s", e) - def save_dht_state(self): + def __save_dht_state(self): """Saves the dht state to a file""" try: dht_data = open(deluge.common.get_default_config_dir("dht.state"), "wb") @@ -320,32 +182,31 @@ class Core( nr = self.new_release.split("_") cv = deluge.common.get_version().split("_") if nr[0] > cv[0]: - self.signals.emit("new_version_available", self.new_release) + self.signalmanager.emit("new_version_available", self.new_release) return self.new_release return False # Exported Methods - def export_ping(self): + @export + def ping(self): """A method to see if the core is running""" return True - def export_shutdown(self): - """Shutdown the core""" - # Make shutdown an async call - gobject.idle_add(self._shutdown) - - def export_register_client(self, port): + @export + def register_client(self, port): """Registers a client with the signal manager so that signals are sent to it.""" - self.signals.register_client(self.client_address, port) + self.signalmanager.register_client(component.get("RPCServer").client_address, port) if self.config["new_release_check"]: self.check_new_release() - def export_deregister_client(self): + @export + def deregister_client(self): """De-registers a client with the signal manager.""" - self.signals.deregister_client(self.client_address) + self.signalmanager.deregister_client(component.get("RPCServer").client_address) - def export_add_torrent_file(self, filename, filedump, options): + @export + def add_torrent_file(self, filename, filedump, options): """Adds a torrent file to the libtorrent session This requires the torrents filename and a dump of it's content """ @@ -366,17 +227,17 @@ class Core( log.warning("Unable to decode torrent file: %s", e) return None - torrent_id = self.torrents.add(filedump=filedump, options=options, filename=filename) + torrent_id = self.torrentmanager.add(filedump=filedump, options=options, filename=filename) # Run the plugin hooks for 'post_torrent_add' - self.plugins.run_post_torrent_add(torrent_id) + self.pluginmanager.run_post_torrent_add(torrent_id) - - def export_get_stats(self): + @export + def get_stats(self): """ document me!!! """ - stats = self.export_get_session_status(["payload_download_rate", "payload_upload_rate", + stats = self.get_session_status(["payload_download_rate", "payload_upload_rate", "dht_nodes", "has_incoming_connections", "download_rate", "upload_rate"]) stats.update({ @@ -391,8 +252,8 @@ class Core( return stats - - def export_get_session_status(self, keys): + @export + def get_session_status(self, keys): """ Gets the session status values for 'keys' @@ -408,10 +269,11 @@ class Core( return status - def export_add_torrent_url(self, url, options): + @export + def add_torrent_url(self, url, options): log.info("Attempting to add url %s", url) - threading.Thread(target=self.fetch_torrent_url_thread, args=(self.export_add_torrent_file, url, options)).start() + threading.Thread(target=self.fetch_torrent_url_thread, args=(self.add_torrent_file, url, options)).start() def fetch_torrent_url_thread(self, callback, url, options): # Get the actual filename of the torrent from the url provided. @@ -432,7 +294,8 @@ class Core( # Add the torrent to session return callback(filename, filedump, options) - def export_add_torrent_magnets(self, uris, options): + @export + def add_torrent_magnets(self, uris, options): for uri in uris: log.debug("Attempting to add by magnet uri: %s", uri) try: @@ -440,71 +303,82 @@ class Core( except IndexError: option = None - torrent_id = self.torrents.add(magnet=uri, options=option) + torrent_id = self.torrentmanager.add(magnet=uri, options=option) # Run the plugin hooks for 'post_torrent_add' - self.plugins.run_post_torrent_add(torrent_id) + self.pluginmanager.run_post_torrent_add(torrent_id) - def export_remove_torrent(self, torrent_ids, remove_data): + @export + def remove_torrent(self, torrent_ids, remove_data): log.debug("Removing torrent %s from the core.", torrent_ids) for torrent_id in torrent_ids: - if self.torrents.remove(torrent_id, remove_data): + if self.torrentmanager.remove(torrent_id, remove_data): # Run the plugin hooks for 'post_torrent_remove' - self.plugins.run_post_torrent_remove(torrent_id) + self.pluginmanager.run_post_torrent_remove(torrent_id) - def export_force_reannounce(self, torrent_ids): + @export + def force_reannounce(self, torrent_ids): log.debug("Forcing reannouncment to: %s", torrent_ids) for torrent_id in torrent_ids: - self.torrents[torrent_id].force_reannounce() + self.torrentmanager[torrent_id].force_reannounce() - def export_pause_torrent(self, torrent_ids): + @export + def pause_torrent(self, torrent_ids): log.debug("Pausing: %s", torrent_ids) for torrent_id in torrent_ids: - if not self.torrents[torrent_id].pause(): + if not self.torrentmanager[torrent_id].pause(): log.warning("Error pausing torrent %s", torrent_id) - def export_connect_peer(self, torrent_id, ip, port): + @export + def connect_peer(self, torrent_id, ip, port): log.debug("adding peer %s to %s", ip, torrent_id) - if not self.torrents[torrent_id].connect_peer(ip, port): + if not self.torrentmanager[torrent_id].connect_peer(ip, port): log.warning("Error adding peer %s:%s to %s", ip, port, torrent_id) - def export_move_storage(self, torrent_ids, dest): + @export + def move_storage(self, torrent_ids, dest): log.debug("Moving storage %s to %s", torrent_ids, dest) for torrent_id in torrent_ids: - if not self.torrents[torrent_id].move_storage(dest): + if not self.torrentmanager[torrent_id].move_storage(dest): log.warning("Error moving torrent %s to %s", torrent_id, dest) - def export_pause_all_torrents(self): + @export + def pause_all_torrents(self): """Pause all torrents in the session""" self.session.pause() - def export_resume_all_torrents(self): + @export + def resume_all_torrents(self): """Resume all torrents in the session""" self.session.resume() - self.signals.emit("torrent_all_resumed") + self.signalmanager.emit("torrent_all_resumed") - def export_resume_torrent(self, torrent_ids): + @export + def resume_torrent(self, torrent_ids): log.debug("Resuming: %s", torrent_ids) for torrent_id in torrent_ids: - self.torrents[torrent_id].resume() + self.torrentmanager[torrent_id].resume() - def export_get_status_keys(self): + @export + def get_status_keys(self): """ returns all possible keys for the keys argument in get_torrent(s)_status. """ - return STATUS_KEYS + self.plugins.status_fields.keys() + return STATUS_KEYS + self.pluginmanager.status_fields.keys() - def export_get_torrent_status(self, torrent_id, keys): + @export + def get_torrent_status(self, torrent_id, keys): # Build the status dictionary - status = self.torrents[torrent_id].get_status(keys) + status = self.torrentmanager[torrent_id].get_status(keys) # Get the leftover fields and ask the plugin manager to fill them leftover_fields = list(set(keys) - set(status.keys())) if len(leftover_fields) > 0: - status.update(self.plugins.get_status(torrent_id, leftover_fields)) + status.update(self.pluginmanager.get_status(torrent_id, leftover_fields)) return status - def export_get_torrents_status(self, filter_dict, keys): + @export + def get_torrents_status(self, filter_dict, keys): """ returns all torrents , optionally filtered by filter_dict. """ @@ -513,32 +387,31 @@ class Core( # Get the torrent status for each torrent_id for torrent_id in torrent_ids: - status_dict[torrent_id] = self.export_get_torrent_status(torrent_id, keys) + status_dict[torrent_id] = self.get_torrent_status(torrent_id, keys) return status_dict - def export_get_filter_tree(self , show_zero_hits=True, hide_cat=None): + @export + def get_filter_tree(self , show_zero_hits=True, hide_cat=None): """ returns {field: [(value,count)] } for use in sidebar(s) """ return self.filtermanager.get_filter_tree(show_zero_hits, hide_cat) - def export_get_session_state(self): + @export + def get_session_state(self): """Returns a list of torrent_ids in the session.""" # Get the torrent list from the TorrentManager - return self.torrents.get_torrent_list() + return self.torrentmanager.get_torrent_list() - def export_save_state(self): - """Save the current session state to file.""" - # Have the TorrentManager save it's state - self.torrents.save_state() - - def export_get_config(self): + @export + def get_config(self): """Get all the preferences as a dictionary""" return self.config.config - def export_get_config_value(self, key): + @export + def get_config_value(self, key): """Get the config value for key""" try: value = self.config[key] @@ -547,7 +420,8 @@ class Core( return value - def export_get_config_values(self, keys): + @export + def get_config_values(self, keys): """Get the config values for the entered keys""" config = {} for key in keys: @@ -557,8 +431,8 @@ class Core( pass return config - - def export_set_config(self, config): + @export + def set_config(self, config): """Set the config with values from dictionary""" # Load all the values into the configuration for key in config.keys(): @@ -566,128 +440,157 @@ class Core( config[key] = config[key].encode("utf8") self.config[key] = config[key] - def export_get_listen_port(self): + @export + def get_listen_port(self): """Returns the active listen port""" return self.session.listen_port() - def export_get_num_connections(self): + @export + def get_num_connections(self): """Returns the current number of connections""" return self.session.num_connections() - def export_get_dht_nodes(self): + @export + def get_dht_nodes(self): """Returns the number of dht nodes""" return self.session.status().dht_nodes - def export_get_download_rate(self): + @export + def get_download_rate(self): """Returns the payload download rate""" return self.session.status().payload_download_rate - def export_get_upload_rate(self): + @export + def get_upload_rate(self): """Returns the payload upload rate""" return self.session.status().payload_upload_rate - def export_get_available_plugins(self): + @export + def get_available_plugins(self): """Returns a list of plugins available in the core""" - return self.plugins.get_available_plugins() + return self.pluginmanager.get_available_plugins() - def export_get_enabled_plugins(self): + @export + def get_enabled_plugins(self): """Returns a list of enabled plugins in the core""" - return self.plugins.get_enabled_plugins() + return self.pluginmanager.get_enabled_plugins() - def export_enable_plugin(self, plugin): - self.plugins.enable_plugin(plugin) + @export + def enable_plugin(self, plugin): + self.pluginmanager.enable_plugin(plugin) return None - def export_disable_plugin(self, plugin): - self.plugins.disable_plugin(plugin) + @export + def disable_plugin(self, plugin): + self.pluginmanager.disable_plugin(plugin) return None - def export_force_recheck(self, torrent_ids): + @export + def force_recheck(self, torrent_ids): """Forces a data recheck on torrent_ids""" for torrent_id in torrent_ids: - self.torrents[torrent_id].force_recheck() + self.torrentmanager[torrent_id].force_recheck() - def export_set_torrent_options(self, torrent_ids, options): + @export + def set_torrent_options(self, torrent_ids, options): """Sets the torrent options for torrent_ids""" for torrent_id in torrent_ids: - self.torrents[torrent_id].set_options(options) + self.torrentmanager[torrent_id].set_options(options) - def export_set_torrent_trackers(self, torrent_id, trackers): + @export + def set_torrent_trackers(self, torrent_id, trackers): """Sets a torrents tracker list. trackers will be [{"url", "tier"}]""" - return self.torrents[torrent_id].set_trackers(trackers) + return self.torrentmanager[torrent_id].set_trackers(trackers) - def export_set_torrent_max_connections(self, torrent_id, value): + @export + def set_torrent_max_connections(self, torrent_id, value): """Sets a torrents max number of connections""" - return self.torrents[torrent_id].set_max_connections(value) + return self.torrentmanager[torrent_id].set_max_connections(value) - def export_set_torrent_max_upload_slots(self, torrent_id, value): + @export + def set_torrent_max_upload_slots(self, torrent_id, value): """Sets a torrents max number of upload slots""" - return self.torrents[torrent_id].set_max_upload_slots(value) + return self.torrentmanager[torrent_id].set_max_upload_slots(value) - def export_set_torrent_max_upload_speed(self, torrent_id, value): + @export + def set_torrent_max_upload_speed(self, torrent_id, value): """Sets a torrents max upload speed""" - return self.torrents[torrent_id].set_max_upload_speed(value) + return self.torrentmanager[torrent_id].set_max_upload_speed(value) - def export_set_torrent_max_download_speed(self, torrent_id, value): + @export + def set_torrent_max_download_speed(self, torrent_id, value): """Sets a torrents max download speed""" - return self.torrents[torrent_id].set_max_download_speed(value) + return self.torrentmanager[torrent_id].set_max_download_speed(value) - def export_set_torrent_file_priorities(self, torrent_id, priorities): + @export + def set_torrent_file_priorities(self, torrent_id, priorities): """Sets a torrents file priorities""" - return self.torrents[torrent_id].set_file_priorities(priorities) + return self.torrentmanager[torrent_id].set_file_priorities(priorities) - def export_set_torrent_prioritize_first_last(self, torrent_id, value): + @export + def set_torrent_prioritize_first_last(self, torrent_id, value): """Sets a higher priority to the first and last pieces""" - return self.torrents[torrent_id].set_prioritize_first_last(value) + return self.torrentmanager[torrent_id].set_prioritize_first_last(value) - def export_set_torrent_auto_managed(self, torrent_id, value): + @export + def set_torrent_auto_managed(self, torrent_id, value): """Sets the auto managed flag for queueing purposes""" - return self.torrents[torrent_id].set_auto_managed(value) + return self.torrentmanager[torrent_id].set_auto_managed(value) - def export_set_torrent_stop_at_ratio(self, torrent_id, value): + @export + def set_torrent_stop_at_ratio(self, torrent_id, value): """Sets the torrent to stop at 'stop_ratio'""" - return self.torrents[torrent_id].set_stop_at_ratio(value) + return self.torrentmanager[torrent_id].set_stop_at_ratio(value) - def export_set_torrent_stop_ratio(self, torrent_id, value): + @export + def set_torrent_stop_ratio(self, torrent_id, value): """Sets the ratio when to stop a torrent if 'stop_at_ratio' is set""" - return self.torrents[torrent_id].set_stop_ratio(value) + return self.torrentmanager[torrent_id].set_stop_ratio(value) - def export_set_torrent_remove_at_ratio(self, torrent_id, value): + @export + def set_torrent_remove_at_ratio(self, torrent_id, value): """Sets the torrent to be removed at 'stop_ratio'""" - return self.torrents[torrent_id].set_remove_at_ratio(value) + return self.torrentmanager[torrent_id].set_remove_at_ratio(value) - def export_set_torrent_move_on_completed(self, torrent_id, value): + @export + def set_torrent_move_on_completed(self, torrent_id, value): """Sets the torrent to be moved when completed""" - return self.torrents[torrent_id].set_move_on_completed(value) + return self.torrentmanager[torrent_id].set_move_on_completed(value) - def export_set_torrent_move_on_completed_path(self, torrent_id, value): + @export + def set_torrent_move_on_completed_path(self, torrent_id, value): """Sets the path for the torrent to be moved when completed""" - return self.torrents[torrent_id].set_move_on_completed_path(value) + return self.torrentmanager[torrent_id].set_move_on_completed_path(value) - def export_block_ip_range(self, range): + @export + def block_ip_range(self, range): """Block an ip range""" self.ip_filter.add_rule(range[0], range[1], 1) # Start a 2 second timer (and remove the previous one if it exists) - if self._set_ip_filter_timer: - gobject.source_remove(self._set_ip_filter_timer) - self._set_ip_filter_timer = gobject.timeout_add(2000, self.session.set_ip_filter, self.ip_filter) + if self.__set_ip_filter_timer: + gobject.source_remove(self.__set_ip_filter_timer) + self.__set_ip_filter_timer = gobject.timeout_add(2000, self.session.set_ip_filter, self.ip_filter) - def export_reset_ip_filter(self): + @export + def reset_ip_filter(self): """Clears the ip filter""" self.ip_filter = lt.ip_filter() self.session.set_ip_filter(self.ip_filter) - def export_get_health(self): + @export + def get_health(self): """Returns True if we have established incoming connections""" return self.session.status().has_incoming_connections - def export_get_path_size(self, path): + @export + def get_path_size(self, path): """Returns the size of the file or folder 'path' and -1 if the path is unaccessible (non-existent or insufficient privs)""" return deluge.common.get_path_size(path) - def export_create_torrent(self, path, tracker, piece_length, comment, target, + @export + def create_torrent(self, path, tracker, piece_length, comment, target, url_list, private, created_by, httpseeds, add_to_session): log.debug("creating torrent..") @@ -719,9 +622,10 @@ class Core( httpseeds=httpseeds) log.debug("torrent created!") if add_to_session: - self.export_add_torrent_file(os.path.split(target)[1], open(target, "rb").read(), None) + self.add_torrent_file(os.path.split(target)[1], open(target, "rb").read(), None) - def export_upload_plugin(self, filename, plugin_data): + @export + def upload_plugin(self, filename, plugin_data): """This method is used to upload new plugins to the daemon. It is used when connecting to the daemon remotely and installing a new plugin on the client side. 'plugin_data' is a xmlrpc.Binary object of the file data, @@ -732,71 +636,79 @@ class Core( f.close() component.get("PluginManager").scan_for_plugins() - def export_rescan_plugins(self): + @export + def rescan_plugins(self): """Rescans the plugin folders for new plugins""" component.get("PluginManager").scan_for_plugins() - def export_rename_files(self, torrent_id, filenames): + @export + def rename_files(self, torrent_id, filenames): """Renames files in 'torrent_id'. The 'filenames' parameter should be a list of (index, filename) pairs.""" - self.torrents[torrent_id].rename_files(filenames) + self.torrentmanager[torrent_id].rename_files(filenames) - def export_rename_folder(self, torrent_id, folder, new_folder): + @export + def rename_folder(self, torrent_id, folder, new_folder): """Renames the 'folder' to 'new_folder' in 'torrent_id'.""" - self.torrents[torrent_id].rename_folder(folder, new_folder) + self.torrentmanager[torrent_id].rename_folder(folder, new_folder) - ## Queueing functions ## - def export_queue_top(self, torrent_ids): + @export + def queue_top(self, torrent_ids): log.debug("Attempting to queue %s to top", torrent_ids) for torrent_id in torrent_ids: try: # If the queue method returns True, then we should emit a signal - if self.torrents.queue_top(torrent_id): - self.signals.emit("torrent_queue_changed") + if self.torrentmanager.queue_top(torrent_id): + self.signalmanager.emit("torrent_queue_changed") except KeyError: log.warning("torrent_id: %s does not exist in the queue", torrent_id) - def export_queue_up(self, torrent_ids): + @export + def queue_up(self, torrent_ids): log.debug("Attempting to queue %s to up", torrent_ids) #torrent_ids must be sorted before moving. - torrent_ids.sort(key = lambda id: self.torrents.torrents[id].get_queue_position()) + torrent_ids.sort(key = lambda id: self.torrentmanager.torrents[id].get_queue_position()) for torrent_id in torrent_ids: try: # If the queue method returns True, then we should emit a signal - if self.torrents.queue_up(torrent_id): - self.signals.emit("torrent_queue_changed") + if self.torrentmanager.queue_up(torrent_id): + self.signalmanager.emit("torrent_queue_changed") except KeyError: log.warning("torrent_id: %s does not exist in the queue", torrent_id) - def export_queue_down(self, torrent_ids): + @export + def queue_down(self, torrent_ids): log.debug("Attempting to queue %s to down", torrent_ids) #torrent_ids must be sorted before moving. - torrent_ids.sort(key = lambda id: -self.torrents.torrents[id].get_queue_position()) + torrent_ids.sort(key = lambda id: -self.torrentmanager.torrents[id].get_queue_position()) for torrent_id in torrent_ids: try: # If the queue method returns True, then we should emit a signal - if self.torrents.queue_down(torrent_id): - self.signals.emit("torrent_queue_changed") + if self.torrentmanager.queue_down(torrent_id): + self.signalmanager.emit("torrent_queue_changed") except KeyError: log.warning("torrent_id: %s does not exist in the queue", torrent_id) - def export_queue_bottom(self, torrent_ids): + @export + def queue_bottom(self, torrent_ids): log.debug("Attempting to queue %s to bottom", torrent_ids) for torrent_id in torrent_ids: try: # If the queue method returns True, then we should emit a signal - if self.torrents.queue_bottom(torrent_id): - self.signals.emit("torrent_queue_changed") + if self.torrentmanager.queue_bottom(torrent_id): + self.signalmanager.emit("torrent_queue_changed") except KeyError: log.warning("torrent_id: %s does not exist in the queue", torrent_id) - def export_glob(self, path): + @export + def glob(self, path): return glob.glob(path) - def export_test_listen_port(self): + @export + def test_listen_port(self): """ Checks if active port is open """ import urllib - port = self.export_get_listen_port() + port = self.get_listen_port() try: status = urllib.urlopen("http://deluge-torrent.org/test_port.php?port=%s" % port).read() except IOError: diff --git a/deluge/core/daemon.py b/deluge/core/daemon.py index 4d05923fe..b7cedb6d4 100644 --- a/deluge/core/daemon.py +++ b/deluge/core/daemon.py @@ -22,15 +22,54 @@ # Boston, MA 02110-1301, USA. # +import signal +import gobject +import gettext +import locale +import pkg_resources + +import deluge.component as component import deluge.configmanager import deluge.common +from deluge.core.rpcserver import RPCServer, export from deluge.log import LOG as log -class Daemon: +class Daemon(object): def __init__(self, options, args): + # Initialize gettext + try: + locale.setlocale(locale.LC_ALL, '') + if hasattr(locale, "bindtextdomain"): + locale.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n")) + if hasattr(locale, "textdomain"): + locale.textdomain("deluge") + gettext.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n")) + gettext.textdomain("deluge") + gettext.install("deluge", pkg_resources.resource_filename("deluge", "i18n")) + except Exception, e: + log.error("Unable to initialize gettext/locale: %s", e) + + # Setup signals + signal.signal(signal.SIGINT, self.shutdown) + signal.signal(signal.SIGTERM, self.shutdown) + if not deluge.common.windows_check(): + signal.signal(signal.SIGHUP, self.shutdown) + else: + from win32api import SetConsoleCtrlHandler + from win32con import CTRL_CLOSE_EVENT + from win32con import CTRL_SHUTDOWN_EVENT + result = 0 + def win_handler(ctrl_type): + log.debug("ctrl_type: %s", ctrl_type) + if ctrl_type == CTRL_CLOSE_EVENT or ctrl_type == CTRL_SHUTDOWN_EVENT: + self.__shutdown() + result = 1 + return result + SetConsoleCtrlHandler(win_handler) + version = deluge.common.get_version() - if deluge.common.get_revision() != "": + if deluge.common.get_revision(): version = version + "r" + deluge.common.get_revision() log.info("Deluge daemon %s", version) @@ -41,5 +80,25 @@ class Daemon: from deluge.core.core import Core # Start the core as a thread and join it until it's done - self.core = Core(options.port).run() + self.core = Core() + self.rpcserver = RPCServer(options.port if options.port else self.core.config["daemon_port"]) + self.rpcserver.register_object(self.core, "core") + self.rpcserver.register_object(self, "daemon") + + gobject.threads_init() + + # Make sure we start the PreferencesManager first + component.start("PreferencesManager") + component.start() + + self.loop = gobject.MainLoop() + try: + self.loop.run() + except KeyboardInterrupt: + self.shutdown() + + @export + def shutdown(self): + component.shutdown() + self.loop.quit() diff --git a/deluge/core/filtermanager.py b/deluge/core/filtermanager.py index 7932507f3..e3eae47e3 100644 --- a/deluge/core/filtermanager.py +++ b/deluge/core/filtermanager.py @@ -75,7 +75,7 @@ class FilterManager(component.Component): component.Component.__init__(self, "FilterManager") log.debug("FilterManager init..") self.core = core - self.torrents = core.torrents + self.torrents = core.torrentmanager self.registered_filters = {} self.register_filter("keyword", filter_keywords) self.tree_fields = {} @@ -129,7 +129,7 @@ class FilterManager(component.Component): #leftover filter arguments: #default filter on status fields. - status_func = self.core.export_get_torrent_status #premature optimalisation.. + status_func = self.core.get_torrent_status #premature optimalisation.. for torrent_id in list(torrent_ids): status = status_func(torrent_id, filter_dict.keys()) #status={key:value} for field, values in filter_dict.iteritems(): @@ -144,7 +144,7 @@ class FilterManager(component.Component): for use in sidebar. """ torrent_ids = self.torrents.get_torrent_list() - status_func = self.core.export_get_torrent_status #premature optimalisation.. + status_func = self.core.get_torrent_status #premature optimalisation.. tree_keys = list(self.tree_fields.keys()) if hide_cat: for cat in hide_cat: @@ -196,7 +196,7 @@ class FilterManager(component.Component): del self.tree_fields[field] def filter_state_active(self, torrent_ids): - get_status = self.core.export_get_torrent_status + get_status = self.core.get_torrent_status for torrent_id in list(torrent_ids): status = get_status(torrent_id, ["download_payload_rate", "upload_payload_rate"]) if status["download_payload_rate"] or status["upload_payload_rate"]: @@ -223,6 +223,3 @@ class FilterManager(component.Component): iy = 99 return ix - iy - - - diff --git a/deluge/core/rpcserver.py b/deluge/core/rpcserver.py index 0713de675..48a210311 100644 --- a/deluge/core/rpcserver.py +++ b/deluge/core/rpcserver.py @@ -22,6 +22,7 @@ # Boston, MA 02110-1301, USA. # +"""RPCServer Module""" import gobject @@ -35,20 +36,29 @@ import deluge.component as component import deluge.configmanager def export(func): + """ + Decorator function to register an object's method as an RPC. The object + will need to be registered with an RPCServer to be effective. + + :param func: function, the function to export + """ func._rpcserver_export = True return func class RPCServer(component.Component): - def __init__(self, port): + """ + This class is used to handle rpc requests from the client. Objects are + registered with this class and their methods are exported using the export + decorator. + + :param port: int, the port the RPCServer will listen on + :param allow_remote: bool, set True if the server should allow remote connections + """ + + def __init__(self, port=58846, allow_remote=False): component.Component.__init__(self, "RPCServer") - # Get config - self.config = deluge.configmanager.ConfigManager("core.conf") - - if port == None: - port = self.config["daemon_port"] - - if self.config["allow_remote"]: + if allow_remote: hostname = "" else: hostname = "localhost" @@ -70,15 +80,24 @@ class RPCServer(component.Component): self.server.socket.setblocking(False) - gobject.io_add_watch(self.server.socket.fileno(), gobject.IO_IN | gobject.IO_OUT | gobject.IO_PRI | gobject.IO_ERR | gobject.IO_HUP, self._on_socket_activity) + gobject.io_add_watch(self.server.socket.fileno(), gobject.IO_IN | gobject.IO_OUT | gobject.IO_PRI | gobject.IO_ERR | gobject.IO_HUP, self.__on_socket_activity) - def _on_socket_activity(self, source, condition): - """This gets called when there is activity on the socket, ie, data to read - or to write.""" + def __on_socket_activity(self, source, condition): + """ + This gets called when there is activity on the socket, ie, data to read + or to write and is called by gobject.io_add_watch(). + """ self.server.handle_request() return True def register_object(self, obj, name=None): + """ + Registers an object to export it's rpc methods. These methods should + be exported with the export decorator prior to registering the object. + + :param obj: object, the object that we want to export + :param name: str, the name to use, if None, it will be the class name of the object + """ if not name: name = obj.__class__.__name__ @@ -89,10 +108,18 @@ class RPCServer(component.Component): log.debug("Registering method: %s", name + "." + d) self.server.register_function(getattr(obj, d), name + "." + d) + @property + def client_address(self): + """ + The ip address of the current client. + """ + return self.server.client_address + class XMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer): def get_request(self): - """Get the request and client address from the socket. - We override this so that we can get the ip address of the client. + """ + Get the request and client address from the socket. + We override this so that we can get the ip address of the client. """ request, client_address = self.socket.accept() self.client_address = client_address[0] diff --git a/deluge/plugins/blocklist/blocklist/core.py b/deluge/plugins/blocklist/blocklist/core.py index 621960f1f..c9c357c7d 100644 --- a/deluge/plugins/blocklist/blocklist/core.py +++ b/deluge/plugins/blocklist/blocklist/core.py @@ -35,6 +35,7 @@ from deluge.log import LOG as log from deluge.plugins.corepluginbase import CorePluginBase import deluge.component as component import deluge.configmanager +from deluge.core.rpcserver import export from peerguardian import PGReader, PGException from text import TextReader, GZMuleReader, PGZip @@ -72,7 +73,7 @@ class Core(CorePluginBase): self.config = deluge.configmanager.ConfigManager("blocklist.conf", DEFAULT_PREFS) if self.config["load_on_start"]: - self.export_import(self.need_new_blocklist()) + self.import_list(self.need_new_blocklist()) # This function is called every 'check_after_days' days, to download # and import a new list if needed. @@ -82,7 +83,7 @@ class Core(CorePluginBase): def disable(self): log.debug("Reset IP Filter..") - component.get("Core").export_reset_ip_filter() + component.get("Core").reset_ip_filter() self.config.save() log.debug('Blocklist: Plugin disabled') @@ -90,25 +91,30 @@ class Core(CorePluginBase): pass ## Exported RPC methods ### - def export_download(self, _import=False): + @export + def download_list(self, _import=False): """Download the blocklist specified in the config as url""" self.download_blocklist(_import) - def export_import(self, download=False, force=False): + @export + def import_list(self, download=False, force=False): """Import the blocklist from the blocklist.cache, if load is True, then it will download the blocklist file if needed.""" threading.Thread(target=self.import_blocklist, kwargs={"download": download, "force": force}).start() - def export_get_config(self): + @export + def get_config(self): """Returns the config dictionary""" return self.config.config - def export_set_config(self, config): + @export + def set_config(self, config): """Sets the config based on values in 'config'""" for key in config.keys(): self.config[key] = config[key] - def export_get_status(self): + @export + def get_status(self): """Returns the status of the plugin.""" status = {} if self.is_downloading: @@ -133,7 +139,7 @@ class Core(CorePluginBase): def on_download_blocklist(self, load): self.is_downloading = False if load: - self.export_import() + self.import_list() def import_blocklist(self, download=False, force=False): """Imports the downloaded blocklist into the session""" @@ -147,7 +153,7 @@ class Core(CorePluginBase): self.is_importing = True log.debug("Reset IP Filter..") - component.get("Core").export_reset_ip_filter() + component.get("Core").reset_ip_filter() self.num_blocked = 0 @@ -170,7 +176,7 @@ class Core(CorePluginBase): log.debug("Blocklist import starting..") ips = read_list.next() while ips: - self.core.export_block_ip_range(ips) + self.core.block_ip_range(ips) self.num_blocked += 1 ips = read_list.next() except Exception, e: diff --git a/deluge/plugins/blocklist/blocklist/gtkui.py b/deluge/plugins/blocklist/blocklist/gtkui.py index e46a46638..4729f97e9 100644 --- a/deluge/plugins/blocklist/blocklist/gtkui.py +++ b/deluge/plugins/blocklist/blocklist/gtkui.py @@ -108,7 +108,7 @@ class GtkUI(ui.UI): self.glade.get_widget("label_url").set_text( status["file_url"]) - client.blocklist_get_status(_on_get_status) + client.blocklist.get_status(_on_get_status) def _on_show_prefs(self): def _on_get_config(config): @@ -126,7 +126,7 @@ class GtkUI(ui.UI): self.glade.get_widget("chk_import_on_start").set_active( config["load_on_start"]) - client.blocklist_get_config(_on_get_config) + client.blocklist.get_config(_on_get_config) def _on_apply_prefs(self): config = {} @@ -135,13 +135,13 @@ class GtkUI(ui.UI): config["url"] = self.glade.get_widget("entry_url").get_text() config["check_after_days"] = self.glade.get_widget("spin_check_days").get_value_as_int() config["load_on_start"] = self.glade.get_widget("chk_import_on_start").get_active() - client.blocklist_set_config(None, config) + client.blocklist.set_config(None, config) def _on_button_check_download_clicked(self, widget): - client.blocklist_import(None, True, False) + client.blocklist.import_list(None, True, False) def _on_button_force_download_clicked(self, widget): - client.blocklist_import(None, True, True) + client.blocklist.import_list(None, True, True) def _on_status_item_clicked(self, widget, event): component.get("Preferences").show("Blocklist") diff --git a/deluge/plugins/blocklist/blocklist/webui.py b/deluge/plugins/blocklist/blocklist/webui.py index 5d28a1422..6acb37bbc 100644 --- a/deluge/plugins/blocklist/blocklist/webui.py +++ b/deluge/plugins/blocklist/blocklist/webui.py @@ -51,18 +51,18 @@ class BlockListCfgForm(forms.Form): #load/save: def initial_data(self): - data = sclient.blocklist_get_config() + data = sclient.blocklist.get_config() return data def save(self, data): cfg = dict(data) del cfg["btn_download_now"] del cfg["btn_import_now"] - sclient.blocklist_set_config(cfg) + sclient.blocklist.set_config(cfg) if data.btn_import_now: - aclient.blocklist_import(None, data.btn_download_now) + aclient.blocklist.import_list(None, data.btn_download_now) elif data.btn_download_now: - aclient.blocklist_download(None) + aclient.blocklist.download(None) #input fields : @@ -78,7 +78,7 @@ class BlockListCfgForm(forms.Form): def post_html(self): "show blocklist status" - status = sclient.blocklist_get_status() + status = sclient.blocklist.get_status() if status["state"] == "Downloading": txt = _("Downloading %.2f%%") % (status["file_progress"] * 100) @@ -109,6 +109,3 @@ class WebUI(ui.UI): def disable(self): config_page_manager.deregister('blocklist') - - - diff --git a/deluge/plugins/corepluginbase.py b/deluge/plugins/corepluginbase.py index 1bc4be00d..a2ac6aa26 100644 --- a/deluge/plugins/corepluginbase.py +++ b/deluge/plugins/corepluginbase.py @@ -22,19 +22,12 @@ # Boston, MA 02110-1301, USA. # - +import deluge.component as component from deluge.log import LOG as log class CorePluginBase: def __init__(self, plugin_api, plugin_name): self.plugin = plugin_api - # Register all export_* functions - for func in dir(self): - if func.startswith("export_"): - log.debug("Registering export function %s as %s", func, - plugin_name.lower() + "_" + func[7:]) - self.plugin.get_core().register_function( - getattr(self, "%s" % func), plugin_name.lower()\ - + "_" + func[7:]) + # Register RPC methods + component.get("RPCServer").register_object(self, plugin_name.lower()) log.debug("CorePlugin initialized..") - diff --git a/deluge/plugins/label/label/core.py b/deluge/plugins/label/label/core.py index 279bbb8fa..765bb9918 100644 --- a/deluge/plugins/label/label/core.py +++ b/deluge/plugins/label/label/core.py @@ -28,6 +28,7 @@ adds a status field for tracker. from deluge.log import LOG as log from deluge.plugins.corepluginbase import CorePluginBase +from deluge.core.rpcserver import export from deluge.configmanager import ConfigManager import deluge.component as component from urlparse import urlparse @@ -93,7 +94,7 @@ class Core(CorePluginBase): #self.set_config_defaults() #reduce typing, assigning some values to self... - self.torrents = core.torrents.torrents + self.torrents = core.torrentmanager.torrents self.labels = self.config["labels"] self.torrent_labels = self.config["torrent_labels"] @@ -180,7 +181,8 @@ class Core(CorePluginBase): return sorted(self.labels.keys()) #Labels: - def export_add(self, label_id): + @export + def add(self, label_id): """add a label see label_set_options for more options. """ @@ -191,7 +193,8 @@ class Core(CorePluginBase): self.labels[label_id] = dict(OPTIONS_DEFAULTS) - def export_remove(self, label_id): + @export + def remove(self, label_id): "remove a label" CheckInput(label_id in self.labels, _("Unknown Label")) del self.labels[label_id] @@ -230,7 +233,8 @@ class Core(CorePluginBase): return True return False - def export_set_options(self, label_id, options_dict , apply = False): + @export + def set_options(self, label_id, options_dict , apply = False): """update the label options options_dict : @@ -266,11 +270,13 @@ class Core(CorePluginBase): self.config.save() - def export_get_options(self, label_id): + @export + def get_options(self, label_id): """returns the label options""" return self.labels[label_id] - def export_set_torrent(self, torrent_id , label_id): + @export + def set_torrent(self, torrent_id , label_id): """ assign a label to a torrent removes a label if the label_id parameter is empty. @@ -291,11 +297,13 @@ class Core(CorePluginBase): self.config.save() - def export_get_config(self): + @export + def get_config(self): "see : label_set_config" return dict((key, self.config[key]) for key in CORE_OPTIONS if key in self.config.config) - def export_set_config(self, options): + @export + def set_config(self, options): """global_options:""" for key, value in options.items: if key in CORE_OPTIONS: diff --git a/deluge/plugins/label/label/gtkui/label_config.py b/deluge/plugins/label/label/gtkui/label_config.py index 990a2873e..affe37885 100644 --- a/deluge/plugins/label/label/gtkui/label_config.py +++ b/deluge/plugins/label/label/gtkui/label_config.py @@ -60,7 +60,7 @@ class LabelConfig(object): return pkg_resources.resource_filename("label", os.path.join("data", filename)) def load_settings(self, widget=None, data=None): - aclient.label_get_config(self.cb_global_options) + aclient.label.get_config(self.cb_global_options) def cb_global_options(self, options): log.debug("options=%s" % options) @@ -72,4 +72,4 @@ class LabelConfig(object): def on_apply_prefs(self): options = {} #update options dict here. - aclient.label_set_config(None, options) + aclient.label.set_config(None, options) diff --git a/deluge/plugins/label/label/gtkui/sidebar_menu.py b/deluge/plugins/label/label/gtkui/sidebar_menu.py index c64138452..cda61f23b 100644 --- a/deluge/plugins/label/label/gtkui/sidebar_menu.py +++ b/deluge/plugins/label/label/gtkui/sidebar_menu.py @@ -81,7 +81,7 @@ class LabelSidebarMenu(object): self.add_dialog.show() def on_remove(self, event=None): - aclient.label_remove(None, self.treeview.value) + aclient.label.remove(None, self.treeview.value) def on_options (self, event=None): self.options_dialog.show(self.treeview.value) @@ -137,7 +137,7 @@ class AddDialog(object): def on_ok(self, event=None): value = self.glade.get_widget("txt_add").get_text() - aclient.label_add(None, value) + aclient.label.add(None, value) self.dialog.destroy() def on_cancel(self, event=None): @@ -179,7 +179,7 @@ class OptionsDialog(object): chk = self.glade.get_widget(chk_id) chk.connect("toggled",self.apply_sensitivity) - aclient.label_get_options(self.load_options, self.label) + aclient.label.get_options(self.load_options, self.label) self.dialog.run() @@ -212,7 +212,7 @@ class OptionsDialog(object): options["auto_add_trackers"] = [x for x in tracker_lst if x] #filter out empty lines. log.debug(options) - aclient.label_set_options(None, self.label, options) + aclient.label.set_options(None, self.label, options) self.dialog.destroy() def apply_sensitivity(self, event=None): diff --git a/deluge/plugins/label/label/gtkui/submenu.py b/deluge/plugins/label/label/gtkui/submenu.py index 80dfc5ea2..f995339aa 100644 --- a/deluge/plugins/label/label/gtkui/submenu.py +++ b/deluge/plugins/label/label/gtkui/submenu.py @@ -58,7 +58,7 @@ class LabelMenu(gtk.MenuItem): def on_show(self, widget=None, data=None): log.debug("label-on-show") - aclient.label_get_labels(self.cb_labels) + aclient.label.get_labels(self.cb_labels) aclient.force_call(block=True) def cb_labels(self , labels): @@ -73,5 +73,5 @@ class LabelMenu(gtk.MenuItem): def on_select_label(self, widget=None, label_id = None): log.debug("select label:%s,%s" % (label_id ,self.get_torrent_ids()) ) for torrent_id in self.get_torrent_ids(): - aclient.label_set_torrent(None, torrent_id, label_id) + aclient.label.set_torrent(None, torrent_id, label_id) #aclient.force_call(block=True) diff --git a/deluge/plugins/label/label/webui.py b/deluge/plugins/label/label/webui.py index ca8ccbb0b..704ddbf45 100644 --- a/deluge/plugins/label/label/webui.py +++ b/deluge/plugins/label/label/webui.py @@ -54,12 +54,12 @@ class options: @api.deco.deluge_page def GET(self, label_id): - return self.page(label_id, sclient.label_get_options(label_id)) + return self.page(label_id, sclient.label.get_options(label_id)) @api.deco.check_session def POST(self, label_id): post_options = api.utils.get_newforms_data(OptionsForm) - options = sclient.label_get_options(label_id) + options = sclient.label.get_options(label_id) log.debug(options) options.update(dict(post_options)) @@ -72,7 +72,7 @@ class options: else: error = None - sclient.label_set_options(label_id, options_form.cleaned_data) + sclient.label.set_options(label_id, options_form.cleaned_data) api.utils.seeother("/config/label") @@ -90,7 +90,7 @@ class config_page: """for ajaxui.""" @api.deco.deluge_page def GET(self, args): - labels = sclient.label_get_labels() + labels = sclient.label.get_labels() return api.render.label.config_page(labels) @@ -125,7 +125,7 @@ class OptionsForm(forms.Form): #load/save: def initial_data(self): - return sclient.label_get_options(self.label_id) + return sclient.label.get_options(self.label_id) #maximum: apply_max = forms.CheckBox(_("apply_max")) @@ -164,15 +164,15 @@ class ConfigForm(forms.Form): #load/save: def initial_data(self): - return sclient.label_get_config() + return sclient.label.get_config() def save(self, data): cfg = dict(data) - sclient.label_set_config(cfg) + sclient.label.set_config(cfg) def pre_html(self): """ custom config html/template""" - labels = sclient.label_get_labels() + labels = sclient.label.get_labels() return api.render.label.config(labels) diff --git a/deluge/ui/client.py b/deluge/ui/client.py index 4f7924c48..e7b296a1b 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -111,6 +111,7 @@ class CoreProxy(gobject.GObject): if self.rpc_core is None: raise deluge.error.NoCoreError("The core proxy is invalid.") return + _func = getattr(self._multi, func) if _func is not None: @@ -200,6 +201,18 @@ class CoreProxy(gobject.GObject): _core = CoreProxy() +class DottedObject(object): + """This is used for dotted name calls to client""" + def __init__(self, client, base): + self.client = client + self.base = base + + def __call__(self, *args, **kwargs): + return self.client.get_method("core." + self.base)(*args, **kwargs) + + def __getattr__(self, name): + return self.client.get_method(self.base + "." + name) + class BaseClient(object): """ wraps all calls to core/coreproxy @@ -242,8 +255,7 @@ class BaseClient(object): raise NotImplementedError() def __getattr__(self, method_name): - return self.get_method(method_name) - #raise AttributeError("no attr/method named:%s" % attr) + return DottedObject(self, method_name) #custom wrapped methods: def add_torrent_file(self, torrent_files, torrent_options=None): @@ -277,7 +289,7 @@ class BaseClient(object): options = None else: options = None - self.get_method("add_torrent_file")(filename, fdump, options) + self.get_method("core.add_torrent_file")(filename, fdump, options) def add_torrent_file_binary(self, filename, fdump, options = None): """ @@ -286,10 +298,11 @@ class BaseClient(object): Expects fdump as a bytestring (== result of f.read()). """ fdump_xmlrpc = xmlrpclib.Binary(fdump) - self.get_method("add_torrent_file")(filename, fdump_xmlrpc, options) + self.get_method("core.add_torrent_file")(filename, fdump_xmlrpc, options) #utility: def has_callback(self, method_name): + method_name = method_name.split(".")[-1] return not (method_name in self.no_callback_list) def is_localhost(self): diff --git a/deluge/ui/console/commands/halt.py b/deluge/ui/console/commands/halt.py index 001336004..6849a409b 100644 --- a/deluge/ui/console/commands/halt.py +++ b/deluge/ui/console/commands/halt.py @@ -7,4 +7,4 @@ from deluge.ui.client import aclient as client class Command(BaseCommand): "Shutdown the deluge server." def handle(self, **options): - client.shutdown() + client.daemon.shutdown(None) diff --git a/deluge/ui/gtkui/connectionmanager.py b/deluge/ui/gtkui/connectionmanager.py index 6ed56ae3e..41bef0ad3 100644 --- a/deluge/ui/gtkui/connectionmanager.py +++ b/deluge/ui/gtkui/connectionmanager.py @@ -358,7 +358,7 @@ class ConnectionManager(component.Component): host = xmlrpclib.ServerProxy(get_localhost_auth_uri(uri)) else: host = xmlrpclib.ServerProxy(uri) - host.ping() + host.core.ping() except Exception: online = False diff --git a/deluge/ui/gtkui/gtkui.py b/deluge/ui/gtkui/gtkui.py index 0675f5819..3d71e1d2c 100644 --- a/deluge/ui/gtkui/gtkui.py +++ b/deluge/ui/gtkui/gtkui.py @@ -230,7 +230,7 @@ class GtkUI: component.shutdown() if self.started_in_classic: try: - client.shutdown() + client.daemon.shutdown(None) except: pass diff --git a/deluge/ui/gtkui/menubar.py b/deluge/ui/gtkui/menubar.py index 30d4fd8d6..dfc030ee4 100644 --- a/deluge/ui/gtkui/menubar.py +++ b/deluge/ui/gtkui/menubar.py @@ -231,14 +231,14 @@ class MenuBar(component.Component): def on_menuitem_quitdaemon_activate(self, data=None): log.debug("on_menuitem_quitdaemon_activate") # Tell the core to shutdown - client.shutdown() + client.daemon.shutdown(None) self.window.quit() def on_menuitem_quit_activate(self, data=None): log.debug("on_menuitem_quit_activate") if self.config["classic_mode"]: try: - client.shutdown() + client.daemon.shutdown(None) except deluge.error.NoCoreError: pass self.window.quit() diff --git a/deluge/ui/gtkui/systemtray.py b/deluge/ui/gtkui/systemtray.py index a37452f44..4465737ad 100644 --- a/deluge/ui/gtkui/systemtray.py +++ b/deluge/ui/gtkui/systemtray.py @@ -308,7 +308,7 @@ class SystemTray(component.Component): return if self.config["classic_mode"]: - client.shutdown() + client.daemon.shutdown(None) self.window.quit() @@ -318,7 +318,7 @@ class SystemTray(component.Component): if not self.unlock_tray(): return - client.shutdown() + client.daemon.shutdown(None) self.window.quit() def tray_setbwdown(self, widget, data=None):