diff --git a/deluge/plugins/WebUi/deluge/plugins/webui/core.py b/deluge/plugins/WebUi/deluge/plugins/webui/core.py index b3a1a2722..0f13e6504 100644 --- a/deluge/plugins/WebUi/deluge/plugins/webui/core.py +++ b/deluge/plugins/WebUi/deluge/plugins/webui/core.py @@ -66,12 +66,12 @@ class Core(CorePluginBase): try: self.server = component.get("DelugeWeb") except KeyError: - self.server = server.DelugeWeb() + self.server = server.DelugeWeb(daemon=False) self.server.port = self.config["port"] self.server.https = self.config["ssl"] try: - self.server.start(standalone=False) + self.server.start() except CannotListenError as ex: log.warn("Failed to start WebUI server: %s", ex) raise diff --git a/deluge/tests/test_ui_entry.py b/deluge/tests/test_ui_entry.py new file mode 100644 index 000000000..8163ef157 --- /dev/null +++ b/deluge/tests/test_ui_entry.py @@ -0,0 +1,49 @@ +# -*- coding: utf-8 -*- +import mock +import pytest + +import deluge.component as component +from deluge.ui import ui_entry + +from . import common +from .basetest import BaseTestCase + + +@pytest.mark.gtkui +class UIEntryTestCase(BaseTestCase): + + def set_up(self): + common.set_tmp_config_dir() + return component.start() + + def tear_down(self): + return component.shutdown() + + def test_start_gtkui(self): + import deluge.ui.gtkui.gtkui + import sys + self.patch(sys, "argv", ['./deluge', "--ui", 'gtk']) + + with mock.patch.object(deluge.ui.gtkui.gtkui.GtkUI, 'start', autospec=True): + ui_entry.start_ui() + + def test_start_console(self): + import sys + self.patch(sys, "argv", ['./deluge', "--ui", 'console']) + with mock.patch('deluge.ui.console.main.ConsoleUI'): + ui_entry.start_ui() + + def test_start_webserver(self): + import sys + from deluge.ui.web.server import DelugeWeb + + self.patch(sys, "argv", ['./deluge', "--ui", 'web', '--do-not-daemonize']) + + class DelugeWebMock(DelugeWeb): + def __init__(self, *args, **kwargs): + kwargs["daemon"] = False + DelugeWeb.__init__(self, *args, **kwargs) + + import deluge.ui.web.server + self.patch(deluge.ui.web.server, 'DelugeWeb', DelugeWebMock) + ui_entry.start_ui() diff --git a/deluge/tests/test_web_api.py b/deluge/tests/test_web_api.py index 6435d1410..f756130fe 100644 --- a/deluge/tests/test_web_api.py +++ b/deluge/tests/test_web_api.py @@ -64,7 +64,7 @@ class WebAPITestCase(BaseTestCase, DaemonBase): config_defaults["port"] = self.webserver_listen_port self.config = configmanager.ConfigManager("web.conf", config_defaults) - self.deluge_web = DelugeWeb() + self.deluge_web = DelugeWeb(daemon=False) host = list(self.deluge_web.web_api.host_list["hosts"][0]) host[2] = self.listen_port diff --git a/deluge/ui/gtkui/gtkui.py b/deluge/ui/gtkui/gtkui.py index 4e62b5b62..85a72c375 100644 --- a/deluge/ui/gtkui/gtkui.py +++ b/deluge/ui/gtkui/gtkui.py @@ -28,7 +28,7 @@ try: reactor = gtk2reactor.install() except ReactorAlreadyInstalledError as ex: # Running unit tests so trial already installed a rector - pass + from twisted.internet import reactor import deluge.common import deluge.component as component @@ -151,7 +151,8 @@ class Gtk(UI): def run(options): try: - GtkUI(options) + gtkui = GtkUI(options) + gtkui.start() except Exception as ex: log.exception(ex) raise @@ -250,19 +251,20 @@ class GtkUI(object): # daemon_bps: time, bytes_sent, bytes_recv self.daemon_bps = (0, 0, 0) self.rpc_stats = LoopingCall(self.print_rpc_stats) + self.closing = False # Twisted catches signals to terminate, so have it call a pre_shutdown method. reactor.addSystemEventTrigger("before", "gtkui_close", self.close) - reactor.callWhenRunning(self._on_reactor_start) - self.closing = False - def gtkui_sigint_handler(num, frame): log.debug("SIGINT signal caught - firing event: 'gtkui_close'") reactor.callLater(0, reactor.fireSystemEvent, 'gtkui_close') signal.signal(signal.SIGINT, gtkui_sigint_handler) + def start(self): + reactor.callWhenRunning(self._on_reactor_start) + # Initialize gdk threading gtk.gdk.threads_enter() reactor.run() diff --git a/deluge/ui/gtkui/ipcinterface.py b/deluge/ui/gtkui/ipcinterface.py index c18816244..8cd764b9b 100644 --- a/deluge/ui/gtkui/ipcinterface.py +++ b/deluge/ui/gtkui/ipcinterface.py @@ -74,6 +74,7 @@ class IPCClientFactory(ClientFactory): class IPCInterface(component.Component): def __init__(self, args): component.Component.__init__(self, "IPCInterface") + self.listener = None ipc_dir = get_config_dir("ipc") if not os.path.exists(ipc_dir): os.makedirs(ipc_dir) @@ -91,7 +92,7 @@ class IPCInterface(component.Component): self.factory.protocol = IPCProtocolServer import random port = random.randrange(20000, 65535) - reactor.listenTCP(port, self.factory) + self.listener = reactor.listenTCP(port, self.factory) # Store the port number in the socket file open(socket, "w").write(str(port)) # We need to process any args when starting this process @@ -132,7 +133,7 @@ class IPCInterface(component.Component): try: self.factory = Factory() self.factory.protocol = IPCProtocolServer - reactor.listenUNIX(socket, self.factory, wantPID=True) + self.listener = reactor.listenUNIX(socket, self.factory, wantPID=True) except twisted.internet.error.CannotListenError as ex: log.info("Deluge is already running! Sending arguments to running instance...") self.factory = IPCClientFactory() @@ -160,6 +161,8 @@ class IPCInterface(component.Component): if windows_check(): import win32api win32api.CloseHandle(self.mutex) + if self.listener: + return self.listener.stopListening() def process_args(args): diff --git a/deluge/ui/web/server.py b/deluge/ui/web/server.py index 5ad2936f2..3b79bf093 100644 --- a/deluge/ui/web/server.py +++ b/deluge/ui/web/server.py @@ -532,8 +532,17 @@ class TopLevel(resource.Resource): class DelugeWeb(component.Component): - def __init__(self, options=None): - super(DelugeWeb, self).__init__("DelugeWeb") + def __init__(self, options=None, daemon=True): + """ + Setup the DelugeWeb server. + + Args: + options (argparse.Namespace): The web server options. + daemon (bool): If True run web server as a seperate daemon process (starts a twisted + reactor). If False shares the process and twisted reactor from WebUI plugin or tests. + + """ + component.Component.__init__(self, "DelugeWeb", depend=["Web"]) self.config = configmanager.ConfigManager("web.conf", CONFIG_DEFAULTS) self.config.run_converter((0, 1), 2, self._migrate_config_1_to_2) self.config.register_set_function("language", self._on_language_changed) @@ -567,7 +576,7 @@ class DelugeWeb(component.Component): self.web_utils = WebUtils() self.auth = Auth(self.config) - self.standalone = True + self.daemon = daemon # Initalize the plugins self.plugins = PluginManager() @@ -594,22 +603,14 @@ class DelugeWeb(component.Component): return 1 SetConsoleCtrlHandler(win_handler) - def start(self, standalone=True): + def start(self): """ Start the DelugeWeb server - - When running WebUI plugin, the server must not try to start - the twisted reactor. - - Args: - standalone (bool): Whether the server runs as a standalone process - If standalone, start twisted reactor. """ if self.socket: log.warn("DelugeWeb is already running and cannot be started") return - self.standalone = standalone log.info("Starting webui server at PID %s", os.getpid()) if self.https: self.start_ssl() @@ -618,7 +619,7 @@ class DelugeWeb(component.Component): component.get("Web").enable() - if self.standalone: + if self.daemon: reactor.run() def start_normal(self): @@ -641,7 +642,10 @@ class DelugeWeb(component.Component): def stop(self): log.info("Shutting down webserver") - component.get("Web").disable() + try: + component.get("Web").disable() + except KeyError: + pass self.plugins.disable_plugins() log.debug("Saving configuration file") @@ -657,7 +661,7 @@ class DelugeWeb(component.Component): def shutdown(self, *args): self.stop() - if self.standalone and reactor.running: + if self.daemon and reactor.running: reactor.stop() def _migrate_config_1_to_2(self, config): diff --git a/deluge/ui/web/web.py b/deluge/ui/web/web.py index c8d9399ce..72a376633 100644 --- a/deluge/ui/web/web.py +++ b/deluge/ui/web/web.py @@ -60,7 +60,7 @@ class Web(UI): self.server.install_signal_handlers() self.server.start() except CannotListenError as ex: - log.error("%s \nCheck that deluge-web or webui plugin are not already running.", ex) + log.error("%s \nCheck that deluge-web or webui plugin is not already running.", ex) except Exception as ex: log.exception(ex) raise