un-revert main.py as it doesn't actually handle events.
This commit is contained in:
parent
499a58f50d
commit
4c2f9a1a0a
|
@ -34,24 +34,28 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
import os, sys
|
import os
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
import optparse
|
import optparse
|
||||||
import shlex
|
import shlex
|
||||||
import locale
|
import locale
|
||||||
|
|
||||||
from twisted.internet import defer, reactor
|
from twisted.internet import defer, reactor
|
||||||
|
|
||||||
from deluge.ui.console import UI_PATH
|
|
||||||
import deluge.component as component
|
import deluge.component as component
|
||||||
from deluge.ui.client import client
|
from deluge.ui.client import client
|
||||||
import deluge.common
|
import deluge.common
|
||||||
from deluge.ui.coreconfig import CoreConfig
|
from deluge.ui.coreconfig import CoreConfig
|
||||||
|
from deluge.ui.sessionproxy import SessionProxy
|
||||||
from deluge.ui.console.statusbars import StatusBars
|
from deluge.ui.console.statusbars import StatusBars
|
||||||
from deluge.ui.console.eventlog import EventLog
|
from deluge.ui.console.eventlog import EventLog
|
||||||
import screen
|
#import screen
|
||||||
import colors
|
import colors
|
||||||
from deluge.log import LOG as log
|
|
||||||
from deluge.ui.ui import _UI
|
from deluge.ui.ui import _UI
|
||||||
|
from deluge.ui.console import UI_PATH
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
class Console(_UI):
|
class Console(_UI):
|
||||||
|
|
||||||
|
@ -59,16 +63,62 @@ class Console(_UI):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super(Console, self).__init__("console")
|
super(Console, self).__init__("console")
|
||||||
cmds = load_commands(os.path.join(UI_PATH, 'commands'))
|
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 = optparse.OptionGroup(self.parser, "Console Commands",
|
group.add_option("-d","--daemon",dest="daemon_addr",
|
||||||
"\n".join(cmds.keys()))
|
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)
|
self.parser.add_option_group(group)
|
||||||
|
|
||||||
|
self.cmds = load_commands(os.path.join(UI_PATH, 'commands'))
|
||||||
|
class CommandOptionGroup(optparse.OptionGroup):
|
||||||
|
def __init__(self, parser, title, description=None, cmds = None):
|
||||||
|
optparse.OptionGroup.__init__(self,parser,title,description)
|
||||||
|
self.cmds = cmds
|
||||||
|
|
||||||
|
def format_help(self, formatter):
|
||||||
|
result = formatter.format_heading(self.title)
|
||||||
|
formatter.indent()
|
||||||
|
if self.description:
|
||||||
|
result += "%s\n"%formatter.format_description(self.description)
|
||||||
|
for cname in self.cmds:
|
||||||
|
cmd = self.cmds[cname]
|
||||||
|
if cmd.interactive_only or cname in cmd.aliases: continue
|
||||||
|
allnames = [cname]
|
||||||
|
allnames.extend(cmd.aliases)
|
||||||
|
cname = "/".join(allnames)
|
||||||
|
result += formatter.format_heading(" - ".join([cname,cmd.__doc__]))
|
||||||
|
formatter.indent()
|
||||||
|
result += "%*s%s\n" % (formatter.current_indent, "", cmd.usage)
|
||||||
|
formatter.dedent()
|
||||||
|
formatter.dedent()
|
||||||
|
return result
|
||||||
|
cmd_group = CommandOptionGroup(self.parser, "Console Commands",
|
||||||
|
description="The following commands can be issued at the "
|
||||||
|
"command line. Commands should be quoted, so, for example, "
|
||||||
|
"to pause torrent with id 'abc' you would run: '%s "
|
||||||
|
"\"pause abc\"'"%os.path.basename(sys.argv[0]),
|
||||||
|
cmds=self.cmds)
|
||||||
|
self.parser.add_option_group(cmd_group)
|
||||||
|
|
||||||
def start(self):
|
def start(self):
|
||||||
super(Console, self).start()
|
super(Console, self).start()
|
||||||
|
ConsoleUI(self.args,self.cmds,(self.options.daemon_addr,
|
||||||
ConsoleUI(self.args)
|
self.options.daemon_port,self.options.daemon_user,
|
||||||
|
self.options.daemon_pass))
|
||||||
|
|
||||||
def start():
|
def start():
|
||||||
Console().start()
|
Console().start()
|
||||||
|
@ -89,9 +139,11 @@ class OptionParser(optparse.OptionParser):
|
||||||
"""
|
"""
|
||||||
raise Exception(msg)
|
raise Exception(msg)
|
||||||
|
|
||||||
|
|
||||||
class BaseCommand(object):
|
class BaseCommand(object):
|
||||||
|
|
||||||
usage = 'usage'
|
usage = 'usage'
|
||||||
|
interactive_only = False
|
||||||
option_list = tuple()
|
option_list = tuple()
|
||||||
aliases = []
|
aliases = []
|
||||||
|
|
||||||
|
@ -119,6 +171,7 @@ class BaseCommand(object):
|
||||||
epilog = self.epilog,
|
epilog = self.epilog,
|
||||||
option_list = self.option_list)
|
option_list = self.option_list)
|
||||||
|
|
||||||
|
|
||||||
def load_commands(command_dir, exclude=[]):
|
def load_commands(command_dir, exclude=[]):
|
||||||
def get_command(name):
|
def get_command(name):
|
||||||
return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, {}, ['Command']), 'Command')()
|
return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, {}, ['Command']), 'Command')()
|
||||||
|
@ -139,11 +192,13 @@ def load_commands(command_dir, exclude=[]):
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
|
||||||
class ConsoleUI(component.Component):
|
class ConsoleUI(component.Component):
|
||||||
def __init__(self, args=None):
|
def __init__(self, args=None, cmds = None, daemon = None):
|
||||||
component.Component.__init__(self, "ConsoleUI", 2)
|
component.Component.__init__(self, "ConsoleUI", 2)
|
||||||
|
|
||||||
self.batch_write = False
|
# keep track of events for the log view
|
||||||
|
self.events = []
|
||||||
|
|
||||||
try:
|
try:
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
locale.setlocale(locale.LC_ALL, '')
|
||||||
|
@ -152,8 +207,10 @@ class ConsoleUI(component.Component):
|
||||||
self.encoding = sys.getdefaultencoding()
|
self.encoding = sys.getdefaultencoding()
|
||||||
|
|
||||||
log.debug("Using encoding: %s", self.encoding)
|
log.debug("Using encoding: %s", self.encoding)
|
||||||
# Load all the commands
|
|
||||||
self._commands = load_commands(os.path.join(UI_PATH, 'commands'))
|
|
||||||
|
# start up the session proxy
|
||||||
|
self.sessionproxy = SessionProxy()
|
||||||
|
|
||||||
client.set_disconnect_callback(self.on_client_disconnect)
|
client.set_disconnect_callback(self.on_client_disconnect)
|
||||||
|
|
||||||
|
@ -162,30 +219,18 @@ class ConsoleUI(component.Component):
|
||||||
if args:
|
if args:
|
||||||
args = args[0]
|
args = args[0]
|
||||||
self.interactive = False
|
self.interactive = False
|
||||||
|
if not cmds:
|
||||||
# Try to connect to the localhost daemon
|
print "Sorry, couldn't find any commands"
|
||||||
def on_connect(result):
|
return
|
||||||
def on_started(result):
|
else:
|
||||||
if not self.interactive:
|
self._commands = cmds
|
||||||
def on_started(result):
|
from commander import Commander
|
||||||
deferreds = []
|
cmdr = Commander(cmds)
|
||||||
# If we have args, lets process them and quit
|
if daemon:
|
||||||
# allow multiple commands split by ";"
|
cmdr.exec_args(args,*daemon)
|
||||||
for arg in args.split(";"):
|
else:
|
||||||
deferreds.append(defer.maybeDeferred(self.do_command, arg.strip()))
|
cmdr.exec_args(args,None,None,None,None)
|
||||||
|
|
||||||
def on_complete(result):
|
|
||||||
self.do_command("quit")
|
|
||||||
|
|
||||||
dl = defer.DeferredList(deferreds).addCallback(on_complete)
|
|
||||||
|
|
||||||
# We need to wait for the rpcs in start() to finish before processing
|
|
||||||
# any of the commands.
|
|
||||||
self.started_deferred.addCallback(on_started)
|
|
||||||
component.start().addCallback(on_started)
|
|
||||||
|
|
||||||
d = client.connect()
|
|
||||||
d.addCallback(on_connect)
|
|
||||||
|
|
||||||
self.coreconfig = CoreConfig()
|
self.coreconfig = CoreConfig()
|
||||||
if self.interactive and not deluge.common.windows_check():
|
if self.interactive and not deluge.common.windows_check():
|
||||||
|
@ -194,8 +239,13 @@ class ConsoleUI(component.Component):
|
||||||
import curses.wrapper
|
import curses.wrapper
|
||||||
curses.wrapper(self.run)
|
curses.wrapper(self.run)
|
||||||
elif self.interactive and deluge.common.windows_check():
|
elif self.interactive and deluge.common.windows_check():
|
||||||
print "You cannot run the deluge-console in interactive mode in Windows.\
|
print """\nDeluge-console does not run in interactive mode on Windows. \n
|
||||||
Please use commands from the command line, eg: deluge-console config;help;exit"
|
Please use commands from the command line, eg:\n
|
||||||
|
deluge-console.exe help
|
||||||
|
deluge-console.exe info
|
||||||
|
deluge-console.exe "add --help"
|
||||||
|
deluge-console.exe "add -p c:\\mytorrents c:\\new.torrent"
|
||||||
|
"""
|
||||||
else:
|
else:
|
||||||
reactor.run()
|
reactor.run()
|
||||||
|
|
||||||
|
@ -210,8 +260,9 @@ class ConsoleUI(component.Component):
|
||||||
# We want to do an interactive session, so start up the curses screen and
|
# We want to do an interactive session, so start up the curses screen and
|
||||||
# pass it the function that handles commands
|
# pass it the function that handles commands
|
||||||
colors.init_colors()
|
colors.init_colors()
|
||||||
self.screen = screen.Screen(stdscr, self.do_command, self.tab_completer, self.encoding)
|
|
||||||
self.statusbars = StatusBars()
|
self.statusbars = StatusBars()
|
||||||
|
from modes.connectionmanager import ConnectionManager
|
||||||
|
self.screen = ConnectionManager(stdscr, self.encoding)
|
||||||
self.eventlog = EventLog()
|
self.eventlog = EventLog()
|
||||||
|
|
||||||
self.screen.topbar = "{!status!}Deluge " + deluge.common.get_version() + " Console"
|
self.screen.topbar = "{!status!}Deluge " + deluge.common.get_version() + " Console"
|
||||||
|
@ -225,202 +276,22 @@ class ConsoleUI(component.Component):
|
||||||
# Start the twisted mainloop
|
# Start the twisted mainloop
|
||||||
reactor.run()
|
reactor.run()
|
||||||
|
|
||||||
def start(self):
|
|
||||||
# This gets fired once we have received the torrents list from the core
|
|
||||||
self.started_deferred = defer.Deferred()
|
|
||||||
|
|
||||||
|
def start(self):
|
||||||
# Maintain a list of (torrent_id, name) for use in tab completion
|
# Maintain a list of (torrent_id, name) for use in tab completion
|
||||||
self.torrents = []
|
self.torrents = []
|
||||||
def on_session_state(result):
|
if not self.interactive:
|
||||||
def on_torrents_status(torrents):
|
self.started_deferred = defer.Deferred()
|
||||||
for torrent_id, status in torrents.items():
|
def on_session_state(result):
|
||||||
self.torrents.append((torrent_id, status["name"]))
|
def on_torrents_status(torrents):
|
||||||
self.started_deferred.callback(True)
|
for torrent_id, status in torrents.items():
|
||||||
|
self.torrents.append((torrent_id, status["name"]))
|
||||||
|
self.started_deferred.callback(True)
|
||||||
|
|
||||||
client.core.get_torrents_status({"id": result}, ["name"]).addCallback(on_torrents_status)
|
client.core.get_torrents_status({"id": result}, ["name"]).addCallback(on_torrents_status)
|
||||||
client.core.get_session_state().addCallback(on_session_state)
|
client.core.get_session_state().addCallback(on_session_state)
|
||||||
|
|
||||||
# Register some event handlers to keep the torrent list up-to-date
|
|
||||||
client.register_event_handler("TorrentAddedEvent", self.on_torrent_added_event)
|
|
||||||
client.register_event_handler("TorrentRemovedEvent", self.on_torrent_removed_event)
|
|
||||||
|
|
||||||
def update(self):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def set_batch_write(self, batch):
|
|
||||||
"""
|
|
||||||
When this is set the screen is not refreshed after a `:meth:write` until
|
|
||||||
this is set to False.
|
|
||||||
|
|
||||||
:param batch: set True to prevent screen refreshes after a `:meth:write`
|
|
||||||
:type batch: bool
|
|
||||||
|
|
||||||
"""
|
|
||||||
self.batch_write = batch
|
|
||||||
if not batch and self.interactive:
|
|
||||||
self.screen.refresh()
|
|
||||||
|
|
||||||
def write(self, line):
|
|
||||||
"""
|
|
||||||
Writes a line out depending on if we're in interactive mode or not.
|
|
||||||
|
|
||||||
:param line: str, the line to print
|
|
||||||
|
|
||||||
"""
|
|
||||||
if self.interactive:
|
|
||||||
self.screen.add_line(line, not self.batch_write)
|
|
||||||
else:
|
|
||||||
print(colors.strip_colors(line))
|
|
||||||
|
|
||||||
def do_command(self, cmd):
|
|
||||||
"""
|
|
||||||
Processes a command.
|
|
||||||
|
|
||||||
:param cmd: str, the command string
|
|
||||||
|
|
||||||
"""
|
|
||||||
if not cmd:
|
|
||||||
return
|
|
||||||
cmd, _, line = cmd.partition(' ')
|
|
||||||
try:
|
|
||||||
parser = self._commands[cmd].create_parser()
|
|
||||||
except KeyError:
|
|
||||||
self.write("{!error!}Unknown command: %s" % cmd)
|
|
||||||
return
|
|
||||||
args = self._commands[cmd].split(line)
|
|
||||||
|
|
||||||
# Do a little hack here to print 'command --help' properly
|
|
||||||
parser._print_help = parser.print_help
|
|
||||||
def print_help(f=None):
|
|
||||||
if self.interactive:
|
|
||||||
self.write(parser.format_help())
|
|
||||||
else:
|
|
||||||
parser._print_help(f)
|
|
||||||
parser.print_help = print_help
|
|
||||||
|
|
||||||
# Only these commands can be run when not connected to a daemon
|
|
||||||
not_connected_cmds = ["help", "connect", "quit"]
|
|
||||||
aliases = []
|
|
||||||
for c in not_connected_cmds:
|
|
||||||
aliases.extend(self._commands[c].aliases)
|
|
||||||
not_connected_cmds.extend(aliases)
|
|
||||||
|
|
||||||
if not client.connected() and cmd not in not_connected_cmds:
|
|
||||||
self.write("{!error!}Not connected to a daemon, please use the connect command first.")
|
|
||||||
return
|
|
||||||
|
|
||||||
try:
|
|
||||||
options, args = parser.parse_args(args)
|
|
||||||
except Exception, e:
|
|
||||||
self.write("{!error!}Error parsing options: %s" % e)
|
|
||||||
return
|
|
||||||
|
|
||||||
if not getattr(options, '_exit', False):
|
|
||||||
try:
|
|
||||||
ret = self._commands[cmd].handle(*args, **options.__dict__)
|
|
||||||
except Exception, e:
|
|
||||||
self.write("{!error!}" + str(e))
|
|
||||||
log.exception(e)
|
|
||||||
import traceback
|
|
||||||
self.write("%s" % traceback.format_exc())
|
|
||||||
return defer.succeed(True)
|
|
||||||
else:
|
|
||||||
return ret
|
|
||||||
|
|
||||||
def tab_completer(self, line, cursor, second_hit):
|
|
||||||
"""
|
|
||||||
Called when the user hits 'tab' and will autocomplete or show options.
|
|
||||||
If a command is already supplied in the line, this function will call the
|
|
||||||
complete method of the command.
|
|
||||||
|
|
||||||
:param line: str, the current input string
|
|
||||||
:param cursor: int, the cursor position in the line
|
|
||||||
:param second_hit: bool, if this is the second time in a row the tab key
|
|
||||||
has been pressed
|
|
||||||
|
|
||||||
:returns: 2-tuple (string, cursor position)
|
|
||||||
|
|
||||||
"""
|
|
||||||
# First check to see if there is no space, this will mean that it's a
|
|
||||||
# command that needs to be completed.
|
|
||||||
if " " not in line:
|
|
||||||
possible_matches = []
|
|
||||||
# Iterate through the commands looking for ones that startwith the
|
|
||||||
# line.
|
|
||||||
for cmd in self._commands:
|
|
||||||
if cmd.startswith(line):
|
|
||||||
possible_matches.append(cmd + " ")
|
|
||||||
|
|
||||||
line_prefix = ""
|
|
||||||
else:
|
|
||||||
cmd = line.split(" ")[0]
|
|
||||||
if cmd in self._commands:
|
|
||||||
# Call the command's complete method to get 'er done
|
|
||||||
possible_matches = self._commands[cmd].complete(line.split(" ")[-1])
|
|
||||||
line_prefix = " ".join(line.split(" ")[:-1]) + " "
|
|
||||||
else:
|
|
||||||
# This is a bogus command
|
|
||||||
return (line, cursor)
|
|
||||||
|
|
||||||
# No matches, so just return what we got passed
|
|
||||||
if len(possible_matches) == 0:
|
|
||||||
return (line, cursor)
|
|
||||||
# If we only have 1 possible match, then just modify the line and
|
|
||||||
# return it, else we need to print out the matches without modifying
|
|
||||||
# the line.
|
|
||||||
elif len(possible_matches) == 1:
|
|
||||||
new_line = line_prefix + possible_matches[0]
|
|
||||||
return (new_line, len(new_line))
|
|
||||||
else:
|
|
||||||
if second_hit:
|
|
||||||
# Only print these out if it's a second_hit
|
|
||||||
self.write(" ")
|
|
||||||
for match in possible_matches:
|
|
||||||
self.write(match)
|
|
||||||
else:
|
|
||||||
p = " ".join(line.split(" ")[:-1])
|
|
||||||
new_line = " ".join([p, os.path.commonprefix(possible_matches)])
|
|
||||||
if len(new_line) > len(line):
|
|
||||||
line = new_line
|
|
||||||
cursor = len(line)
|
|
||||||
return (line, cursor)
|
|
||||||
|
|
||||||
def tab_complete_torrent(self, line):
|
|
||||||
"""
|
|
||||||
Completes torrent_ids or names.
|
|
||||||
|
|
||||||
:param line: str, the string to complete
|
|
||||||
|
|
||||||
:returns: list of matches
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
possible_matches = []
|
|
||||||
|
|
||||||
# Find all possible matches
|
|
||||||
for torrent_id, torrent_name in self.torrents:
|
|
||||||
if torrent_id.startswith(line):
|
|
||||||
possible_matches.append(torrent_id + " ")
|
|
||||||
if torrent_name.startswith(line):
|
|
||||||
possible_matches.append(torrent_name + " ")
|
|
||||||
|
|
||||||
return possible_matches
|
|
||||||
|
|
||||||
def get_torrent_name(self, torrent_id):
|
|
||||||
"""
|
|
||||||
Gets a torrent name from the torrents list.
|
|
||||||
|
|
||||||
:param torrent_id: str, the torrent_id
|
|
||||||
|
|
||||||
:returns: the name of the torrent or None
|
|
||||||
"""
|
|
||||||
|
|
||||||
for tid, name in self.torrents:
|
|
||||||
if torrent_id == tid:
|
|
||||||
return name
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def match_torrent(self, string):
|
def match_torrent(self, string):
|
||||||
"""
|
"""
|
||||||
Returns a list of torrent_id matches for the string. It will search both
|
Returns a list of torrent_id matches for the string. It will search both
|
||||||
|
@ -439,15 +310,33 @@ class ConsoleUI(component.Component):
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def on_torrent_added_event(self, event):
|
|
||||||
def on_torrent_status(status):
|
|
||||||
self.torrents.append((event.torrent_id, status["name"]))
|
|
||||||
client.core.get_torrent_status(event.torrent_id, ["name"]).addCallback(on_torrent_status)
|
|
||||||
|
|
||||||
def on_torrent_removed_event(self, event):
|
def get_torrent_name(self, torrent_id):
|
||||||
for index, (tid, name) in enumerate(self.torrents):
|
if self.interactive and hasattr(self.screen,"get_torrent_name"):
|
||||||
if event.torrent_id == tid:
|
return self.screen.get_torrent_name(torrent_id)
|
||||||
del self.torrents[index]
|
|
||||||
|
for tid, name in self.torrents:
|
||||||
|
if torrent_id == tid:
|
||||||
|
return name
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def set_batch_write(self, batch):
|
||||||
|
# only kept for legacy reasons, don't actually do anything
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_mode(self, mode):
|
||||||
|
reactor.removeReader(self.screen)
|
||||||
|
self.screen = mode
|
||||||
|
self.statusbars.screen = self.screen
|
||||||
|
reactor.addReader(self.screen)
|
||||||
|
|
||||||
def on_client_disconnect(self):
|
def on_client_disconnect(self):
|
||||||
component.stop()
|
component.stop()
|
||||||
|
|
||||||
|
def write(self, s):
|
||||||
|
if self.interactive:
|
||||||
|
self.events.append(s)
|
||||||
|
else:
|
||||||
|
print colors.strip_colors(s)
|
||||||
|
|
Loading…
Reference in New Issue