Author: andar
Revision: 5073
Log:
Start work on new Console UI .. Don't expect anything to work, cause
most of it doesn't.
Diff:
Modified: trunk/deluge/ui/console/colors.py
===================================================================
--- trunk/deluge/ui/console/colors.py 2009-04-17 21:24:49 UTC (rev 5072)
+++ trunk/deluge/ui/console/colors.py 2009-04-18 05:35:03 UTC (rev 5073)
@@ -1,8 +1,7 @@
-#!/usr/bin/env python
#
# colors.py
#
-# Copyright (C) 2008-2009 Ido Abramovich <[email protected]>
+# Copyright (C) 2009 Andrew Resch <[email protected]>
#
# Deluge is free software.
#
@@ -22,87 +21,108 @@
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301, USA.
#
-import re, sys
-def color(string, fg=None, attrs=[], bg=None, keep_open=False, input=False):
- if isinstance(attrs, basestring):
- attrs = [attrs]
- attrs = map(str.lower, attrs)
- ansi_reset = "\x1b[0m"
- if input:
- ansi_reset = '\001'+ansi_reset+'\002'
- if len(attrs) == 1 and 'reset' in attrs:
- return ansi_reset
- colors = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan',
'white']
- attributes = ['reset', 'bright', 'dim', None, 'underscore', 'blink',
'reverse', 'hidden']
- _fg = 30 + colors.index(fg.lower()) if fg and fg.lower() in colors else
None
- _bg = 40 + colors.index(bg.lower()) if bg and bg.lower() in colors else
None
- _attrs = [ str(attributes.index(a)) for a in attrs if a in attributes]
- color_vals = map(str, filter(lambda x: x is not None, [_fg, _bg]))
- color_vals.extend(_attrs)
- reset_cmd = ansi_reset if not keep_open else ''
- color_code = '\x1b['+';'.join(color_vals)+'m'
- if input:
- color_code = '\001'+color_code+'\002'
- return color_code+string+reset_cmd
+import curses
-def make_style(*args, **kwargs):
- return lambda text: color(text, *args, **kwargs)
+colors = [
+ 'COLOR_BLACK',
+ 'COLOR_BLUE',
+ 'COLOR_CYAN',
+ 'COLOR_GREEN',
+ 'COLOR_MAGENTA',
+ 'COLOR_RED',
+ 'COLOR_WHITE',
+ 'COLOR_YELLOW'
+]
-default_style = {
- 'black' : make_style(fg='black'),
- 'red' : make_style(fg='red'),
- 'green' : make_style(fg='green'),
- 'yellow' : make_style(fg='yellow'),
- 'blue' : make_style(fg='blue'),
- 'magenta' : make_style(fg='magenta'),
- 'cyan' : make_style(fg='cyan'),
- 'white' : make_style(fg='white'),
+# {(fg, bg): pair_number, ...}
+color_pairs = {
+ ("white", "black"): 0 # Special case, can't be changed
+}
- 'bold_black' : make_style(fg='black', attrs='bright'),
- 'bold_red' : make_style(fg='red', attrs='bright'),
- 'bold_green' : make_style(fg='green', attrs='bright'),
- 'bold_yellow' : make_style(fg='yellow', attrs='bright'),
- 'bold_blue' : make_style(fg='blue', attrs='bright'),
- 'bold_magenta' : make_style(fg='magenta', attrs='bright'),
- 'bold_cyan' : make_style(fg='cyan', attrs='bright'),
- 'bold_white' : make_style(fg='white', attrs='bright'),
+# Some default color schemes
+schemes = {
+ "input": ("white", "black"),
+ "status": ("yellow", "blue", "bold"),
+ "info": ("white", "black", "bold"),
+ "error": ("red", "black", "bold"),
+ "success": ("green", "black", "bold")
}
-class Template(str):
- regex = re.compile(r'{{\s*(?P<style>.*?)\((?P<arg>.*?)\)\s*}}')
- style = default_style
- def __new__(self, text):
- return str.__new__(self, Template.regex.sub(lambda mo:
Template.style[mo.group('style')](mo.group('arg')), text))
- def __call__(self, *args, **kwargs):
- if kwargs:
- return str(self) % kwargs
+def init_colors():
+ # Create the color_pairs dict
+ counter = 1
+ for fg in colors:
+ for bg in colors:
+ if fg == "COLOR_WHITE" and bg == "COLOR_BLACK":
+ continue
+ color_pairs[(fg[6:].lower(), bg[6:].lower())] = counter
+ curses.init_pair(counter, getattr(curses, fg), getattr(curses, bg))
+ counter += 1
+
+class BadColorString(Exception):
+ pass
+
+def parse_color_string(s):
+ """
+ Parses a string and returns a list of 2-tuples (color, string).
+
+ :param s:, string to parse
+
+ """
+ ret = []
+ # Keep track of where the strings
+ col_index = 0
+ while s.find("{{") != -1:
+ begin = s.find("{{")
+ end = s.find("}}")
+ if end == -1:
+ raise BadColorString("Missing closing '}}'")
+
+ # Get a list of attributes in the bracketed section
+ attrs = s[begin+2:end].split(",")
+
+ if len(attrs) == 1 and not attrs:
+ raise BadColorString("No description in {{ }}")
+
+ def apply_attrs(cp, a):
+ # This function applies any additional attributes as necessary
+ if len(a) > 2:
+ for attr in a[2:]:
+ cp |= getattr(curses, "A_" + attr.upper())
+ return cp
+
+ # Check for a builtin type first
+ if attrs[0] in schemes:
+ # Get the color pair number
+ color_pair = curses.color_pair(color_pairs[(schemes[attrs[0]][0],
schemes[attrs[0]][1])])
+ color_pair = apply_attrs(color_pair, schemes[attrs[0]])
+
else:
- return str(self) % args
+ # This is a custom color scheme
+ fg = attrs[0]
+ if len(attrs) > 1:
+ bg = attrs[1]
+ else:
+ # Default to 'black' if no bg is chosen
+ bg = "black"
-class InputTemplate(Template):
- """This class is similar to Template, but the escapes are wrapped in \001
- and \002 so that readline can properly know the length of each line and
- can wrap lines accordingly. Use this class for any colored text which
- needs to be used in input prompts, such as in calls to raw_input()."""
- input_codes = re.compile('(\x1b\[.*?m)')
- def __new__(self, text):
- regular_string = InputTemplate.regex.sub(lambda mo:
InputTemplate.style[mo.group('style')](mo.group('arg')) , text)
- return str.__new__(self, InputTemplate.input_codes.sub(r'\001\1\002',
regular_string))
+ color_pair = curses.color_pair(color_pairs[(fg, bg)])
+ # Check for additional attributes and OR them to the color_pair
+ color_pair = apply_attrs(color_pair, attrs)
-class struct(object):
- pass
+ # We need to find the text now, so lets try to find another {{ and if
+ # there isn't one, then it's the rest of the string
+ next_begin = s.find("{{", end)
+ if next_begin == -1:
+ ret.append((color_pair, s[end+2:]))
+ break
+ else:
+ ret.append((color_pair, s[end+2:next_begin]))
+ s = s[next_begin:]
-templates = struct()
-templates.prompt = InputTemplate('{{bold_white(%s)}}')
-templates.ERROR = Template('{{bold_red( * %s)}}')
-templates.SUCCESS = Template('{{bold_green( * %s)}}')
-templates.help = Template(' * {{bold_blue(%-*s)}} %s')
-templates.info_general = Template('{{bold_blue(*** %s:)}} %s')
-templates.info_transfers = Template('{{bold_green(*** %s:)}} %s')
-templates.info_network = Template('{{bold_white(*** %s:)}} %s')
-templates.info_files_header = Template('{{bold_cyan(*** %s:)}}')
-templates.info_peers_header = Template('{{bold_magenta(*** %s:)}}')
-templates.info_peers = Template('\t * {{bold_blue(%-22s)}}
{{bold_green(%-25s)}} {{bold_cyan(Up: %-12s)}} {{bold_magenta(Down: %-12s)}}')
-templates.config_display = Template(' * {{bold_blue(%s)}}: %s')
+ if not ret:
+ # There was no color scheme so we add it with a 0 for white on black
+ ret = [(0, s)]
+ return ret
Modified: trunk/deluge/ui/console/commands/add.py
===================================================================
--- trunk/deluge/ui/console/commands/add.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/add.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python
#
# add.py
#
# Copyright (C) 2008-2009 Ido Abramovich <[email protected]>
+# Copyright (C) 2009 Andrew Resch <[email protected]>
#
# Deluge is free software.
#
@@ -24,10 +24,11 @@
#
from deluge.ui.console.main import BaseCommand, match_torrents
from deluge.ui.console import mapping
-from deluge.ui.console.colors import templates
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+from deluge.ui.client import client
from optparse import make_option
import os
+import base64
class Command(BaseCommand):
"""Add a torrent"""
@@ -39,19 +40,18 @@
usage = "Usage: add [-p <save-location>] <torrent-file> [<torrent-file>
...]"
def handle(self, *args, **options):
- if options['path'] is None:
- def _got_config(configs):
- global save_path
- save_path = configs['download_location']
- client.get_config(_got_config)
- client.force_call()
- options['path'] = save_path
- else:
- client.set_config({'download_location': options['path']})
- if not options['path']:
- print templates.ERROR("There's no save-path specified. You must
specify a path to save the downloaded files.")
- return
- try:
- client.add_torrent_file(args)
- except Exception, msg:
- print templates.ERROR("Error: %s" % str(msg))
+ t_options = {}
+ if options["path"]:
+ t_options["download_location"] = options["path"]
+
+ for arg in args:
+ self.write("{{info}}Attempting to add torrent: %s" % arg)
+ filename = os.path.split(arg)[-1]
+ filedump = base64.encodestring(open(arg).read())
+
+ def on_success(result):
+ self.write("{{success}}Torrent added!")
+ def on_fail(result):
+ self.write("{{error}}Torrent was not added! %s" % result)
+
+ client.core.add_torrent_file(filename, filedump,
t_options).addCallback(on_success).addErrback(on_fail)
Modified: trunk/deluge/ui/console/commands/config.py
===================================================================
--- trunk/deluge/ui/console/commands/config.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/config.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -24,8 +24,8 @@
#
from deluge.ui.console.main import BaseCommand, match_torrents
-from deluge.ui.console.colors import templates, default_style as style
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+from deluge.ui.client import client
from optparse import make_option
import re
@@ -136,4 +136,3 @@
def split(self, text):
return str.split(text)
-
Modified: trunk/deluge/ui/console/commands/connect.py
===================================================================
--- trunk/deluge/ui/console/commands/connect.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/connect.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -23,14 +23,19 @@
# Boston, MA 02110-1301, USA.
#
from deluge.ui.console.main import BaseCommand
-from deluge.ui.console.colors import templates, default_style as style
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+from deluge.ui.client import client
class Command(BaseCommand):
"""Connect to a new deluge server."""
- def handle(self, host='localhost', port='58846', **options):
+ def handle(self, host='localhost', port='58846', username="", password="",
**options):
port = int(port)
- if host[:7] != "http://":
- host = "http://" + host
- client.set_core_uri("%s:%d" % (host, port))
- print templates.SUCCESS('connected to %s:%d' % (host, port))
+ d = client.connect(host, port, username, password)
+ def on_connect(result):
+ print templates.SUCCESS('Connected to %s:%d!' % (host, port))
+
+ def on_connect_fail(result):
+ print templates.ERROR("Failed to connect to %s:%d!" % (host, port))
+
+ d.addCallback(on_connect)
+ d.addErrback(on_connect_fail)
Modified: trunk/deluge/ui/console/commands/debug.py
===================================================================
--- trunk/deluge/ui/console/commands/debug.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/debug.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -23,8 +23,8 @@
# Boston, MA 02110-1301, USA.
#
from deluge.ui.console.main import BaseCommand
-from deluge.ui.client import aclient as client
-from deluge.ui.console.colors import templates, default_style as style
+from deluge.ui.client import client
+import deluge.ui.console.colors as colors
import logging
class Command(BaseCommand):
Modified: trunk/deluge/ui/console/commands/halt.py
===================================================================
--- trunk/deluge/ui/console/commands/halt.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/halt.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -24,8 +24,8 @@
#
from deluge.ui.console.main import BaseCommand, match_torrents
from deluge.ui.console import mapping
-from deluge.ui.console.colors import templates
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+from deluge.ui.client import client
class Command(BaseCommand):
"Shutdown the deluge server."
Modified: trunk/deluge/ui/console/commands/help.py
===================================================================
--- trunk/deluge/ui/console/commands/help.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/help.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -24,7 +24,8 @@
#
from deluge.ui.console import UI_PATH
from deluge.ui.console.main import BaseCommand, load_commands
-from deluge.ui.console.colors import templates
+import deluge.ui.console.colors as colors
+#from deluge.ui.console.colors import templates
import os
class Command(BaseCommand):
@@ -35,30 +36,32 @@
def __init__(self):
BaseCommand.__init__(self)
# get a list of commands, exclude 'help' so we won't run into a
recursive loop.
- self._commands = load_commands(os.path.join(UI_PATH,'commands'),
exclude=['help'])
+ self._commands = load_commands(os.path.join(UI_PATH,'commands'), None,
exclude=['help'])
self._commands['help'] = self
def handle(self, *args, **options):
if args:
if len(args) > 1:
- print usage
+ #print usage
+ self.write(usage)
return
try:
cmd = self._commands[args[0]]
except KeyError:
- print templates.ERROR('unknown command %r' % args[0])
+ #print templates.ERROR('unknown command %r' % args[0])
+ self.write("{{error}}Unknown command %r" % args[0])
return
try:
parser = cmd.create_parser()
- print parser.format_help()
+ self.write(parser.format_help())
except AttributeError, e:
- print cmd.__doc__ or 'No help for this command'
+ self.write(cmd.__doc__ or 'No help for this command')
else:
max_length = max( len(k) for k in self._commands)
for cmd in sorted(self._commands):
- print templates.help(max_length, cmd,
self._commands[cmd].__doc__ or '')
- print
- print 'for help on a specific command, use "<command> --help"'
+ self.write("{{info}}" + cmd + "{{input}} - " +
self._commands[cmd].__doc__ or '')
+ self.write("")
+ self.write('For help on a specific command, use "<command>
--help"')
def complete(self, text, *args):
return [ x for x in self._commands.keys() if x.startswith(text) ]
Modified: trunk/deluge/ui/console/commands/info.py
===================================================================
--- trunk/deluge/ui/console/commands/info.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/info.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -25,8 +25,9 @@
from deluge.ui.console.main import BaseCommand, match_torrents
from deluge.ui.console import mapping
-from deluge.ui.console.colors import templates
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+#from deluge.ui.console.colors import templates
+from deluge.ui.client import client
import deluge.common as common
from optparse import make_option
@@ -72,16 +73,19 @@
def handle(self, *args, **options):
- args = mapping.to_ids(args)
- self.torrents = match_torrents(args)
- for tor in self.torrents:
- self.show_info(tor, options.get('verbose'))
+ def on_to_ids(result):
+ def on_match_torrents(torrents):
+ for torrent in torrents:
+ self.show_info(torrent, options.get("verbose"))
- def complete(self, text, *args):
- torrents = match_torrents()
- names = mapping.get_names(torrents)
- return [ x[1] for x in names if x[1].startswith(text) ]
+ match_torrents(result).addCallback(on_match_torrents)
+ mapping.to_ids(args).addCallback(on_to_ids)
+# def complete(self, text, *args):
+# torrents = match_torrents()
+# names = mapping.get_names(torrents)
+# return [ x[1] for x in names if x[1].startswith(text) ]
+
def show_info(self, torrent, verbose):
def _got_torrent_status(state):
print templates.info_general('ID', torrent)
@@ -124,4 +128,4 @@
print templates.info_peers(str(peer['ip']),
unicode(client_str),
str(common.fspeed(peer['up_speed'])),
str(common.fspeed(peer['down_speed'])))
print ""
- client.get_torrent_status(_got_torrent_status, torrent, status_keys)
+ client.core.get_torrent_status(torrent,
status_keys).addCallback(_got_torrent_status)
Modified: trunk/deluge/ui/console/commands/pause.py
===================================================================
--- trunk/deluge/ui/console/commands/pause.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/pause.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -24,8 +24,8 @@
#
from deluge.ui.console.main import BaseCommand, match_torrents
from deluge.ui.console import mapping
-from deluge.ui.client import aclient as client
-from deluge.ui.console.colors import templates, default_style as style
+from deluge.ui.client import client
+import deluge.ui.console.colors as colors
class Command(BaseCommand):
"""Pause a torrent"""
Modified: trunk/deluge/ui/console/commands/quit.py
===================================================================
--- trunk/deluge/ui/console/commands/quit.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/quit.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -22,7 +22,7 @@
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301, USA.
#
-from deluge.ui.client import aclient as client
+from deluge.ui.client import client
from deluge.ui.console.main import BaseCommand
class Command(BaseCommand):
@@ -31,4 +31,3 @@
def handle(self, *args, **options):
print "Thanks!"
raise StopIteration
-
Modified: trunk/deluge/ui/console/commands/resume.py
===================================================================
--- trunk/deluge/ui/console/commands/resume.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/resume.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -24,8 +24,8 @@
#
from deluge.ui.console.main import BaseCommand, match_torrents
from deluge.ui.console import mapping
-from deluge.ui.client import aclient as client
-from deluge.ui.console.colors import templates, default_style as style
+from deluge.ui.client import client
+import deluge.ui.console.colors as colors
class Command(BaseCommand):
"""Resume a torrent"""
@@ -49,4 +49,3 @@
torrents = match_torrents()
names = mapping.get_names(torrents)
return [ x[1] for x in names if x[1].startswith(text) ]
-
Modified: trunk/deluge/ui/console/commands/rm.py
===================================================================
--- trunk/deluge/ui/console/commands/rm.py 2009-04-17 21:24:49 UTC (rev
5072)
+++ trunk/deluge/ui/console/commands/rm.py 2009-04-18 05:35:03 UTC (rev
5073)
@@ -24,8 +24,8 @@
#
from deluge.ui.console.main import BaseCommand, match_torrents
from deluge.ui.console import mapping
-from deluge.ui.console.colors import templates
-from deluge.ui.client import aclient as client
+import deluge.ui.console.colors as colors
+from deluge.ui.client import client
from optparse import make_option
import os
Modified: trunk/deluge/ui/console/main.py
===================================================================
--- trunk/deluge/ui/console/main.py 2009-04-17 21:24:49 UTC (rev 5072)
+++ trunk/deluge/ui/console/main.py 2009-04-18 05:35:03 UTC (rev 5073)
@@ -1,8 +1,8 @@
-#!/usr/bin/env python
#
# main.py
#
# Copyright (C) 2008-2009 Ido Abramovich <[email protected]>
+# Copyright (C) 2009 Andrew Resch <[email protected]>
#
# Deluge is free software.
#
@@ -22,16 +22,23 @@
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301, USA.
#
-import logging
-logging.disable(logging.ERROR)
+
import os, sys
import optparse
from deluge.ui.console import UI_PATH
-from deluge.ui.console.colors import Template, make_style, templates,
default_style as style
-from deluge.ui.client import aclient as client
-from deluge.ui.common import get_localhost_auth_uri
+#from deluge.ui.console.colors import Template, make_style, templates,
default_style as style
+import deluge.component as component
+from deluge.ui.client import client
+import deluge.common
+from deluge.ui.coreconfig import CoreConfig
+from deluge.ui.console.statusbars import StatusBars
+
+from twisted.internet import defer, reactor
import shlex
+import screen
+import colors
+from deluge.log import LOG as log
class OptionParser(optparse.OptionParser):
"""subclass from optparse.OptionParser so exit() won't exit."""
@@ -49,7 +56,6 @@
"""
raise
-
class BaseCommand(object):
usage = 'usage'
@@ -79,24 +85,24 @@
epilog = self.epilog,
option_list = self.option_list)
-def match_torrents(array=None):
- global torrents
- if array is None:
- array = list()
- torrents = []
+
+def match_torrents(array=[]):
+ # Make sure we don't have any duplicates
array = set(array)
+ # We return this defer and it will be fired once we received the session
+ # state and intersect the data.
+ d = defer.Deferred()
+
def _got_session_state(tors):
if not array:
- torrents.extend(tors)
- return
- tors = set(tors)
- torrents.extend(list(tors.intersection(array)))
- return
- client.get_session_state(_got_session_state)
- client.force_call()
- return torrents
+ d.callback(tors)
+ d.callback(list(tors.intersection(array)))
-def load_commands(command_dir, exclude=[]):
+ client.core.get_session_state().addCallback(_got_session_state)
+
+ return d
+
+def load_commands(command_dir, write_func, exclude=[]):
def get_command(name):
return getattr(__import__('deluge.ui.console.commands.%s' % name, {},
{}, ['Command']), 'Command')()
@@ -106,6 +112,8 @@
if filename.split('.')[0] in exclude or filename.startswith('_')
or not filename.endswith('.py'):
continue
cmd = get_command(filename[:-3])
+ # Hack to give the commands a write function
+ cmd.write = write_func
aliases = [ filename[:-3] ]
aliases.extend(cmd.aliases)
for a in aliases:
@@ -114,80 +122,103 @@
except OSError, e:
return {}
-class ConsoleUI(object):
- prompt = '>>> '
-
+class ConsoleUI(component.Component):
def __init__(self, args=None):
- client.set_core_uri(get_localhost_auth_uri("http://localhost:58846"))
- self._commands = load_commands(os.path.join(UI_PATH, 'commands'))
+ component.Component.__init__(self, "ConsoleUI", 2)
+ # Load all the commands
+ self._commands = load_commands(os.path.join(UI_PATH, 'commands'),
self.write)
+
+ # Try to connect to the localhost daemon
+ def on_connect(result):
+ component.start()
+ client.connect().addCallback(on_connect)
+
+ # Set the interactive flag to indicate where we should print the output
+ self.interactive = True
if args:
- self.precmd()
+ self.interactive = False
+ # If we have args, lets process them and quit
#allow multiple commands split by ";"
for arg in args.split(";"):
- self.onecmd(arg)
- self.postcmd()
+ self.do_command(arg)
sys.exit(0)
- def completedefault(self, *ignored):
- """Method called to complete an input line when no command-specific
- method is available.
+ self.coreconfig = CoreConfig()
- By default, it returns an empty list.
+ # We use the curses.wrapper function to prevent the console from
getting
+ # messed up if an uncaught exception is experienced.
+ import curses.wrapper
+ curses.wrapper(self.run)
+ def run(self, stdscr):
"""
- return []
+ This method is called by the curses.wrapper to start the mainloop and
+ screen.
- def completenames(self, text, *ignored):
- return [n for n in self._commands.keys() if n.startswith(text)]
+ :param stdscr: curses screen passed in from curses.wrapper
- def complete(self, text, state):
- """Return the next possible completion for 'text'.
- If a command has not been entered, then complete against command list.
- Otherwise try to call complete_<command> to get list of completions.
"""
- if state == 0:
- import readline
- origline = readline.get_line_buffer()
- line = origline.lstrip()
- stripped = len(origline) - len(line)
- begidx = readline.get_begidx() - stripped
- endidx = readline.get_endidx() - stripped
- if begidx>0:
- cmd = line.split()[0]
- if cmd == '':
- compfunc = self.completedefault
- else:
- try:
- compfunc = getattr(self._commands[cmd], 'complete')
- except AttributeError:
- compfunc = self.completedefault
- else:
- compfunc = self.completenames
- self.completion_matches = compfunc(text, line, begidx, endidx)
- try:
- return self.completion_matches[state]
- except IndexError:
- return None
+ # We want to do an interactive session, so start up the curses screen
and
+ # pass it the function that handles commands
+ colors.init_colors()
+ self.screen = screen.Screen(stdscr, self.do_command)
+ self.statusbars = StatusBars()
- def preloop(self):
- pass
+ self.screen.topbar = "{{status}}Deluge " + deluge.common.get_version()
+ " Console"
+ self.screen.bottombar = "{{status}}"
+ self.screen.refresh()
- def postloop(self):
+ # The Screen object is designed to run as a twisted reader so that it
+ # can use twisted's select poll for non-blocking user input.
+ reactor.addReader(self.screen)
+
+ # Start the twisted mainloop
+ reactor.run()
+
+ def start(self):
pass
- def precmd(self):
+ def update(self):
pass
- def onecmd(self, line):
- if not line:
+ 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)
+ else:
+ print(line)
+
+ def do_command(self, cmd):
+ """
+ Processes a command.
+
+ :param cmd: str, the command string
+
+ """
+ if not cmd:
return
- cmd, _, line = line.partition(' ')
+ cmd, _, line = cmd.partition(' ')
try:
parser = self._commands[cmd].create_parser()
except KeyError:
- print templates.ERROR('unknown command: %s' % cmd)
+ 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
+
options, args = parser.parse_args(args)
if not getattr(options, '_exit', False):
try:
@@ -195,41 +226,4 @@
except StopIteration, e:
raise
except Exception, e:
- print templates.ERROR(str(e))
-
- def postcmd(self):
- client.force_call()
-
- def cmdloop(self):
- self.preloop()
- try:
- import readline
- self.old_completer = readline.get_completer()
- readline.set_completer(self.complete)
- readline.parse_and_bind("tab: complete")
- except ImportError:
- pass
-
- while True:
- try:
- line = raw_input(templates.prompt(self.prompt)).strip()
- except EOFError:
- break
- except Exception, e:
- print e
- continue
- try:
- self.precmd()
- self.onecmd(line)
- self.postcmd()
- except StopIteration:
- break
- self.postloop()
- print
- run = cmdloop
-
-if __name__ == '__main__':
- ui = ConsoleUI()
- ui.precmd()
- ui.onecmd(' '.join(sys.argv[1:]))
- ui.postcmd()
+ self.write("{{error}}" + str(e))
Modified: trunk/deluge/ui/console/mapping.py
===================================================================
--- trunk/deluge/ui/console/mapping.py 2009-04-17 21:24:49 UTC (rev 5072)
+++ trunk/deluge/ui/console/mapping.py 2009-04-18 05:35:03 UTC (rev 5073)
@@ -22,10 +22,11 @@
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301, USA.
#
-from deluge.ui.client import aclient as client
+from deluge.ui.client import client
from deluge.ui.console.main import match_torrents
import re
import logging
+from twisted.internet import defer
_idregex = re.compile(r'^[0-9a-f]{40}$')
@@ -35,40 +36,42 @@
return bool(_idregex.match(arg))
def get_names(torrents):
- global names
- names = []
+ d = defer.Deferred()
def _got_torrents_status(states):
try:
- names.extend(list([ (tid, state['name']) for (tid, state) in
states.items() ]))
+ d.callback(list([ (tid, state['name']) for (tid, state) in
states.items() ]))
except Exception, e:
print e
+ d.errback(e)
- client.get_torrents_status(_got_torrents_status, {'id':torrents}, ['name'])
- client.force_call()
- return names
+ client.core.get_torrents_status({'id':torrents},
['name']).addCallback(_got_torrents_status)
+ return d
def rehash():
global _mapping
- torrents = match_torrents()
- names = get_names(torrents)
- _mapping = dict([(x[1],x[0]) for x in names])
- logging.debug('rehashed torrent name->id mapping')
+ d = defer.Deferred()
+ def on_match_torrents(torrents):
+ def on_get_names(names):
+ _mapping = dict([(x[1],x[0]) for x in names])
+ d.callback()
+ get_names(torrents).addCallback(on_get_names)
+ match_torrents().addCallback(on_match_torrents)
+ return d
def to_ids(args):
- res = []
- rehashed = False
- for i in args:
- if _arg_is_id(i):
- res.append(i)
- else:
- if i in _mapping:
- res.append(_mapping[i])
- elif not rehashed:
- rehash()
+ d = defer.Deferred()
+ def on_rehash(result):
+ res = []
+ for i in args:
+ if _arg_is_id(i):
+ res.append(i)
+ else:
if i in _mapping:
res.append(_mapping[i])
- rehashed = True
- return res
+ d.callback(res)
+ rehash().addCallback(on_rehash)
+ return d
+
def names():
return _mapping.keys()
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"deluge-commit" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/deluge-commit?hl=en
-~----------~----~----~----~------~----~------~--~---