Ticket #1974: 0001-Decouple-GUI-selection-from-core.patch

File 0001-Decouple-GUI-selection-from-core.patch, 17.6 KB (added by jumentous, 13 years ago)
  • deluge/main.py

    From 3227bdc65af97bb74d383333a32eb6153c065eda Mon Sep 17 00:00:00 2001
    From: Jamie Lennox <jamielennox@gmail.com>
    Date: Sat, 19 Nov 2011 20:20:43 +1100
    Subject: [PATCH] Decouple GUI selection from core.
    
    Add EntryPoints into setup for each of the UIs and then use this information
    to determine which client UI to run.
    ---
     deluge/main.py                |   34 +++++------
     deluge/ui/console/__init__.py |    5 +-
     deluge/ui/console/console.py  |  128 +++++++++++++++++++++++++++++++++++++++++
     deluge/ui/console/main.py     |   91 -----------------------------
     deluge/ui/gtkui/__init__.py   |   18 ++++++-
     deluge/ui/gtkui/gtkui.py      |   15 -----
     deluge/ui/web/__init__.py     |    5 +-
     deluge/ui/web/web.py          |    1 +
     setup.py                      |    7 ++-
     9 files changed, 174 insertions(+), 130 deletions(-)
     create mode 100644 deluge/ui/console/console.py
    
    diff --git a/deluge/main.py b/deluge/main.py
    index 5797251..4b429e7 100644
    a b  
    4444import sys
    4545import optparse
    4646import logging
     47import pkg_resources
    4748
    4849import deluge.log
    4950import deluge.error
    def start_ui():  
    6263    parser = CommonOptionParser()
    6364    group = optparse.OptionGroup (parser, _("Default Options"))
    6465
    65     group.add_option("-u", "--ui", dest="ui",
    66         help="""The UI that you wish to launch.  The UI choices are:\n
    67         \t gtk -- A GTK-based graphical user interface (default)\n
    68         \t web -- A web-based interface (http://localhost:8112)\n
    69         \t console -- A console or command-line interface""", action="store", type="str")
     66    ui_entrypoints = dict([(entrypoint.name, entrypoint.load())
     67        for entrypoint in pkg_resources.iter_entry_points('deluge.ui')])
     68
     69    cmd_help = ['The UI that you wish to launch.  The UI choices are:']
     70    cmd_help.extend(["%s -- %s" % (k, getattr(v, 'cmdline', "")) for k,v in ui_entrypoints.iteritems()])
     71
     72    parser.add_option("-u", "--ui", dest="ui",
     73        choices = ui_entrypoints.keys(), help="\n\t ".join(cmd_help), action="store")
    7074    group.add_option("-a", "--args", dest="args",
    7175        help="Arguments to pass to UI, -a '--option args'", action="store", type="str")
    7276    group.add_option("-s", "--set-default-ui", dest="default_ui",
    def start_ui():  
    106110    client_args.extend(args)
    107111
    108112    try:
    109         if selected_ui == "gtk":
    110             log.info("Starting GtkUI..")
    111             from deluge.ui.gtkui.gtkui import Gtk
    112             ui = Gtk(skip_common = True)
    113             ui.start(client_args)
    114         elif selected_ui == "web":
    115             log.info("Starting WebUI..")
    116             from deluge.ui.web.web import Web
    117             ui = Web(skip_common = True)
    118             ui.start(client_args)
    119         elif selected_ui == "console":
    120             log.info("Starting ConsoleUI..")
    121             from deluge.ui.console.main import Console
    122             ui = Console(skip_common = True)
    123             ui.start(client_args)
     113        ui = ui_entrypoints[selected_ui](skip_common = True)
     114    except KeyError, e:
     115        log.error("Unable to find the requested UI: %s. Please select a different UI with the '-u' option or alternatively use the '-s' option to select a different default UI.", selected_ui)
    124116    except ImportError, e:
    125117        import sys
    126118        import traceback
    def start_ui():  
    134126            log.error("There was an error whilst launching the request UI: %s", selected_ui)
    135127            log.error("Look at the traceback above for more information.")
    136128        sys.exit(1)
     129    else:
     130        ui.start(client_args)
    137131
    138132def start_daemon():
    139133    """Entry point for daemon script"""
  • deluge/ui/console/__init__.py

    diff --git a/deluge/ui/console/__init__.py b/deluge/ui/console/__init__.py
    index 0747fac..09b2438 100644
    a b  
    3333#
    3434#
    3535UI_PATH = __path__[0]
    36 from main import start
     36from console import Console
     37
     38def start():
     39    Console().start()
  • new file deluge/ui/console/console.py

    diff --git a/deluge/ui/console/console.py b/deluge/ui/console/console.py
    new file mode 100644
    index 0000000..139e789
    - +  
     1#
     2# main.py
     3#
     4# Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com>
     5# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
     6#
     7# Deluge is free software.
     8#
     9# You may redistribute it and/or modify it under the terms of the
     10# GNU General Public License, as published by the Free Software
     11# Foundation; either version 3 of the License, or (at your option)
     12# any later version.
     13#
     14# deluge is distributed in the hope that it will be useful,
     15# but WITHOUT ANY WARRANTY; without even the implied warranty of
     16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
     17# See the GNU General Public License for more details.
     18#
     19# You should have received a copy of the GNU General Public License
     20# along with deluge.    If not, write to:
     21#       The Free Software Foundation, Inc.,
     22#       51 Franklin Street, Fifth Floor
     23#       Boston, MA  02110-1301, USA.
     24#
     25#    In addition, as a special exception, the copyright holders give
     26#    permission to link the code of portions of this program with the OpenSSL
     27#    library.
     28#    You must obey the GNU General Public License in all respects for all of
     29#    the code used other than OpenSSL. If you modify file(s) with this
     30#    exception, you may extend this exception to your version of the file(s),
     31#    but you are not obligated to do so. If you do not wish to do so, delete
     32#    this exception statement from your version. If you delete this exception
     33#    statement from all source files in the program, then also delete it here.
     34#
     35#
     36
     37import os
     38import sys
     39import optparse
     40from deluge.ui.ui import _UI
     41from deluge.ui.console import UI_PATH
     42
     43def load_commands(command_dir, exclude=[]):
     44    def get_command(name):
     45        return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, {}, ['Command']), 'Command')()
     46
     47    try:
     48        commands = []
     49        for filename in os.listdir(command_dir):
     50            if filename.split('.')[0] in exclude or filename.startswith('_'):
     51                continue
     52            if not (filename.endswith('.py') or filename.endswith('.pyc')):
     53                continue
     54            cmd = get_command(filename.split('.')[len(filename.split('.')) - 2])
     55            aliases = [ filename.split('.')[len(filename.split('.')) - 2] ]
     56            aliases.extend(cmd.aliases)
     57            for a in aliases:
     58                commands.append((a, cmd))
     59        return dict(commands)
     60    except OSError, e:
     61        return {}
     62
     63class Console(_UI):
     64
     65    help = """Starts the Deluge console interface"""
     66    cmdline = """A console or command-line interface"""
     67
     68    def __init__(self, *args, **kwargs):
     69        super(Console, self).__init__("console", *args, **kwargs)
     70        group = optparse.OptionGroup(self.parser, "Console Options","These options control how "
     71                                     "the console connects to the daemon.  These options will be "
     72                                     "used if you pass a command, or if you have autoconnect "
     73                                     "enabled for the console ui.")
     74
     75        group.add_option("-d","--daemon",dest="daemon_addr",
     76                         action="store",type="str",default="127.0.0.1",
     77                         help="Set the address of the daemon to connect to."
     78                              " [default: %default]")
     79        group.add_option("-p","--port",dest="daemon_port",
     80                         help="Set the port to connect to the daemon on. [default: %default]",
     81                         action="store",type="int",default=58846)
     82        group.add_option("-u","--username",dest="daemon_user",
     83                         help="Set the username to connect to the daemon with. [default: %default]",
     84                         action="store",type="string")
     85        group.add_option("-P","--password",dest="daemon_pass",
     86                         help="Set the password to connect to the daemon with. [default: %default]",
     87                         action="store",type="string")
     88        self.parser.add_option_group(group)
     89
     90        self.cmds = load_commands(os.path.join(UI_PATH, 'commands'))
     91        class CommandOptionGroup(optparse.OptionGroup):
     92            def __init__(self, parser, title, description=None, cmds = None):
     93                optparse.OptionGroup.__init__(self,parser,title,description)
     94                self.cmds = cmds
     95
     96            def format_help(self, formatter):
     97                result = formatter.format_heading(self.title)
     98                formatter.indent()
     99                if self.description:
     100                    result += "%s\n"%formatter.format_description(self.description)
     101                for cname in self.cmds:
     102                    cmd = self.cmds[cname]
     103                    if cmd.interactive_only or cname in cmd.aliases: continue
     104                    allnames = [cname]
     105                    allnames.extend(cmd.aliases)
     106                    cname = "/".join(allnames)
     107                    result += formatter.format_heading(" - ".join([cname,cmd.__doc__]))
     108                    formatter.indent()
     109                    result += "%*s%s\n" % (formatter.current_indent, "", cmd.usage)
     110                    formatter.dedent()
     111                formatter.dedent()
     112                return result
     113        cmd_group = CommandOptionGroup(self.parser, "Console Commands",
     114                                       description="The following commands can be issued at the "
     115                                       "command line.  Commands should be quoted, so, for example, "
     116                                       "to pause torrent with id 'abc' you would run: '%s "
     117                                       "\"pause abc\"'"%os.path.basename(sys.argv[0]),
     118                                       cmds=self.cmds)
     119        self.parser.add_option_group(cmd_group)
     120
     121    def start(self, args = None):
     122        from main import ConsoleUI
     123        super(Console, self).start(args)
     124        ConsoleUI(self.args,self.cmds,(self.options.daemon_addr,
     125                  self.options.daemon_port,self.options.daemon_user,
     126                  self.options.daemon_pass))
     127
     128
  • deluge/ui/console/main.py

    diff --git a/deluge/ui/console/main.py b/deluge/ui/console/main.py
    index 1f0467e..bbe5a24 100644
    a b  
    3434#
    3535#
    3636
    37 import os
    3837import sys
    3938import logging
    4039import optparse
     
    5150from deluge.ui.console.statusbars import StatusBars
    5251from deluge.ui.console.eventlog import EventLog
    5352import colors
    54 from deluge.ui.ui import _UI
    55 from deluge.ui.console import UI_PATH
    56 
    5753
    5854log = logging.getLogger(__name__)
    5955
    60 class Console(_UI):
    61 
    62     help = """Starts the Deluge console interface"""
    63 
    64     def __init__(self, *args, **kwargs):
    65         super(Console, self).__init__("console", *args, **kwargs)
    66         group = optparse.OptionGroup(self.parser, "Console Options","These options control how "
    67                                      "the console connects to the daemon.  These options will be "
    68                                      "used if you pass a command, or if you have autoconnect "
    69                                      "enabled for the console ui.")
    70 
    71         group.add_option("-d","--daemon",dest="daemon_addr",
    72                          action="store",type="str",default="127.0.0.1",
    73                          help="Set the address of the daemon to connect to."
    74                               " [default: %default]")
    75         group.add_option("-p","--port",dest="daemon_port",
    76                          help="Set the port to connect to the daemon on. [default: %default]",
    77                          action="store",type="int",default=58846)
    78         group.add_option("-u","--username",dest="daemon_user",
    79                          help="Set the username to connect to the daemon with. [default: %default]",
    80                          action="store",type="string")
    81         group.add_option("-P","--password",dest="daemon_pass",
    82                          help="Set the password to connect to the daemon with. [default: %default]",
    83                          action="store",type="string")
    84         self.parser.add_option_group(group)
    85 
    86         self.cmds = load_commands(os.path.join(UI_PATH, 'commands'))
    87         class CommandOptionGroup(optparse.OptionGroup):
    88             def __init__(self, parser, title, description=None, cmds = None):
    89                 optparse.OptionGroup.__init__(self,parser,title,description)
    90                 self.cmds = cmds
    91 
    92             def format_help(self, formatter):
    93                 result = formatter.format_heading(self.title)
    94                 formatter.indent()
    95                 if self.description:
    96                     result += "%s\n"%formatter.format_description(self.description)
    97                 for cname in self.cmds:
    98                     cmd = self.cmds[cname]
    99                     if cmd.interactive_only or cname in cmd.aliases: continue
    100                     allnames = [cname]
    101                     allnames.extend(cmd.aliases)
    102                     cname = "/".join(allnames)
    103                     result += formatter.format_heading(" - ".join([cname,cmd.__doc__]))
    104                     formatter.indent()
    105                     result += "%*s%s\n" % (formatter.current_indent, "", cmd.usage)
    106                     formatter.dedent()
    107                 formatter.dedent()
    108                 return result
    109         cmd_group = CommandOptionGroup(self.parser, "Console Commands",
    110                                        description="The following commands can be issued at the "
    111                                        "command line.  Commands should be quoted, so, for example, "
    112                                        "to pause torrent with id 'abc' you would run: '%s "
    113                                        "\"pause abc\"'"%os.path.basename(sys.argv[0]),
    114                                        cmds=self.cmds)
    115         self.parser.add_option_group(cmd_group)
    116 
    117     def start(self, args = None):
    118         super(Console, self).start(args)
    119         ConsoleUI(self.args,self.cmds,(self.options.daemon_addr,
    120                   self.options.daemon_port,self.options.daemon_user,
    121                   self.options.daemon_pass))
    122 
    123 def start():
    124     Console().start()
    125 
    12656class OptionParser(optparse.OptionParser):
    12757    """subclass from optparse.OptionParser so exit() won't exit."""
    12858    def exit(self, status=0, msg=None):
    def create_parser(self):  
    172102                            option_list = self.option_list)
    173103
    174104
    175 def load_commands(command_dir, exclude=[]):
    176     def get_command(name):
    177         return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, {}, ['Command']), 'Command')()
    178 
    179     try:
    180         commands = []
    181         for filename in os.listdir(command_dir):
    182             if filename.split('.')[0] in exclude or filename.startswith('_'):
    183                 continue
    184             if not (filename.endswith('.py') or filename.endswith('.pyc')):
    185                 continue
    186             cmd = get_command(filename.split('.')[len(filename.split('.')) - 2])
    187             aliases = [ filename.split('.')[len(filename.split('.')) - 2] ]
    188             aliases.extend(cmd.aliases)
    189             for a in aliases:
    190                 commands.append((a, cmd))
    191         return dict(commands)
    192     except OSError, e:
    193         return {}
    194 
    195 
    196105class ConsoleUI(component.Component):
    197106    def __init__(self, args=None, cmds = None, daemon = None):
    198107        component.Component.__init__(self, "ConsoleUI", 2)
  • deluge/ui/gtkui/__init__.py

    diff --git a/deluge/ui/gtkui/__init__.py b/deluge/ui/gtkui/__init__.py
    index c5a539a..5b3edba 100644
    a b  
    1 from gtkui import start
     1from deluge.ui.ui import _UI
     2
     3class Gtk(_UI):
     4
     5    help = """Starts the Deluge GTK+ interface"""
     6    cmdline = """A GTK-based graphical user interface"""
     7
     8    def __init__(self, *args, **kwargs):
     9        super(Gtk, self).__init__("gtk", *args, **kwargs)
     10
     11    def start(self, args = None):
     12        from gtkui import GtkUI
     13        super(Gtk, self).start(args)
     14        GtkUI(self.args)
     15
     16def start():
     17    Gtk().start()
  • deluge/ui/gtkui/gtkui.py

    diff --git a/deluge/ui/gtkui/gtkui.py b/deluge/ui/gtkui/gtkui.py
    index 634c266..22f55c2 100644
    a b  
    7171import deluge.common
    7272import deluge.error
    7373
    74 from deluge.ui.ui import _UI
    75 
    76 class Gtk(_UI):
    77 
    78     help = """Starts the Deluge GTK+ interface"""
    79 
    80     def __init__(self, *args, **kwargs):
    81         super(Gtk, self).__init__("gtk", *args, **kwargs)
    82 
    83     def start(self, args = None):
    84         super(Gtk, self).start(args)
    85         GtkUI(self.args)
    86 
    87 def start():
    88     Gtk().start()
    8974
    9075DEFAULT_PREFS = {
    9176    "classic_mode": True,
  • deluge/ui/web/__init__.py

    diff --git a/deluge/ui/web/__init__.py b/deluge/ui/web/__init__.py
    index 28f53f2..a1ad237 100644
    a b  
    1 from web import start
    2  No newline at end of file
     1from web import Web
     2
     3def start():
     4    Web().start()
  • deluge/ui/web/web.py

    diff --git a/deluge/ui/web/web.py b/deluge/ui/web/web.py
    index 6ecbdfc..f862ebf 100644
    a b def __init__(self, args):  
    4848class Web(_UI):
    4949
    5050    help = """Starts the Deluge web interface"""
     51    cmdline = """A web-based interface (http://localhost:8112)"""
    5152
    5253    def __init__(self, *args, **kwargs):
    5354        super(Web, self).__init__("web", *args, **kwargs)
  • setup.py

    diff --git a/setup.py b/setup.py
    index 5a55d42..6960ac4 100644
    a b def run(self):  
    515515    "gui_scripts": [
    516516        "deluge = deluge.main:start_ui",
    517517        "deluge-gtk = deluge.ui.gtkui:start"
    518     ]
     518    ],
     519    "deluge.ui" : [
     520        "console = deluge.ui.console:Console",
     521        "web = deluge.ui.web:Web",
     522        "gtk = deluge.ui.gtkui:Gtk",
     523    ],
    519524}
    520525
    521526