[UI] Replace optparse with argparse for cmd arguments handling

optparse is deprecation and succeeded by argparse. See
https://www.python.org/dev/peps/pep-0389
This commit is contained in:
bendikro 2016-01-11 18:12:14 +01:00
parent aa82efd4f1
commit 7b54a2a1ee
36 changed files with 783 additions and 789 deletions

View File

@ -162,6 +162,28 @@ def osx_check():
return platform.system() == "Darwin"
def linux_check():
"""
Checks if the current platform is Linux
:returns: True or False
:rtype: bool
"""
return platform.system() == "Linux"
def get_os_version():
if windows_check():
return platform.win32_ver()
elif osx_check():
return platform.mac_ver()
elif linux_check():
return platform.linux_distribution()
else:
return (platform.release(), )
def get_pixmap(fname):
"""
Provides easy access to files in the deluge/ui/data/pixmaps folder within the Deluge egg

View File

@ -1,79 +0,0 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
import logging
import optparse
import os
import platform
import sys
import deluge.common
import deluge.configmanager
from deluge.log import setup_logger
def version_callback(option, opt_str, value, parser):
print(os.path.basename(sys.argv[0]) + ": " + deluge.common.get_version())
try:
from deluge._libtorrent import lt
print("libtorrent: %s" % lt.version)
except ImportError:
pass
print("Python: %s" % platform.python_version())
for version in (platform.linux_distribution(), platform.win32_ver(), platform.mac_ver(), ("n/a",)):
if filter(None, version): # pylint: disable=bad-builtin
print("OS: %s %s" % (platform.system(), " ".join(version)))
break
raise SystemExit
class CommonOptionParser(optparse.OptionParser):
def __init__(self, *args, **kwargs):
optparse.OptionParser.__init__(self, *args, **kwargs)
self.common_group = optparse.OptionGroup(self, _("Common Options"))
self.common_group.add_option("-v", "--version", action="callback", callback=version_callback,
help="Show program's version number and exit")
self.common_group.add_option("-c", "--config", dest="config", action="store", type="str",
help="Set the config folder location")
self.common_group.add_option("-l", "--logfile", dest="logfile", action="store", type="str",
help="Output to designated logfile instead of stdout")
self.common_group.add_option("-L", "--loglevel", dest="loglevel", action="store", type="str",
help="Set the log level: none, info, warning, error, critical, debug")
self.common_group.add_option("-q", "--quiet", dest="quiet", action="store_true", default=False,
help="Sets the log level to 'none', this is the same as `-L none`")
self.common_group.add_option("-r", "--rotate-logs", action="store_true", default=False,
help="Rotate logfiles.")
self.add_option_group(self.common_group)
def parse_args(self, *args):
options, args = optparse.OptionParser.parse_args(self, *args)
# Setup the logger
if options.quiet:
options.loglevel = "none"
if options.loglevel:
options.loglevel = options.loglevel.lower()
logfile_mode = 'w'
if options.rotate_logs:
logfile_mode = 'a'
# Setup the logger
setup_logger(level=options.loglevel, filename=options.logfile, filemode=logfile_mode)
if options.config:
if not deluge.configmanager.set_config_dir(options.config):
log = logging.getLogger(__name__)
log.error("There was an error setting the config dir! Exiting..")
sys.exit(1)
else:
if not os.path.exists(deluge.common.get_default_config_dir()):
os.makedirs(deluge.common.get_default_config_dir())
return options, args

View File

@ -15,7 +15,6 @@
"""Main starting point for Deluge. Contains the main() entry point."""
from __future__ import print_function
import optparse
import os
import sys
from logging import FileHandler, getLogger
@ -38,25 +37,27 @@ def start_ui():
# Setup the argument parser
parser = CommonOptionParser()
group = optparse.OptionGroup(parser, _("Default Options"))
group = parser.add_argument_group(_("Default Options"))
ui_entrypoints = dict([(entrypoint.name, entrypoint.load())
for entrypoint in pkg_resources.iter_entry_points('deluge.ui')])
cmd_help = ['The UI that you wish to launch. The UI choices are:']
max_len = 0
for k, v in ui_entrypoints.iteritems():
cmdline = getattr(v, 'cmdline', "")
max_len = max(max_len, len(cmdline))
cmd_help.extend(["%s -- %s" % (k, getattr(v, 'cmdline', "")) for k, v in ui_entrypoints.iteritems()])
parser.add_option("-u", "--ui", dest="ui",
choices=ui_entrypoints.keys(), help="\n\t ".join(cmd_help), action="store")
group.add_option("-a", "--args", dest="args",
help="Arguments to pass to UI, -a '--option args'", action="store", type="str")
group.add_option("-s", "--set-default-ui", dest="default_ui",
help="Sets the default UI to be run when no UI is specified", action="store", type="str")
parser.add_argument("-u", "--ui", action="store",
choices=ui_entrypoints.keys(), help="\n* ".join(cmd_help))
group.add_argument("-a", "--args", action="store",
help='Arguments to pass to the UI. Multiple args must be quoted, e.g. -a "--option args"')
group.add_argument("-s", "--set-default-ui", dest="default_ui", choices=ui_entrypoints.keys(),
help="Sets the default UI to be run when no UI is specified", action="store")
parser.add_option_group(group)
# Get the options and args from the OptionParser
(options, args) = parser.parse_args(deluge.common.unicode_argv()[1:])
options = parser.parse_args(deluge.common.unicode_argv()[1:])
config = deluge.configmanager.ConfigManager("ui.conf", DEFAULT_PREFS)
log = getLogger(__name__)
@ -69,7 +70,6 @@ def start_ui():
log.info("Deluge ui %s", deluge.common.get_version())
log.debug("options: %s", options)
log.debug("args: %s", args)
selected_ui = options.ui if options.ui else config["default_ui"]
@ -81,15 +81,13 @@ def start_ui():
if options.args:
import shlex
client_args.extend(shlex.split(options.args))
client_args.extend(args)
try:
ui = ui_entrypoints[selected_ui](skip_common=True)
ui = ui_entrypoints[selected_ui](parser=parser)
except KeyError as ex:
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)
except ImportError as ex:
import sys
import traceback
error_type, error_value, tb = sys.exc_info()
stack = traceback.extract_tb(tb)
@ -106,6 +104,31 @@ def start_ui():
ui.start(client_args)
def add_daemon_options(parser):
group = parser.add_argument_group('Daemon Options')
group.add_argument("-p", "--port", metavar="<port>", action="store", type=int,
help="The port the daemon will listen on")
group.add_argument("-i", "--interface", metavar="<iface>", dest="listen_interface",
help="Interface daemon will listen for bittorrent connections on, "
"this should be an IP address", action="store")
group.add_argument("-u", "--ui-interface", metavar="<iface>", action="store",
help="Interface daemon will listen for UI connections on, "
"this should be an IP address")
if not deluge.common.windows_check():
group.add_argument("-d", "--do-not-daemonize", dest="donot",
help="Do not daemonize", action="store_true", default=False)
group.add_argument("-P", "--pidfile", metavar="<pidfile>",
help="Use pidfile to store process id", action="store")
if not deluge.common.windows_check():
group.add_argument("-U", "--user", metavar="<user>", action="store",
help="User to switch to. Only use it when starting as root")
group.add_argument("-g", "--group", metavar="<group>", action="store",
help="Group to switch to. Only use it when starting as root")
group.add_argument("--read-only-config-keys",
help="List of comma-separated config keys that will not be modified by set_config RPC.",
action="store", type=str, default="")
def start_daemon(skip_start=False):
"""
Entry point for daemon script
@ -124,38 +147,10 @@ def start_daemon(skip_start=False):
warnings.filterwarnings('ignore', category=DeprecationWarning, module='twisted')
# Setup the argument parser
parser = CommonOptionParser(usage="%prog [options] [actions]")
parser = CommonOptionParser()
add_daemon_options(parser)
group = optparse.OptionGroup(parser, _("Daemon Options"))
group.add_option("-p", "--port", dest="port",
help="Port daemon will listen on", action="store", type="int")
group.add_option("-i", "--interface", dest="listen_interface",
help="Interface daemon will listen for bittorrent connections on, "
"this should be an IP address", metavar="IFACE",
action="store", type="str")
group.add_option("-u", "--ui-interface", dest="ui_interface",
help="Interface daemon will listen for UI connections on, this should be "
"an IP address", metavar="IFACE", action="store", type="str")
if not (deluge.common.windows_check() or deluge.common.osx_check()):
group.add_option("-d", "--do-not-daemonize", dest="donot",
help="Do not daemonize", action="store_true", default=False)
group.add_option("-P", "--pidfile", dest="pidfile",
help="Use pidfile to store process id", action="store", type="str")
if not deluge.common.windows_check():
group.add_option("-U", "--user", dest="user",
help="User to switch to. Only use it when starting as root", action="store", type="str")
group.add_option("-g", "--group", dest="group",
help="Group to switch to. Only use it when starting as root", action="store", type="str")
group.add_option("--read-only-config-keys",
help="List of comma-separated config keys that will not be modified by set_config RPC.",
action="store", type="str", default="")
group.add_option("--profile", dest="profile", action="store_true", default=False,
help="Profiles the daemon")
parser.add_option_group(group)
# Get the options and args from the OptionParser
(options, args) = parser.parse_args()
options = parser.parse_args()
# Check for any daemons running with this same config
from deluge.core.daemon import check_running_daemon

View File

@ -10,42 +10,31 @@ from __future__ import print_function
import os
import sys
from argparse import ArgumentParser
from datetime import datetime
from optparse import OptionParser
import deluge.common
parser = OptionParser()
parser.add_option("-n", "--name", dest="name", help="plugin name")
parser.add_option("-m", "--module-name", dest="module", help="plugin name")
parser.add_option("-p", "--basepath", dest="path", help="base path")
parser.add_option("-a", "--author-name", dest="author_name", help="author name,for the GPL header")
parser.add_option("-e", "--author-email", dest="author_email", help="author email,for the GPL header")
parser.add_option("-u", "--url", dest="url", help="Homepage URL")
parser.add_option("-c", "--config", dest="configdir", help="location of deluge configuration")
parser = ArgumentParser()
parser.add_argument("-n", "--name", metavar="<plugin name>", required=True, help="Plugin name")
parser.add_argument("-m", "--module-name", metavar="<module name>", help="Module name")
parser.add_argument("-p", "--basepath", metavar="<path>", required=True, help="Base path")
parser.add_argument("-a", "--author-name", metavar="<author name>", required=True,
help="Author name,for the GPL header")
parser.add_argument("-e", "--author-email", metavar="<author email>", required=True,
help="Author email,for the GPL header")
parser.add_argument("-u", "--url", metavar="<URL>", help="Homepage URL")
parser.add_argument("-c", "--config", metavar="<Config dir>", dest="configdir", help="Location of deluge configuration")
(options, args) = parser.parse_args()
options = parser.parse_args()
def create_plugin():
if not options.name:
print("--name is mandatory , use -h for more info")
return
if not options.path:
print("--basepath is mandatory , use -h for more info")
return
if not options.author_email:
print("--author-email is mandatory , use -h for more info")
return
if not options.author_email:
print("--author-name is mandatory , use -h for more info")
return
if not options.url:
options.url = ""
if not os.path.exists(options.path):
if not os.path.exists(options.basepath):
print("basepath does not exist")
return
@ -57,9 +46,9 @@ def create_plugin():
real_name = options.name
name = real_name.replace(" ", "_")
safe_name = name.lower()
if options.module:
safe_name = options.module.lower()
plugin_base = os.path.realpath(os.path.join(options.path, name))
if options.module_name:
safe_name = options.module_name.lower()
plugin_base = os.path.realpath(os.path.join(options.basepath, name))
deluge_namespace = os.path.join(plugin_base, "deluge")
plugins_namespace = os.path.join(deluge_namespace, "plugins")
src = os.path.join(plugins_namespace, safe_name)
@ -121,16 +110,16 @@ def create_plugin():
CORE = """
import logging
from deluge.plugins.pluginbase import CorePluginBase
import deluge.component as component
import deluge.configmanager
from deluge.core.rpcserver import export
DEFAULT_PREFS = {
"test":"NiNiNi"
"test": "NiNiNi"
}
log = logging.getLogger(__name__)
class Core(CorePluginBase):
def enable(self):
self.config = deluge.configmanager.ConfigManager("%(safe_name)s.conf", DEFAULT_PREFS)
@ -157,22 +146,25 @@ class Core(CorePluginBase):
INIT = """
from deluge.plugins.init import PluginInitBase
class CorePlugin(PluginInitBase):
def __init__(self, plugin_name):
from core import Core as _plugin_cls
self._plugin_cls = _plugin_cls
from core import Core as PluginClass
self._plugin_cls = PluginClass
super(CorePlugin, self).__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from gtkui import GtkUI as _plugin_cls
self._plugin_cls = _plugin_cls
from gtkui import GtkUI as PluginClass
self._plugin_cls = PluginClass
super(GtkUIPlugin, self).__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from webui import WebUI as _plugin_cls
self._plugin_cls = _plugin_cls
from webui import WebUI as PluginClass
self._plugin_cls = PluginClass
super(WebUIPlugin, self).__init__(plugin_name)
"""
@ -201,8 +193,8 @@ setup(
long_description=__long_description__ if __long_description__ else __description__,
packages=find_packages(),
namespace_packages = ["deluge", "deluge.plugins"],
package_data = __pkg_data__,
namespace_packages=["deluge", "deluge.plugins"],
package_data=__pkg_data__,
entry_points=\"\"\"
[deluge.plugin.core]
@ -218,7 +210,8 @@ setup(
COMMON = """
def get_resource(filename):
import pkg_resources, os
import pkg_resources
import os
return pkg_resources.resource_filename("deluge.plugins.%(safe_name)s",
os.path.join("data", filename))
"""
@ -230,12 +223,12 @@ import logging
from deluge.ui.client import client
from deluge.plugins.pluginbase import GtkPluginBase
import deluge.component as component
import deluge.common
from common import get_resource
log = logging.getLogger(__name__)
class GtkUI(GtkPluginBase):
def enable(self):
self.glade = gtk.glade.XML(get_resource("config.glade"))
@ -252,7 +245,7 @@ class GtkUI(GtkPluginBase):
def on_apply_prefs(self):
log.debug("applying prefs for %(name)s")
config = {
"test":self.glade.get_widget("txt_test").get_text()
"test": self.glade.get_widget("txt_test").get_text()
}
client.%(safe_name)s.set_config(config)
@ -296,13 +289,13 @@ GLADE = """<?xml version="1.0" encoding="UTF-8" standalone="no"?>
WEBUI = """
import logging
from deluge.ui.client import client
from deluge import component
from deluge.plugins.pluginbase import WebPluginBase
from common import get_resource
log = logging.getLogger(__name__)
class WebUI(WebPluginBase):
scripts = [get_resource("%(safe_name)s.js")]

137
deluge/ui/baseargparser.py Normal file
View File

@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
#
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
#
# This file is part of Deluge and is licensed under GNU General Public License 3.0, or later, with
# the additional special exception to link portions of this program with the OpenSSL library.
# See LICENSE for more details.
#
import argparse
import logging
import os
import platform
import sys
import textwrap
import deluge.common
import deluge.configmanager
import deluge.log
from deluge.log import setup_logger
def get_version():
version_str = "%s\n" % (deluge.common.get_version())
try:
from deluge._libtorrent import lt
version_str += "libtorrent: %s\n" % lt.version
except ImportError:
pass
version_str += "Python: %s\n" % platform.python_version()
version_str += "OS: %s %s\n" % (platform.system(), " ".join(deluge.common.get_os_version()))
return version_str
class DelugeTextHelpFormatter(argparse.RawDescriptionHelpFormatter):
"""Help message formatter which retains formatting of all help text.
"""
def _split_lines(self, text, width):
"""
Do not remove whitespaces in string but still wrap text to max width.
Instead of passing the entire text to textwrap.wrap, split and pass each
line instead. This way list formatting is not mangled by textwrap.wrap.
"""
wrapped_lines = []
for l in text.splitlines():
wrapped_lines.extend(textwrap.wrap(l, width))
return wrapped_lines
def _format_action_invocation(self, action):
"""
Combines the options with comma and displays the argument
value only once instead of after both options.
Instead of: -s <arg>, --long-opt <arg>
Show : -s, --long-opt <arg>
"""
if not action.option_strings:
metavar, = self._metavar_formatter(action, action.dest)(1)
return metavar
else:
parts = []
# if the Optional doesn't take a value, format is:
# -s, --long
if action.nargs == 0:
parts.extend(action.option_strings)
# if the Optional takes a value, format is:
# -s, --long ARGS
else:
default = action.dest.upper()
args_string = self._format_args(action, default)
opt = ", ".join(action.option_strings)
parts.append('%s %s' % (opt, args_string))
return ', '.join(parts)
class HelpAction(argparse._HelpAction):
def __call__(self, parser, namespace, values, option_string=None):
if hasattr(parser, "subparser"):
subparser = getattr(parser, "subparser")
# If -h on a subparser is given, the subparser will exit after help message
subparser.parse_args()
parser.print_help()
parser.exit()
class BaseArgParser(argparse.ArgumentParser):
def __init__(self, *args, **kwargs):
if "formatter_class" not in kwargs:
kwargs["formatter_class"] = lambda prog: DelugeTextHelpFormatter(prog, max_help_position=33, width=90)
super(BaseArgParser, self).__init__(*args, add_help=False, **kwargs)
self.group = self.add_argument_group('Common Options')
self.group.add_argument('--version', action='version', version='%(prog)s ' + get_version(),
help="Show program's version number and exit")
self.group.add_argument("-c", "--config", action="store", metavar='<config>',
help="Set the config directory path")
self.group.add_argument("-l", "--logfile", action="store", metavar='<logfile>',
help="Output to designated logfile instead of stdout")
self.group.add_argument("-L", "--loglevel", action="store", choices=deluge.log.levels, metavar='<level>',
help="Set the log level: %s" % ", ".join(deluge.log.levels))
self.group.add_argument("-q", "--quiet", action="store_true", default=False,
help="Sets the log level to 'none', this is the same as `-L none`")
self.group.add_argument("-r", "--rotate-logs", action="store_true", default=False,
help="Rotate logfiles.")
self.group.add_argument("-h", "--help", action=HelpAction, help='Show this help message and exit')
def parse_args(self, *args):
options, remaining = super(BaseArgParser, self).parse_known_args(*args)
options.remaining = remaining
# Setup the logger
if options.quiet:
options.loglevel = "none"
if options.loglevel:
options.loglevel = options.loglevel.lower()
logfile_mode = 'w'
if options.rotate_logs:
logfile_mode = 'a'
# Setup the logger
setup_logger(level=options.loglevel, filename=options.logfile, filemode=logfile_mode)
if options.config:
if not deluge.configmanager.set_config_dir(options.config):
log = logging.getLogger(__name__)
log.error("There was an error setting the config dir! Exiting..")
sys.exit(1)
else:
if not os.path.exists(deluge.common.get_default_config_dir()):
os.makedirs(deluge.common.get_default_config_dir())
return options

View File

@ -379,14 +379,14 @@ class DaemonSSLProxy(DaemonProxy):
def authenticate(self, username, password):
log.debug("%s.authenticate: %s", self.__class__.__name__, username)
self.login_deferred = defer.Deferred()
login_deferred = defer.Deferred()
d = self.call("daemon.login", username, password,
client_version=deluge.common.get_version())
d.addCallback(self.__on_login, username)
d.addErrback(self.__on_login_fail)
return self.login_deferred
d.addCallbacks(self.__on_login, self.__on_login_fail, callbackArgs=[username, login_deferred],
errbackArgs=[login_deferred])
return login_deferred
def __on_login(self, result, username):
def __on_login(self, result, username, login_deferred):
log.debug("__on_login called: %s %s", username, result)
self.username = username
self.authentication_level = result
@ -399,11 +399,10 @@ class DaemonSSLProxy(DaemonProxy):
self.__on_auth_levels_mappings
)
self.login_deferred.callback(result)
login_deferred.callback(result)
def __on_login_fail(self, result):
log.debug("_on_login_fail(): %s", result.value)
self.login_deferred.errback(result)
def __on_login_fail(self, result, login_deferred):
login_deferred.errback(result)
def __on_auth_levels_mappings(self, result):
auth_levels_mapping, auth_levels_mapping_reverse = result
@ -549,7 +548,12 @@ class Client(object):
d = self._daemon_proxy.connect(host, port)
def on_connected(daemon_version):
log.debug("on_connected. Daemon version: %s", daemon_version)
return daemon_version
def on_connect_fail(reason):
log.debug("on_connect_fail: %s", reason)
self.disconnect()
return reason
@ -561,11 +565,6 @@ class Client(object):
log.debug("Failed to authenticate: %s", reason.value)
return reason
def on_connected(daemon_version):
log.debug("Client.connect.on_connected. Daemon version: %s",
daemon_version)
return daemon_version
def authenticate(daemon_version, username, password):
if not username and host in ("127.0.0.1", "localhost"):
# No username provided and it's localhost, so attempt to get credentials from auth file.

View File

@ -7,7 +7,7 @@
# See LICENSE for more details.
#
UI_PATH = __path__[0]
UI_PATH = __path__[0] # NOQA Ignore 'E402 module level import not at top of file'
from deluge.ui.console.console import Console

View File

@ -7,6 +7,8 @@
# See LICENSE for more details.
#
import re
from deluge.ui.console.modes import format_utils
try:
@ -228,3 +230,39 @@ def parse_color_string(s, encoding="UTF-8"):
# There was no color scheme so we add it with a 0 for white on black
ret = [(0, s)]
return ret
class ConsoleColorFormatter(object):
"""
Format help in a way suited to deluge Legacy mode - colors, format, indentation...
"""
replace_dict = {
"<torrent-id>": "{!green!}%s{!input!}",
"<torrent>": "{!green!}%s{!input!}",
"<command>": "{!green!}%s{!input!}",
"<state>": "{!yellow!}%s{!input!}",
"\\.\\.\\.": "{!yellow!}%s{!input!}",
"\\s\\*\\s": "{!blue!}%s{!input!}",
"(?<![\\-a-z])(-[a-zA-Z0-9])": "{!red!}%s{!input!}",
# "(\-[a-zA-Z0-9])": "{!red!}%s{!input!}",
"--[_\\-a-zA-Z0-9]+": "{!green!}%s{!input!}",
"(\\[|\\])": "{!info!}%s{!input!}",
"<tab>": "{!white!}%s{!input!}",
"[_A-Z]{3,}": "{!cyan!}%s{!input!}",
"<key>": "{!cyan!}%s{!input!}",
"<value>": "{!cyan!}%s{!input!}",
"usage:": "{!info!}%s{!input!}",
"<download-folder>": "{!yellow!}%s{!input!}",
"<torrent-file>": "{!green!}%s{!input!}"
}
def format_colors(self, string):
def r(repl):
return lambda s: repl % s.group()
for key, replacement in self.replace_dict.items():
string = re.sub(key, r(replacement), string)
return string

View File

@ -13,43 +13,45 @@
from __future__ import print_function
import logging
import sys
from twisted.internet import defer
import deluge.component as component
from deluge.error import DelugeError
from deluge.ui.client import client
from deluge.ui.console.colors import strip_colors
log = logging.getLogger(__name__)
class Commander:
class Commander(object):
def __init__(self, cmds, interactive=False):
self._commands = cmds
self.console = component.get("ConsoleUI")
self.interactive = interactive
def write(self, line):
print(strip_colors(line))
def do_command(self, cmd):
def do_command(self, cmd_line):
"""
Processes a command.
:param cmd: str, the command string
"""
if not cmd:
if not cmd_line:
return
cmd, _, line = cmd.partition(" ")
cmd, _, line = cmd_line.partition(" ")
try:
parser = self._commands[cmd].create_parser()
except KeyError:
self.write("{!error!}Unknown command: %s" % cmd)
return
args = self._commands[cmd].split(line)
try:
args = [cmd] + self._commands[cmd].split(line)
except ValueError as ex:
self.write("{!error!}Error parsing command: %s" % ex)
return
# Do a little hack here to print 'command --help' properly
parser._print_help = parser.print_help
@ -73,14 +75,20 @@ class Commander:
return
try:
options, args = parser.parse_args(args)
options = parser.parse_args(args=args)
except TypeError as ex:
self.write("{!error!}Error parsing options: %s" % ex)
import traceback
self.write("%s" % traceback.format_exc())
return
except Exception as ex:
self.write("{!error!} %s" % ex)
parser.print_help()
return
if not getattr(options, "_exit", False):
if not getattr(parser, "_exit", False):
try:
ret = self._commands[cmd].handle(*args, **options.__dict__)
ret = self._commands[cmd].handle(options)
except Exception as ex:
self.write("{!error!} %s" % ex)
log.exception(ex)
@ -89,50 +97,3 @@ class Commander:
return defer.succeed(True)
else:
return ret
def exec_args(self, args, host, port, username, password):
commands = []
if args:
# Multiple commands split by ";"
commands = [arg.strip() for arg in args.split(";")]
def on_connect(result):
def on_started(result):
def on_started(result):
def do_command(result, cmd):
return self.do_command(cmd)
d = defer.succeed(None)
for command in commands:
if command in ("quit", "exit"):
break
d.addCallback(do_command, command)
d.addCallback(do_command, "quit")
# We need to wait for the rpcs in start() to finish before processing
# any of the commands.
self.console.started_deferred.addCallback(on_started)
component.start().addCallback(on_started)
def on_connect_fail(reason):
if reason.check(DelugeError):
rm = reason.value.message
else:
rm = reason.getErrorMessage()
if host:
print("Could not connect to daemon: %s:%s\n %s" % (host, port, rm))
else:
print("Could not connect to localhost daemon\n %s" % rm)
self.do_command("quit")
if host:
d = client.connect(host, port, username, password)
else:
d = client.connect()
if not self.interactive:
if commands[0].startswith("connect"):
d = self.do_command(commands.pop(0))
elif "help" in commands:
self.do_command("help")
sys.exit(0)
d.addCallback(on_connect)
d.addErrback(on_connect_fail)

View File

@ -10,7 +10,6 @@
import base64
import os
from optparse import make_option
from urllib import url2pathname
from urlparse import urlparse
@ -23,20 +22,19 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Add a torrent"""
option_list = BaseCommand.option_list + (
make_option('-p', '--path', dest='path', help='download folder for torrent'),
)
"""Add torrents"""
usage = "Usage: add [-p <download-folder>] <torrent-file> [<torrent-file> ...]\n"\
" <torrent-file> arguments can be file paths, URLs or magnet uris"
def add_arguments(self, parser):
parser.add_argument("-p", "--path", dest="path", help="download folder for torrent")
parser.add_argument("torrents", metavar="<torrent>", nargs="+",
help="One or more torrent files, URLs or magnet URIs")
def handle(self, *args, **options):
def handle(self, options):
self.console = component.get("ConsoleUI")
t_options = {}
if options["path"]:
t_options["download_location"] = os.path.expanduser(options["path"])
if options.path:
t_options["download_location"] = os.path.expanduser(options.path)
def on_success(result):
if not result:
@ -49,22 +47,22 @@ class Command(BaseCommand):
# Keep a list of deferreds to make a DeferredList
deferreds = []
for arg in args:
if not arg.strip():
for torrent in options.torrents:
if not torrent.strip():
continue
if deluge.common.is_url(arg):
self.console.write("{!info!}Attempting to add torrent from url: %s" % arg)
deferreds.append(client.core.add_torrent_url(arg, t_options).addCallback(on_success).addErrback(
if deluge.common.is_url(torrent):
self.console.write("{!info!}Attempting to add torrent from url: %s" % torrent)
deferreds.append(client.core.add_torrent_url(torrent, t_options).addCallback(on_success).addErrback(
on_fail))
elif deluge.common.is_magnet(arg):
self.console.write("{!info!}Attempting to add torrent from magnet uri: %s" % arg)
deferreds.append(client.core.add_torrent_magnet(arg, t_options).addCallback(on_success).addErrback(
elif deluge.common.is_magnet(torrent):
self.console.write("{!info!}Attempting to add torrent from magnet uri: %s" % torrent)
deferreds.append(client.core.add_torrent_magnet(torrent, t_options).addCallback(on_success).addErrback(
on_fail))
else:
# Just a file
if urlparse(arg).scheme == "file":
arg = url2pathname(urlparse(arg).path)
path = os.path.abspath(os.path.expanduser(arg))
if urlparse(torrent).scheme == "file":
torrent = url2pathname(urlparse(torrent).path)
path = os.path.abspath(os.path.expanduser(torrent))
if not os.path.exists(path):
self.console.write("{!error!}%s doesn't exist!" % path)
continue

View File

@ -14,9 +14,8 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Show information about the disk cache"""
usage = "Usage: cache"
def handle(self, *args, **options):
def handle(self, options):
self.console = component.get("ConsoleUI")
def on_cache_status(status):

View File

@ -11,7 +11,6 @@
import cStringIO
import logging
import tokenize
from optparse import make_option
import deluge.component as component
import deluge.ui.console.colors as colors
@ -66,25 +65,28 @@ def simple_eval(source):
class Command(BaseCommand):
"""Show and set configuration values"""
option_list = BaseCommand.option_list + (
make_option("-s", "--set", action="store", nargs=2, dest="set", help="set value for key"),
)
usage = """Usage: config [key1 [key2 ...]]"
config --set key value"""
usage = "config [--set <key> <value>] [<key> [<key>...] ]"
def handle(self, *args, **options):
def add_arguments(self, parser):
set_group = parser.add_argument_group("setting a value")
set_group.add_argument("-s", "--set", action="store", metavar="<key>", help="set value for this key")
set_group.add_argument("values", metavar="<value>", nargs="+", help="Value to set")
get_group = parser.add_argument_group("getting values")
get_group.add_argument("keys", metavar="<keys>", nargs="*", help="one or more keys separated by space")
def handle(self, options):
self.console = component.get("ConsoleUI")
if options["set"]:
return self._set_config(*args, **options)
if options.set:
return self._set_config(options)
else:
return self._get_config(*args, **options)
return self._get_config(options)
def _get_config(self, *args, **options):
def _get_config(self, options):
def _on_get_config(config):
keys = sorted(config.keys())
s = ""
for key in keys:
if args and key not in args:
if key not in options.values:
continue
color = "{!white,black,bold!}"
value = config[key]
@ -107,10 +109,16 @@ class Command(BaseCommand):
return client.core.get_config().addCallback(_on_get_config)
def _set_config(self, *args, **options):
def _set_config(self, options):
config = component.get("CoreConfig")
key = options["set"][0]
val = simple_eval(options["set"][1] + " " .join(args))
key = options.set
val = " ".join(options.values)
try:
val = simple_eval(val)
except SyntaxError as ex:
self.console.write("{!error!}%s" % ex)
return
if key not in config.keys():
self.console.write("{!error!}The key '%s' is invalid!" % key)
@ -126,7 +134,7 @@ class Command(BaseCommand):
def on_set_config(result):
self.console.write("{!success!}Configuration value successfully updated.")
self.console.write("Setting %s to %s.." % (key, val))
self.console.write("Setting '%s' to '%s'" % (key, val))
return client.core.set_config({key: val}).addCallback(on_set_config)
def complete(self, text):

View File

@ -8,27 +8,41 @@
# See LICENSE for more details.
#
import logging
import deluge.component as component
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
log = logging.getLogger(__name__)
class Command(BaseCommand):
"""Connect to a new deluge server."""
usage = "Usage: connect <host[:port]> <username> <password>"
usage = "Usage: connect <host[:port]> [<username>] [<password>]"
def handle(self, host="127.0.0.1:58846", username="", password="", **options):
def add_arguments(self, parser):
parser.add_argument("host", help="host and port", metavar="<host[:port]>")
parser.add_argument("username", help="Username", metavar="<username>", nargs="?", default="")
parser.add_argument("password", help="Password", metavar="<password>", nargs="?", default="")
def add_parser(self, subparsers):
parser = subparsers.add_parser(self.name, help=self.__doc__, description=self.__doc__, prog="connect")
self.add_arguments(parser)
def handle(self, options):
self.console = component.get("ConsoleUI")
host = options.host
try:
host, port = host.split(":")
port = int(port)
except ValueError:
port = 58846
else:
port = int(port)
def do_connect():
d = client.connect(host, port, username, password)
d = client.connect(host, port, options.username, options.password)
def on_connect(result):
if self.console.interactive:
@ -39,17 +53,18 @@ class Command(BaseCommand):
try:
msg = result.value.exception_msg
except AttributeError:
msg = result.value.args[0]
msg = result.value.message
self.console.write("{!error!}Failed to connect to %s:%s with reason: %s" % (host, port, msg))
return result
d.addCallback(on_connect)
d.addErrback(on_connect_fail)
d.addCallbacks(on_connect, on_connect_fail)
return d
if client.connected():
def on_disconnect(result):
self.console.statusbars.update_statusbars()
if self.console.statusbars:
self.console.statusbars.update_statusbars()
return do_connect()
return client.disconnect().addCallback(on_disconnect)
else:

View File

@ -17,12 +17,14 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Enable and disable debugging"""
usage = "Usage: debug [on|off]"
def handle(self, state="", **options):
if state == "on":
def add_arguments(self, parser):
parser.add_argument("state", metavar="<on|off>", choices=["on", "off"], help="The new state")
def handle(self, options):
if options.state == "on":
deluge.log.set_logger_level("debug")
elif state == "off":
elif options.state == "off":
deluge.log.set_logger_level("error")
else:
component.get("ConsoleUI").write("{!error!}%s" % self.usage)

View File

@ -7,22 +7,26 @@
# See LICENSE for more details.
#
import logging
import deluge.component as component
from deluge.ui.console.main import BaseCommand
from deluge.ui.console.modes.alltorrents import AllTorrents
log = logging.getLogger(__name__)
class Command(BaseCommand):
"""Exit this mode and go into the more 'gui' like mode"""
usage = "Usage: gui"
"""Enable interactive mode"""
interactive_only = True
def handle(self, *args, **options):
def handle(self, options):
console = component.get("ConsoleUI")
try:
at = component.get("AllTorrents")
except KeyError:
at = AllTorrents(console.stdscr, console.encoding)
console.set_mode(at)
at._go_top = True
at.resume()

View File

@ -15,9 +15,8 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"Shutdown the deluge server"
usage = "Usage: halt"
def handle(self, *args, **options):
def handle(self, options):
self.console = component.get("ConsoleUI")
def on_shutdown(result):

View File

@ -8,27 +8,32 @@
# See LICENSE for more details.
#
import logging
from twisted.internet import defer
import deluge.component as component
from deluge.ui.console.main import BaseCommand
log = logging.getLogger(__name__)
class Command(BaseCommand):
"""displays help on other commands"""
usage = "Usage: help [command]"
def add_arguments(self, parser):
parser.add_argument("commands", metavar="<command>", nargs="*", help="One or more commands")
def handle(self, *args, **options):
def handle(self, options):
self.console = component.get("ConsoleUI")
self._commands = self.console._commands
deferred = defer.succeed(True)
if args:
for arg in args:
if options.commands:
for arg in options.commands:
try:
cmd = self._commands[arg]
except KeyError:
self.console.write("{!error!}Unknown command %r" % args[0])
self.console.write("{!error!}Unknown command %s" % arg)
continue
try:
parser = cmd.create_parser()
@ -38,8 +43,15 @@ class Command(BaseCommand):
self.console.write(" ")
else:
self.console.set_batch_write(True)
cmds_doc = ""
for cmd in sorted(self._commands):
self.console.write("{!info!}" + cmd + "{!input!} - " + self._commands[cmd].__doc__ or '')
if cmd in self._commands[cmd].aliases:
continue
parser = self._commands[cmd].create_parser()
cmd_doc = "{!info!}" + "%-9s" % self._commands[cmd].name_with_alias + "{!input!} - "\
+ self._commands[cmd].__doc__ + "\n " + parser.format_usage() or ''
cmds_doc += parser.formatter.format_colors(cmd_doc)
self.console.write(cmds_doc)
self.console.write(" ")
self.console.write("For help on a specific command, use '<command> --help'")
self.console.set_batch_write(False)

View File

@ -8,7 +8,6 @@
# See LICENSE for more details.
#
from optparse import make_option
from os.path import sep as dirsep
import deluge.common as common
@ -90,44 +89,51 @@ class Command(BaseCommand):
sort_help = "sort items. Possible keys: " + ", ".join(STATUS_KEYS)
option_list = BaseCommand.option_list + (
make_option("-v", "--verbose", action="store_true", default=False, dest="verbose",
help="Show more information per torrent."),
make_option("-d", "--detailed", action="store_true", default=False, dest="detailed",
help="Show more detailed information including files and peers."),
make_option("-s", "--state", action="store", dest="state",
help="Show torrents with state STATE: %s." % (", ".join(STATES))),
make_option("--sort", action="store", type="string", default="", dest="sort", help=sort_help),
make_option("--sort-reverse", action="store", type="string", default="", dest="sort_rev",
help="Same as --sort but items are in reverse order.")
)
epilog = """
You can give the first few characters of a torrent-id to identify the torrent.
usage = """Usage: info [-v | -d | -s <state>] [<torrent-id> [<torrent-id> ...]]
You can give the first few characters of a torrent-id to identify the torrent.
info * will list all torrents.
Tab Completion (info *pattern*<tab>):\n
| First press of <tab> will output up to 15 matches;
| hitting <tab> a second time, will print 15 more matches;
| and a third press will print all remaining matches.
| (To modify behaviour of third <tab>, set `third_tab_lists_all` to False)
"""
Tab Completion (info *pattern*<tab>):
| First press of <tab> will output up to 15 matches;
| hitting <tab> a second time, will print 15 more matches;
| and a third press will print all remaining matches.
| (To modify behaviour of third <tab>, set `third_tab_lists_all` to False)"""
def add_arguments(self, parser):
parser.add_argument("-v", "--verbose", action="store_true", default=False, dest="verbose",
help="Show more information per torrent.")
parser.add_argument("-d", "--detailed", action="store_true", default=False, dest="detailed",
help="Show more detailed information including files and peers.")
parser.add_argument("-s", "--state", action="store", dest="state",
help="Show torrents with state STATE: %s." % (", ".join(STATES)))
parser.add_argument("--sort", action="store", type=str, default="", dest="sort", help=self.sort_help)
parser.add_argument("--sort-reverse", action="store", type=str, default="", dest="sort_rev",
help="Same as --sort but items are in reverse order.")
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="*",
help="One or more torrent ids. If none is given, list all")
def handle(self, *args, **options):
def add_subparser(self, subparsers):
parser = subparsers.add_parser(self.name, prog=self.name, help=self.__doc__,
description=self.__doc__, epilog=self.epilog)
self.add_arguments(parser)
def handle(self, options):
self.console = component.get("ConsoleUI")
# Compile a list of torrent_ids to request the status of
torrent_ids = []
for arg in args:
torrent_ids.extend(self.console.match_torrent(arg))
if not args:
if options.torrent_ids:
for t_id in options.torrent_ids:
torrent_ids.extend(self.console.match_torrent(t_id))
else:
torrent_ids.extend(self.console.match_torrent(""))
def on_torrents_status(status):
# Print out the information for each torrent
sort_key = options["sort"]
sort_key = options.sort
sort_reverse = False
if not sort_key:
sort_key = options["sort_rev"]
sort_key = options.sort_rev
sort_reverse = True
if not sort_key:
sort_key = "name"
@ -138,19 +144,19 @@ class Command(BaseCommand):
sort_key = "name"
sort_reverse = False
for key, value in sorted(status.items(), key=lambda x: x[1].get(sort_key), reverse=sort_reverse):
self.show_info(key, status[key], options["verbose"], options["detailed"])
self.show_info(key, status[key], options.verbose, options.detailed)
def on_torrents_status_fail(reason):
self.console.write("{!error!}Error getting torrent info: %s" % reason)
status_dict = {"id": torrent_ids}
if options["state"]:
options["state"] = options["state"].capitalize()
if options["state"] in STATES:
status_dict["state"] = options["state"]
if options.state:
options.state = options.state.capitalize()
if options.state in STATES:
status_dict.state = options.state
else:
self.console.write("Invalid state: %s" % options["state"])
self.console.write("Invalid state: %s" % options.state)
self.console.write("Possible values are: %s." % (", ".join(STATES)))
return

View File

@ -8,8 +8,6 @@
# See LICENSE for more details.
#
from optparse import make_option
from twisted.internet import defer
import deluge.component as component
@ -35,20 +33,25 @@ torrent_options = {
class Command(BaseCommand):
"""Show and manage per-torrent options"""
option_list = BaseCommand.option_list + (
make_option("-s", "--set", action="store", nargs=2, dest="set", help="set value for key"),
)
usage = "Usage: manage <torrent-id> [<key1> [<key2> ...]]\n"\
" manage <torrent-id> --set <key> <value>"
usage = "manage <torrent-id> [--set <key> <value>] [<key> [<key>...] ]"
def handle(self, *args, **options):
def add_arguments(self, parser):
parser.add_argument("torrent", metavar="<torrent>",
help="an expression matched against torrent ids and torrent names")
set_group = parser.add_argument_group("setting a value")
set_group.add_argument("-s", "--set", action="store", metavar="<key>", help="set value for this key")
set_group.add_argument("values", metavar="<value>", nargs="+", help="Value to set")
get_group = parser.add_argument_group("getting values")
get_group.add_argument("keys", metavar="<keys>", nargs="*", help="one or more keys separated by space")
def handle(self, options):
self.console = component.get("ConsoleUI")
if options['set']:
return self._set_option(*args, **options)
if options.set:
return self._set_option(options)
else:
return self._get_option(*args, **options)
return self._get_option(options)
def _get_option(self, *args, **options):
def _get_option(self, options):
def on_torrents_status(status):
for torrentid, data in status.items():
@ -63,11 +66,10 @@ class Command(BaseCommand):
def on_torrents_status_fail(reason):
self.console.write('{!error!}Failed to get torrent data.')
torrent_ids = []
torrent_ids.extend(self.console.match_torrent(args[0]))
torrent_ids = self.console.match_torrent(options.torrent)
request_options = []
for opt in args[1:]:
for opt in options.values:
if opt not in torrent_options:
self.console.write('{!error!}Unknown torrent option: %s' % opt)
return
@ -77,16 +79,14 @@ class Command(BaseCommand):
request_options.append('name')
d = client.core.get_torrents_status({"id": torrent_ids}, request_options)
d.addCallback(on_torrents_status)
d.addErrback(on_torrents_status_fail)
d.addCallbacks(on_torrents_status, on_torrents_status_fail)
return d
def _set_option(self, *args, **options):
def _set_option(self, options):
deferred = defer.Deferred()
torrent_ids = []
torrent_ids.extend(self.console.match_torrent(args[0]))
key = options["set"][0]
val = options["set"][1] + " " .join(args[1:])
key = options.set
val = " " .join(options.values)
torrent_ids = self.console.match_torrent(options.torrent)
if key not in torrent_options:
self.console.write("{!error!}The key '%s' is invalid!" % key)

View File

@ -7,43 +7,43 @@
# See LICENSE for more details.
#
import logging
import os.path
import deluge.component as component
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
log = logging.getLogger(__name__)
class Command(BaseCommand):
"""Move torrents' storage location"""
usage = "Usage: move <torrent-id> [<torrent-id> ...] <path>"
def handle(self, *args, **options):
def add_arguments(self, parser):
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="+", help="One or more torrent ids")
parser.add_argument("path", metavar="<path>", help="The path to move the torrents to")
def handle(self, options):
self.console = component.get("ConsoleUI")
if len(args) < 2:
self.console.write(self.usage)
return
path = args[-1]
if os.path.exists(path) and not os.path.isdir(path):
self.console.write("{!error!}Cannot Move Download Folder: %s exists and is not a directory" % path)
if os.path.exists(options.path) and not os.path.isdir(options.path):
self.console.write("{!error!}Cannot Move Download Folder: %s exists and is not a directory" % options.path)
return
ids = []
for i in args[:-1]:
ids.extend(self.console.match_torrent(i))
names = []
for i in ids:
names.append(self.console.get_torrent_name(i))
namestr = ", ".join(names)
for t_id in options.torrent_ids:
tid = self.console.match_torrent(t_id)
ids.extend(tid)
names.append(self.console.get_torrent_name(tid))
def on_move(res):
self.console.write("Moved \"%s\" to %s" % (namestr, path))
msg = "Moved \"%s\" to %s" % (", ".join(names), options.path)
self.console.write(msg)
log.info(msg)
d = client.core.move_storage(ids, path)
d = client.core.move_storage(ids, options.path)
d.addCallback(on_move)
return d
@ -81,5 +81,4 @@ class Command(BaseCommand):
if os.path.isdir(p):
p += "/"
ret.append(p)
return ret

View File

@ -14,21 +14,22 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Pause a torrent"""
usage = "Usage: pause [ * | <torrent-id> [<torrent-id> ...] ]"
"""Pause torrents"""
usage = "pause [ * | <torrent-id> [<torrent-id> ...] ]"
def handle(self, *args, **options):
def add_arguments(self, parser):
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="+",
help="One or more torrent ids. '*' pauses all torrents")
def handle(self, options):
self.console = component.get("ConsoleUI")
if len(args) == 0:
self.console.write(self.usage)
return
if len(args) > 0 and args[0].lower() == '*':
if options.torrent_ids[0] == "*":
client.core.pause_session()
return
torrent_ids = []
for arg in args:
for arg in options.torrent_ids:
torrent_ids.extend(self.console.match_torrent(arg))
if torrent_ids:

View File

@ -7,8 +7,6 @@
# See LICENSE for more details.
#
from optparse import make_option
import deluge.component as component
import deluge.configmanager
from deluge.ui.client import client
@ -16,41 +14,28 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Manage plugins with this command"""
option_list = BaseCommand.option_list + (
make_option("-l", "--list", action="store_true", default=False, dest="list",
help="Lists available plugins"),
make_option("-s", "--show", action="store_true", default=False, dest="show",
help="Shows enabled plugins"),
make_option("-e", "--enable", dest="enable",
help="Enables a plugin"),
make_option("-d", "--disable", dest="disable",
help="Disables a plugin"),
make_option("-r", "--reload", action="store_true", default=False, dest="reload",
help="Reload list of available plugins"),
make_option("-i", "--install", dest="plugin_file",
help="Install a plugin from an .egg file"),
)
usage = """Usage: plugin [ -l | --list ]
plugin [ -s | --show ]
plugin [ -e | --enable ] <plugin-name>
plugin [ -d | --disable ] <plugin-name>
plugin [ -i | --install ] <plugin-file>
plugin [ -r | --reload]"""
"""Manage plugins"""
def handle(self, *args, **options):
def add_arguments(self, parser):
parser.add_argument("-l", "--list", action="store_true", default=False, dest="list",
help="Lists available plugins")
parser.add_argument("-s", "--show", action="store_true", default=False, dest="show",
help="Shows enabled plugins")
parser.add_argument("-e", "--enable", dest="enable", nargs="+", help="Enables a plugin")
parser.add_argument("-d", "--disable", dest="disable", nargs="+", help="Disables a plugin")
parser.add_argument("-r", "--reload", action="store_true", default=False, dest="reload",
help="Reload list of available plugins")
parser.add_argument("-i", "--install", help="Install a plugin from an .egg file")
def handle(self, options):
self.console = component.get("ConsoleUI")
if len(args) == 0 and not any(options.values()):
self.console.write(self.usage)
return
if options["reload"]:
if options.reload:
client.core.pluginmanager.rescan_plugins()
self.console.write("{!green!}Plugin list successfully reloaded")
return
elif options["list"]:
elif options.list:
def on_available_plugins(result):
self.console.write("{!info!}Available Plugins:")
for p in result:
@ -58,7 +43,7 @@ class Command(BaseCommand):
return client.core.get_available_plugins().addCallback(on_available_plugins)
elif options["show"]:
elif options.show:
def on_enabled_plugins(result):
self.console.write("{!info!}Enabled Plugins:")
for p in result:
@ -66,50 +51,42 @@ class Command(BaseCommand):
return client.core.get_enabled_plugins().addCallback(on_enabled_plugins)
elif options["enable"]:
elif options.enable:
def on_available_plugins(result):
plugins = {}
for p in result:
plugins[p.lower()] = p
p_args = [options["enable"]] + list(args)
for arg in p_args:
for arg in options.enable:
if arg.lower() in plugins:
client.core.enable_plugin(plugins[arg.lower()])
return client.core.get_available_plugins().addCallback(on_available_plugins)
elif options["disable"]:
elif options.disable:
def on_enabled_plugins(result):
plugins = {}
for p in result:
plugins[p.lower()] = p
p_args = [options["disable"]] + list(args)
for arg in p_args:
for arg in options.disable:
if arg.lower() in plugins:
client.core.disable_plugin(plugins[arg.lower()])
return client.core.get_enabled_plugins().addCallback(on_enabled_plugins)
elif options["plugin_file"]:
filepath = options["plugin_file"]
elif options.install:
import os.path
import base64
import shutil
filepath = options.install
if not os.path.exists(filepath):
self.console.write("{!error!}Invalid path: %s" % filepath)
return
config_dir = deluge.configmanager.get_config_dir()
filename = os.path.split(filepath)[1]
shutil.copyfile(
filepath,
os.path.join(config_dir, "plugins", filename))
shutil.copyfile(filepath, os.path.join(config_dir, "plugins", filename))
client.core.rescan_plugins()

View File

@ -15,11 +15,11 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Exit from the client."""
"""Exit the client."""
aliases = ["exit"]
interactive_only = True
def handle(self, *args, **options):
def handle(self, options):
if client.connected():
def on_disconnect(result):
reactor.stop()

View File

@ -14,20 +14,20 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Forces a recheck of the torrent data"""
usage = "Usage: recheck [ * | <torrent-id> [<torrent-id> ...] ]"
usage = "recheck [ * | <torrent-id> [<torrent-id> ...] ]"
def handle(self, *args, **options):
def add_arguments(self, parser):
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="+", help="One or more torrent ids")
def handle(self, options):
self.console = component.get("ConsoleUI")
if len(args) == 0:
self.console.write(self.usage)
return
if len(args) > 0 and args[0].lower() == "*":
if options.torrent_ids[0] == "*":
client.core.force_recheck(self.console.match_torrent(""))
return
torrent_ids = []
for arg in args:
for arg in options.torrent_ids:
torrent_ids.extend(self.console.match_torrent(arg))
if torrent_ids:

View File

@ -14,22 +14,23 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Resume a torrent"""
usage = "Usage: resume [ * | <torrent-id> [<torrent-id> ...] ]"
"""Resume torrents"""
usage = "resume [ * | <torrent-id> [<torrent-id> ...] ]"
def handle(self, *args, **options):
def add_arguments(self, parser):
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="+",
help="One or more torrent ids. '*' resumes all torrents")
def handle(self, options):
self.console = component.get("ConsoleUI")
if len(args) == 0:
self.console.write(self.usage)
return
if len(args) > 0 and args[0] == "*":
if options.torrent_ids[0] == "*":
client.core.resume_session()
return
torrent_ids = []
for arg in args:
torrent_ids.extend(self.console.match_torrent(arg))
for t_id in options.torrent_ids:
torrent_ids.extend(self.console.match_torrent(t_id))
if torrent_ids:
return client.core.resume_torrent(torrent_ids)

View File

@ -8,8 +8,6 @@
# See LICENSE for more details.
#
from optparse import make_option
import deluge.component as component
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
@ -17,21 +15,16 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Remove a torrent"""
usage = "Usage: rm <torrent-id>"
aliases = ["del"]
option_list = BaseCommand.option_list + (
make_option("--remove_data", action="store_true", default=False,
help="remove the torrent's data"),
)
def add_arguments(self, parser):
parser.add_argument("--remove_data", action="store_true", default=False, help="remove the torrent's data")
parser.add_argument("torrent_ids", metavar="<torrent-id>", nargs="+", help="One or more torrent ids")
def handle(self, *args, **options):
def handle(self, options):
self.console = component.get("ConsoleUI")
if len(args) == 0:
self.console.write(self.usage)
torrent_ids = []
for arg in args:
for arg in options.torrent_ids:
torrent_ids.extend(self.console.match_torrent(arg))
def on_removed_finished(errors):
@ -40,7 +33,7 @@ class Command(BaseCommand):
for t_id, e_msg in errors:
self.console.write("Error removing torrent %s : %s" % (t_id, e_msg))
d = client.core.remove_torrents(torrent_ids, options["remove_data"])
d = client.core.remove_torrents(torrent_ids, options.remove_data)
d.addCallback(on_removed_finished)
def complete(self, line):

View File

@ -7,7 +7,7 @@
# See LICENSE for more details.
#
from optparse import make_option
import logging
from twisted.internet import defer
@ -16,44 +16,34 @@ from deluge.common import TORRENT_STATE, fspeed
from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
log = logging.getLogger(__name__)
class Command(BaseCommand):
"""Shows a various status information from the daemon."""
option_list = BaseCommand.option_list + (
make_option("-r", "--raw", action="store_true", default=False, dest="raw",
help="Don't format upload/download rates in KiB/s \
(useful for scripts that want to do their own parsing)"),
make_option("-n", "--no-torrents", action="store_false", default=True, dest="show_torrents",
help="Don't show torrent status (this will make the command a bit faster)"),
)
usage = "Usage: status [-r] [-n]"
def add_arguments(self, parser):
parser.add_argument("-r", "--raw", action="store_true", default=False, dest="raw",
help="Don't format upload/download rates in KiB/s \
(useful for scripts that want to do their own parsing)")
parser.add_argument("-n", "--no-torrents", action="store_false", default=True, dest="show_torrents",
help="Don't show torrent status (this will make the command a bit faster)")
def handle(self, *args, **options):
def handle(self, options):
self.console = component.get("ConsoleUI")
self.status = None
self.connections = None
if options["show_torrents"]:
self.torrents = None
else:
self.torrents = -2
self.raw = options["raw"]
self.torrents = 1 if options.show_torrents else 0
self.raw = options.raw
def on_session_status(status):
self.status = status
if self.status is not None and self.connections is not None and self.torrents is not None:
self.print_status()
def on_torrents_status(status):
self.torrents = status
if self.status is not None and self.connections is not None and self.torrents is not None:
self.print_status()
def on_torrents_status_fail(reason):
self.torrents = -1
if self.status is not None and self.connections is not None and self.torrents is not None:
self.print_status()
log.warn("Failed to retrieve session status: %s", reason)
self.torrents = -2
deferreds = []
@ -61,15 +51,15 @@ class Command(BaseCommand):
ds.addCallback(on_session_status)
deferreds.append(ds)
if options["show_torrents"]:
if options.show_torrents:
dt = client.core.get_torrents_status({}, ["state"])
dt.addCallback(on_torrents_status)
dt.addErrback(on_torrents_status_fail)
deferreds.append(dt)
return defer.DeferredList(deferreds)
return defer.DeferredList(deferreds).addCallback(self.print_status)
def print_status(self):
def print_status(self, *args):
self.console.set_batch_write(True)
if self.raw:
self.console.write("{!info!}Total upload: %f" % self.status["payload_upload_rate"])
@ -78,12 +68,12 @@ class Command(BaseCommand):
self.console.write("{!info!}Total upload: %s" % fspeed(self.status["payload_upload_rate"]))
self.console.write("{!info!}Total download: %s" % fspeed(self.status["payload_download_rate"]))
self.console.write("{!info!}DHT Nodes: %i" % self.status["dht_nodes"])
self.console.write("{!info!}Total connections: %i" % self.connections)
if self.torrents == -1:
self.console.write("{!error!}Error getting torrent info")
elif self.torrents != -2:
self.console.write("{!info!}Total torrents: %i" % len(self.torrents))
if isinstance(self.torrents, int):
if self.torrents == -2:
self.console.write("{!error!}Error getting torrent info")
else:
self.console.write("{!info!}Total torrents: %i" % len(self.torrents))
state_counts = {}
for state in TORRENT_STATE:
state_counts[state] = 0

View File

@ -15,15 +15,17 @@ from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
"""Update tracker for torrent(s)"""
usage = "Usage: update_tracker [ * | <torrent-id> [<torrent-id> ...] ]"
usage = "update_tracker [ * | <torrent-id> [<torrent-id> ...] ]"
aliases = ['reannounce']
def handle(self, *args, **options):
def add_arguments(self, parser):
parser.add_argument('torrent_ids', metavar="<torrent-id>", nargs='+',
help='One or more torrent ids. "*" updates all torrents')
def handle(self, options):
self.console = component.get("ConsoleUI")
if len(args) == 0:
self.console.write(self.usage)
return
if len(args) > 0 and args[0].lower() == '*':
args = options.torrent_ids
if options.torrent_ids[0] == "*":
args = [""]
torrent_ids = []

View File

@ -8,29 +8,36 @@
# See LICENSE for more details.
#
import optparse
import os
import sys
from deluge.ui.baseargparser import DelugeTextHelpFormatter
from deluge.ui.console import UI_PATH
from deluge.ui.ui import UI
def load_commands(command_dir, exclude=[]):
#
# Note: Cannot import from console.main here because it imports the twisted reactor.
# Console is imported from console/__init__.py loaded by the script entry points
# defined in setup.py
#
def load_commands(command_dir):
def get_command(name):
return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, {}, ['Command']), 'Command')()
try:
commands = []
for filename in os.listdir(command_dir):
if filename.split('.')[0] in exclude or filename.startswith('_'):
if filename.startswith('_'):
continue
if not (filename.endswith('.py') or filename.endswith('.pyc')):
continue
cmd = get_command(filename.split('.')[len(filename.split('.')) - 2])
aliases = [filename.split('.')[len(filename.split('.')) - 2]]
aliases.extend(cmd.aliases)
for a in aliases:
cmd._name = filename.split('.')[len(filename.split('.')) - 2]
names = [cmd._name]
names.extend(cmd.aliases)
for a in names:
commands.append((a, cmd))
return dict(commands)
except OSError:
@ -44,61 +51,29 @@ class Console(UI):
def __init__(self, *args, **kwargs):
super(Console, self).__init__("console", *args, **kwargs)
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.")
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")
self.parser.add_option_group(group)
group = self.parser.add_argument_group(_("Console Options"), "These daemon connect options will be "
"used for commands, or if console ui autoconnect is enabled.")
group.add_argument("-d", "--daemon", dest="daemon_addr", required=False, default="127.0.0.1")
group.add_argument("-p", "--port", dest="daemon_port", type=int, required=False, default="58846")
group.add_argument("-U", "--username", dest="daemon_user", required=False)
group.add_argument("-P", "--password", dest="daemon_pass", required=False)
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)
# To properly print help message for the console commands ( e.g. deluge-console info -h),
# we add a subparser for each command which will trigger the help/usage when given
from deluge.ui.console.main import ConsoleCommandParser # import here because (see top)
self.console_parser = ConsoleCommandParser(parents=[self.parser], add_help=False,
formatter_class=lambda prog:
DelugeTextHelpFormatter(prog, max_help_position=33, width=90))
self.parser.subparser = self.console_parser
subparsers = self.console_parser.add_subparsers(title="Console commands", help="Description", dest="commands",
description="The following console commands are available:",
metavar="command")
self.console_cmds = load_commands(os.path.join(UI_PATH, "commands"))
for c in sorted(self.console_cmds):
self.console_cmds[c].add_subparser(subparsers)
def start(self, args=None):
from main import ConsoleUI
super(Console, self).start(args)
ConsoleUI(self.args, self.cmds, (self.options.daemon_addr,
self.options.daemon_port, self.options.daemon_user,
self.options.daemon_pass))
from deluge.ui.console.main import ConsoleUI # import here because (see top)
ConsoleUI(self.options, self.console_cmds)

View File

@ -10,10 +10,9 @@
from __future__ import print_function
import argparse
import locale
import logging
import optparse
import re
import shlex
import sys
@ -21,8 +20,10 @@ from twisted.internet import defer, reactor
import deluge.common
import deluge.component as component
from deluge.error import DelugeError
from deluge.ui.client import client
from deluge.ui.console import colors
from deluge.ui.console.colors import ConsoleColorFormatter
from deluge.ui.console.eventlog import EventLog
from deluge.ui.console.statusbars import StatusBars
from deluge.ui.coreconfig import CoreConfig
@ -31,84 +32,32 @@ from deluge.ui.sessionproxy import SessionProxy
log = logging.getLogger(__name__)
class DelugeHelpFormatter(optparse.IndentedHelpFormatter):
"""
Format help in a way suited to deluge Legacy mode - colors, format, indentation...
"""
class ConsoleCommandParser(argparse.ArgumentParser):
replace_dict = {
"<torrent-id>": "{!green!}%s{!input!}",
"<state>": "{!yellow!}%s{!input!}",
"\\.\\.\\.": "{!yellow!}%s{!input!}",
"\\s\\*\\s": "{!blue!}%s{!input!}",
"(?<![\\-a-z])(-[a-zA-Z0-9])": "{!red!}%s{!input!}",
# "(\-[a-zA-Z0-9])": "{!red!}%s{!input!}",
"--[_\\-a-zA-Z0-9]+": "{!green!}%s{!input!}",
"(\\[|\\])": "{!info!}%s{!input!}",
def format_help(self):
"""
Differs from ArgumentParser.format_help by adding the raw epilog
as formatted in the string. Default bahavior mangles the formatting.
"<tab>": "{!white!}%s{!input!}",
"[_A-Z]{3,}": "{!cyan!}%s{!input!}",
"<download-folder>": "{!yellow!}%s{!input!}",
"<torrent-file>": "{!green!}%s{!input!}"
}
def __init__(self,
indent_increment=2,
max_help_position=24,
width=None,
short_first=1):
optparse.IndentedHelpFormatter.__init__(
self, indent_increment, max_help_position, width, short_first)
def _format_colors(self, string):
def r(repl):
return lambda s: repl % s.group()
for key, replacement in self.replace_dict.items():
string = re.sub(key, r(replacement), string)
return string
def format_usage(self, usage):
return _("{!info!}Usage{!input!}: %s\n") % self._format_colors(usage)
def format_option(self, option):
result = []
opts = self.option_strings[option]
opt_width = self.help_position - self.current_indent - 2
if len(opts) > opt_width:
opts = "%*s%s\n" % (self.current_indent, "", opts)
opts = self._format_colors(opts)
indent_first = self.help_position
else: # start help on same line as opts
opts = "%*s%-*s " % (self.current_indent, "", opt_width, opts)
opts = self._format_colors(opts)
indent_first = 0
result.append(opts)
if option.help:
help_text = self.expand_default(option)
help_text = self._format_colors(help_text)
help_lines = optparse.textwrap.wrap(help_text, self.help_width)
result.append("%*s%s\n" % (indent_first, "", help_lines[0]))
result.extend(["%*s%s\n" % (self.help_position, "", line)
for line in help_lines[1:]])
elif opts[-1] != "\n":
result.append("\n")
return "".join(result)
"""
# Handle epilog manually to keep the text formatting
epilog = self.epilog
self.epilog = ""
help_str = super(ConsoleCommandParser, self).format_help()
if epilog is not None:
help_str += epilog
self.epilog = epilog
return help_str
class OptionParser(optparse.OptionParser):
"""subclass from optparse.OptionParser so exit() won't exit."""
class OptionParser(ConsoleCommandParser):
def __init__(self, **kwargs):
optparse.OptionParser.__init__(self, **kwargs)
self.formatter = DelugeHelpFormatter()
super(OptionParser, self).__init__(**kwargs)
self.formatter = ConsoleColorFormatter()
def exit(self, status=0, msg=None):
self.values._exit = True
self._exit = True
if msg:
print(msg)
@ -124,7 +73,7 @@ class OptionParser(optparse.OptionParser):
def print_usage(self, _file=None):
console = component.get("ConsoleUI")
if self.usage:
for line in self.get_usage().splitlines():
for line in self.format_usage().splitlines():
console.write(line)
def print_help(self, _file=None):
@ -134,43 +83,36 @@ class OptionParser(optparse.OptionParser):
console.write(line)
console.set_batch_write(False)
def format_option_help(self, formatter=None):
if formatter is None:
formatter = self.formatter
formatter.store_option_strings(self)
result = []
result.append(formatter.format_heading(_("{!info!}Options{!input!}")))
formatter.indent()
if self.option_list:
result.append(optparse.OptionContainer.format_option_help(self, formatter))
result.append("\\n")
for group in self.option_groups:
result.append(group.format_help(formatter))
result.append("\\n")
formatter.dedent()
# Drop the last "\\n", or the header if no options or option groups:
return "".join(result[:-1])
def format_help(self):
"""Return help formatted with colors."""
help_str = super(OptionParser, self).format_help()
return self.formatter.format_colors(help_str)
class BaseCommand(object):
usage = "usage"
usage = None
interactive_only = False
option_list = tuple()
aliases = []
_name = "base"
epilog = ""
def complete(self, text, *args):
return []
def handle(self, *args, **options):
def handle(self, options):
pass
@property
def name(self):
return "base"
return self._name
@property
def epilog(self):
def name_with_alias(self):
return "/".join([self._name] + self.aliases)
@property
def description(self):
return self.__doc__
def split(self, text):
@ -183,16 +125,32 @@ class BaseCommand(object):
return result
def create_parser(self):
return OptionParser(prog=self.name, usage=self.usage, epilog=self.epilog, option_list=self.option_list)
opts = {"prog": self.name_with_alias, "description": self.__doc__, "epilog": self.epilog}
if self.usage:
opts["usage"] = self.usage
parser = OptionParser(**opts)
parser.add_argument(self.name, metavar="")
self.add_arguments(parser)
return parser
def add_subparser(self, subparsers):
opts = {"prog": self.name_with_alias, "help": self.__doc__, "description": self.__doc__}
if self.usage:
opts["usage"] = self.usage
parser = subparsers.add_parser(self.name, **opts)
self.add_arguments(parser)
def add_arguments(self, parser):
pass
class ConsoleUI(component.Component):
def __init__(self, args=None, cmds=None, daemon=None):
component.Component.__init__(self, "ConsoleUI", 2)
def __init__(self, options=None, cmds=None):
component.Component.__init__(self, "ConsoleUI", 2)
# keep track of events for the log view
self.events = []
self.statusbars = None
try:
locale.setlocale(locale.LC_ALL, "")
self.encoding = locale.getpreferredencoding()
@ -209,19 +167,14 @@ class ConsoleUI(component.Component):
# Set the interactive flag to indicate where we should print the output
self.interactive = True
self._commands = cmds
if args:
args = " ".join(args)
if options.remaining:
self.interactive = False
if not cmds:
print("Sorry, couldn't find any commands")
return
else:
from deluge.ui.console.commander import Commander
cmdr = Commander(cmds)
if daemon:
cmdr.exec_args(args, *daemon)
else:
cmdr.exec_args(args, None, None, None, None)
self.exec_args(options)
self.coreconfig = CoreConfig()
if self.interactive and not deluge.common.windows_check():
@ -240,6 +193,60 @@ Please use commands from the command line, eg:\n
else:
reactor.run()
def exec_args(self, options):
args = options.remaining
commands = []
if args:
cmd = " ".join([arg for arg in args])
# Multiple commands split by ";"
commands += [arg.strip() for arg in cmd.split(";")]
from deluge.ui.console.commander import Commander
commander = Commander(self._commands)
def on_connect(result):
def on_started(result):
def on_started(result):
def do_command(result, cmd):
return commander.do_command(cmd)
d = defer.succeed(None)
for command in commands:
if command in ("quit", "exit"):
break
d.addCallback(do_command, command)
d.addCallback(do_command, "quit")
# 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)
def on_connect_fail(reason):
if reason.check(DelugeError):
rm = reason.getErrorMessage()
else:
rm = reason.value.message
print("Could not connect to daemon: %s:%s\n %s" % (options.daemon_addr, options.daemon_port, rm))
commander.do_command("quit")
d = None
if not self.interactive:
if commands[0] is not None:
if commands[0].startswith("connect"):
d = commander.do_command(commands.pop(0))
if d is None:
# Error parsing command
sys.exit(0)
elif "help" in commands:
commander.do_command("help")
sys.exit(0)
if not d:
log.info("connect: host=%s, port=%s, username=%s, password=%s",
options.daemon_addr, options.daemon_port, options.daemon_user, options.daemon_pass)
d = client.connect(options.daemon_addr, options.daemon_port, options.daemon_user, options.daemon_pass)
d.addCallback(on_connect)
d.addErrback(on_connect_fail)
def run(self, stdscr):
"""
This method is called by the curses.wrapper to start the mainloop and
@ -332,6 +339,7 @@ Please use commands from the command line, eg:\n
self.screen = mode
self.statusbars.screen = self.screen
reactor.addReader(self.screen)
self.stdscr.clear()
mode.refresh()
def on_client_disconnect(self):

View File

@ -812,9 +812,6 @@ class AllTorrents(BaseMode, component.Component):
self.refresh()
def refresh(self, lines=None):
# log.error("ref")
# import traceback
# traceback.print_stack()
# Something has requested we scroll to the top of the list
if self._go_top:
self.cursel = 1

View File

@ -38,6 +38,7 @@ DEFAULT_CONFIG = {
class ConnectionManager(BaseMode):
def __init__(self, stdscr, encoding=None):
self.popup = None
self.statuses = {}
@ -87,6 +88,7 @@ class ConnectionManager(BaseMode):
port = host[2]
user = host[3]
password = host[4]
log.debug("connect: hadr=%s, port=%s, user=%s, password=%s", hadr, port, user, password)
d = c.connect(hadr, port, user, password)
d.addCallback(on_connect, c, host[0])
d.addErrback(on_connect_failed, host[0])

View File

@ -24,6 +24,7 @@ import deluge.component as component
import deluge.configmanager
import deluge.ui.console.colors as colors
from deluge.ui.client import client
from deluge.ui.console.commander import Commander
from deluge.ui.console.modes import format_utils
from deluge.ui.console.modes.basemode import BaseMode
@ -94,11 +95,15 @@ def commonprefix(m):
return s2
class Legacy(BaseMode, component.Component):
class Legacy(BaseMode, Commander, component.Component):
def __init__(self, stdscr, encoding=None):
component.Component.__init__(self, "LegacyUI", 1, depend=["SessionProxy"])
# Get a handle to the main console
self.console = component.get("ConsoleUI")
Commander.__init__(self, self.console._commands, interactive=True)
self.batch_write = False
# A list of strings to be displayed based on the offset (scroll)
@ -122,9 +127,6 @@ class Legacy(BaseMode, component.Component):
# Keep track of double- and multi-tabs
self.tab_count = 0
# Get a handle to the main console
self.console = component.get("ConsoleUI")
self.console_config = component.get("AllTorrents").config
# To avoid having to truncate the file every time we're writing
@ -586,64 +588,6 @@ class Legacy(BaseMode, component.Component):
col += strwidth(s)
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.console._commands[cmd].create_parser()
except KeyError:
self.write("{!error!}Unknown command: %s" % cmd)
return
try:
args = self.console._commands[cmd].split(line)
except ValueError as ex:
self.write("{!error!}Error parsing command: %s" % ex)
return
# Do a little hack here to print 'command --help' properly
parser._print_help = parser.print_help
def print_help(f=None):
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.console._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 TypeError as ex:
self.write("{!error!}Error parsing options: %s" % ex)
return
if not getattr(options, "_exit", False):
try:
ret = self.console._commands[cmd].handle(*args, **options.__dict__)
except Exception as ex:
self.write("{!error!} %s" % ex)
log.exception(ex)
import traceback
self.write("%s" % traceback.format_exc())
return defer.succeed(True)
else:
return ret
def set_batch_write(self, batch):
"""
When this is set the screen is not refreshed after a `:meth:write` until

View File

@ -8,6 +8,7 @@
#
# We skip isorting this file as it want to move the gtk2reactor.install() below the imports
# isort:skip_file
from __future__ import division
import logging
@ -24,7 +25,7 @@ from twisted.internet.task import LoopingCall
try:
# Install twisted reactor, before any other modules import reactor.
reactor = gtk2reactor.install()
except ReactorAlreadyInstalledError:
except ReactorAlreadyInstalledError as ex:
# Running unit tests so trial already installed a rector
pass
@ -196,7 +197,7 @@ class GtkUI(object):
# Start the IPC Interface before anything else.. Just in case we are
# already running.
self.queuedtorrents = QueuedTorrents()
self.ipcinterface = IPCInterface(args)
self.ipcinterface = IPCInterface(args.torrents)
# Initialize gdk threading
gtk.gdk.threads_init()

View File

@ -8,12 +8,13 @@
#
import logging
import optparse
import deluge.common
import deluge.configmanager
import deluge.log
from deluge.commonoptions import CommonOptionParser
from deluge.ui.baseargparser import BaseArgParser
log = logging.getLogger(__name__)
try:
from setproctitle import setproctitle
@ -21,10 +22,6 @@ except ImportError:
def setproctitle(title):
return
DEFAULT_PREFS = {
"default_ui": "gtk"
}
if 'dev' not in deluge.common.get_version():
import warnings
warnings.filterwarnings('ignore', category=DeprecationWarning, module='twisted')
@ -32,15 +29,16 @@ if 'dev' not in deluge.common.get_version():
class UI(object):
def __init__(self, name="gtk", skip_common=False):
def __init__(self, name="gtk", parser=None):
self.__name = name
self.__parser = parser if parser else BaseArgParser()
deluge.common.setup_translations(setup_pygtk=(name == "gtk"))
if name == "gtk":
deluge.common.setup_translations(setup_pygtk=True)
else:
deluge.common.setup_translations()
self.__parser = optparse.OptionParser() if skip_common else CommonOptionParser()
def parse_args(self, args=None):
options = self.parser.parse_args(args)
if not hasattr(options, "remaining"):
options.remaining = []
return options
@property
def name(self):
@ -54,22 +52,15 @@ class UI(object):
def options(self):
return self.__options
@property
def args(self):
return self.__args
def start(self, extra_args=None):
args = deluge.common.unicode_argv()[1:]
if extra_args:
args.extend(extra_args)
def start(self, args=None):
if args is None:
# Make sure all arguments are unicode
args = deluge.common.unicode_argv()[1:]
self.__options, self.__args = self.__parser.parse_args(args)
log = logging.getLogger(__name__)
self.__options = self.parse_args(args)
setproctitle("deluge-%s" % self.__name)
log.info("Deluge ui %s", deluge.common.get_version())
log.debug("options: %s", self.__options)
log.debug("args: %s", self.__args)
log.info("Starting %s ui..", self.__name)

View File

@ -10,13 +10,18 @@
from __future__ import print_function
import os
from optparse import OptionGroup
from deluge.common import osx_check, windows_check
from deluge.configmanager import get_config_dir
from deluge.ui.ui import UI
#
# Note: Cannot import twisted.internet.reactor because Web is imported from
# from web/__init__.py loaded by the script entry points defined in setup.py
#
class WebUI(object):
def __init__(self, args):
from deluge.ui.web import server
@ -33,36 +38,36 @@ class Web(UI):
super(Web, self).__init__("web", *args, **kwargs)
self.__server = None
group = OptionGroup(self.parser, "Web Options")
group.add_option("-b", "--base", dest="base", action="store", default=None,
help="Set the base path that the ui is running on (proxying)")
group = self.parser.add_argument_group(_('Web Options'))
group.add_argument("-b", "--base", metavar="<path>", action="store", default=None,
help="Set the base path that the ui is running on (proxying)")
if not (windows_check() or osx_check()):
group.add_option("-d", "--do-not-daemonize", dest="donotdaemonize", action="store_true", default=False,
help="Do not daemonize the web interface")
group.add_option("-P", "--pidfile", dest="pidfile", type="str", action="store", default=None,
help="Use pidfile to store process id")
group.add_argument("-d", "--do-not-daemonize", dest="donotdaemonize", action="store_true", default=False,
help="Do not daemonize the web interface")
group.add_argument("-P", "--pidfile", metavar="<pidfile>", action="store", default=None,
help="Use pidfile to store process id")
if not windows_check():
group.add_option("-U", "--user", dest="user", type="str", action="store", default=None,
help="User to switch to. Only use it when starting as root")
group.add_option("-g", "--group", dest="group", type="str", action="store", default=None,
help="Group to switch to. Only use it when starting as root")
group.add_option("-i", "--interface", dest="interface", action="store", default=None,
type="str", help="Binds the webserver to a specific IP address")
group.add_option("-p", "--port", dest="port", type="int", action="store", default=None,
help="Sets the port to be used for the webserver")
group.add_option("--profile", dest="profile", action="store_true", default=False,
help="Profile the web server code")
group.add_argument("-U", "--user", metavar="<user>", action="store", default=None,
help="User to switch to. Only use it when starting as root")
group.add_argument("-g", "--group", metavar="<group>", action="store", default=None,
help="Group to switch to. Only use it when starting as root")
group.add_argument("-i", "--interface", metavar="<interface>", action="store", default=None,
help="Binds the webserver to a specific IP address")
group.add_argument("-p", "--port", metavar="<port>", type=int, action="store", default=None,
help="Sets the port to be used for the webserver")
group.add_argument("--profile", action="store_true", default=False,
help="Profile the web server code")
try:
import OpenSSL
assert OpenSSL.__version__
except ImportError:
pass
else:
group.add_option("--no-ssl", dest="ssl", action="store_false",
help="Forces the webserver to disable ssl", default=False)
group.add_option("--ssl", dest="ssl", action="store_true",
help="Forces the webserver to use ssl", default=False)
self.parser.add_option_group(group)
group.add_argument("--no-ssl", dest="ssl", action="store_false",
help="Forces the webserver to disable ssl", default=False)
group.add_argument("--ssl", dest="ssl", action="store_true",
help="Forces the webserver to use ssl", default=False)
@property
def server(self):
@ -73,7 +78,7 @@ class Web(UI):
# Steps taken from http://www.faqs.org/faqs/unix-faq/programmer/faq/
# Section 1.7
if not self.options.ensure_value("donotdaemonize", True):
if self.options.donotdaemonize is not True:
# fork() so the parent can exit, returns control to the command line
# or shell invoking the program.
if os.fork():
@ -93,12 +98,12 @@ class Web(UI):
if self.options.pidfile:
open(self.options.pidfile, "wb").write("%d\n" % os.getpid())
if self.options.ensure_value("group", None):
if self.options.group:
if not self.options.group.isdigit():
import grp
self.options.group = grp.getgrnam(self.options.group)[2]
os.setuid(self.options.group)
if self.options.ensure_value("user", None):
if self.options.user:
if not self.options.user.isdigit():
import pwd
self.options.user = pwd.getpwnam(self.options.user)[2]
@ -116,8 +121,7 @@ class Web(UI):
if self.options.port:
self.server.port = self.options.port
if self.options.ensure_value("ssl", None):
self.server.https = self.options.ssl
self.server.https = self.options.ssl
def run_server():
self.server.install_signal_handlers()
@ -133,7 +137,7 @@ class Web(UI):
profiler.dump_stats(profile_output)
print("Profile stats saved to %s" % profile_output)
from twisted.internet import reactor
from twisted.internet import reactor # import here because (see top)
reactor.addSystemEventTrigger("before", "shutdown", save_profile_stats)
print("Running with profiler...")
profiler.runcall(run_server)