1 | #!/usr/bin/env python |
---|
2 | # |
---|
3 | # main.py |
---|
4 | # |
---|
5 | # Copyright (C) 2008-2009 Ido Abramovich <ido.deluge@gmail.com> |
---|
6 | # |
---|
7 | # Deluge is free software. |
---|
8 | # |
---|
9 | # You may redistribute it and/or modify it under the terms of the |
---|
10 | # GNU General Public License, as published by the Free Software |
---|
11 | # Foundation; either version 3 of the License, or (at your option) |
---|
12 | # any later version. |
---|
13 | # |
---|
14 | # deluge is distributed in the hope that it will be useful, |
---|
15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
---|
17 | # See the GNU General Public License for more details. |
---|
18 | # |
---|
19 | # You should have received a copy of the GNU General Public License |
---|
20 | # along with deluge. If not, write to: |
---|
21 | # The Free Software Foundation, Inc., |
---|
22 | # 51 Franklin Street, Fifth Floor |
---|
23 | # Boston, MA 02110-1301, USA. |
---|
24 | # |
---|
25 | # In addition, as a special exception, the copyright holders give |
---|
26 | # permission to link the code of portions of this program with the OpenSSL |
---|
27 | # library. |
---|
28 | # You must obey the GNU General Public License in all respects for all of |
---|
29 | # the code used other than OpenSSL. If you modify file(s) with this |
---|
30 | # exception, you may extend this exception to your version of the file(s), |
---|
31 | # but you are not obligated to do so. If you do not wish to do so, delete |
---|
32 | # this exception statement from your version. If you delete this exception |
---|
33 | # statement from all source files in the program, then also delete it here. |
---|
34 | # |
---|
35 | |
---|
36 | # |
---|
37 | import logging |
---|
38 | logging.disable(logging.ERROR) |
---|
39 | import os, sys |
---|
40 | import optparse |
---|
41 | from deluge.ui.console import UI_PATH |
---|
42 | from deluge.ui.console.colors import Template, make_style, templates, default_style as style |
---|
43 | from deluge.ui.client import aclient as client |
---|
44 | from deluge.ui.common import get_localhost_auth_uri |
---|
45 | import shlex |
---|
46 | |
---|
47 | |
---|
48 | class OptionParser(optparse.OptionParser): |
---|
49 | """subclass from optparse.OptionParser so exit() won't exit.""" |
---|
50 | def exit(self, status=0, msg=None): |
---|
51 | self.values._exit = True |
---|
52 | if msg: |
---|
53 | print msg |
---|
54 | |
---|
55 | def error(self, msg): |
---|
56 | """error(msg : string) |
---|
57 | |
---|
58 | Print a usage message incorporating 'msg' to stderr and exit. |
---|
59 | If you override this in a subclass, it should not return -- it |
---|
60 | should either exit or raise an exception. |
---|
61 | """ |
---|
62 | raise |
---|
63 | |
---|
64 | |
---|
65 | class BaseCommand(object): |
---|
66 | |
---|
67 | usage = 'usage' |
---|
68 | option_list = tuple() |
---|
69 | aliases = [] |
---|
70 | |
---|
71 | |
---|
72 | def complete(self, text, *args): |
---|
73 | return [] |
---|
74 | def handle(self, *args, **options): |
---|
75 | pass |
---|
76 | |
---|
77 | @property |
---|
78 | def name(self): |
---|
79 | return 'base' |
---|
80 | |
---|
81 | @property |
---|
82 | def epilog(self): |
---|
83 | return self.__doc__ |
---|
84 | |
---|
85 | def split(self, text): |
---|
86 | return shlex.split(text) |
---|
87 | |
---|
88 | def create_parser(self): |
---|
89 | return OptionParser(prog = self.name, |
---|
90 | usage = self.usage, |
---|
91 | epilog = self.epilog, |
---|
92 | option_list = self.option_list) |
---|
93 | |
---|
94 | def match_torrents(array=None): |
---|
95 | global torrents |
---|
96 | if array is None: |
---|
97 | array = list() |
---|
98 | torrents = [] |
---|
99 | array = set(array) |
---|
100 | def _got_session_state(tors): |
---|
101 | if not array: |
---|
102 | torrents.extend(tors) |
---|
103 | return |
---|
104 | tors = set(tors) |
---|
105 | torrents.extend(list(tors.intersection(array))) |
---|
106 | return |
---|
107 | client.get_session_state(_got_session_state) |
---|
108 | client.force_call() |
---|
109 | return torrents |
---|
110 | |
---|
111 | def load_commands(command_dir, exclude=[]): |
---|
112 | def get_command(name): |
---|
113 | return getattr(__import__('deluge.ui.console.commands.%s' % name, {}, {}, ['Command']), 'Command')() |
---|
114 | |
---|
115 | try: |
---|
116 | commands = [] |
---|
117 | for filename in os.listdir(command_dir): |
---|
118 | if filename.split('.')[0] in exclude or filename.startswith('_'): |
---|
119 | continue |
---|
120 | if not (filename.endswith('.pyc') or filename.endswith('.py')): |
---|
121 | continue |
---|
122 | cmd = get_command(filename.split('.')[len(filename.split('.')) - 2]) |
---|
123 | aliases = [ filename.split('.')[len(filename.split('.')) - 2] ] |
---|
124 | aliases.extend(cmd.aliases) |
---|
125 | for a in aliases: |
---|
126 | commands.append((a, cmd)) |
---|
127 | return dict(commands) |
---|
128 | except OSError, e: |
---|
129 | return {} |
---|
130 | |
---|
131 | class ConsoleUI(object): |
---|
132 | prompt = '>>> ' |
---|
133 | |
---|
134 | def __init__(self, args=None): |
---|
135 | client.set_core_uri(get_localhost_auth_uri("http://localhost:58846")) |
---|
136 | self._commands = load_commands(os.path.join(UI_PATH, 'commands')) |
---|
137 | if args: |
---|
138 | self.precmd() |
---|
139 | #allow multiple commands split by ";" |
---|
140 | for arg in args.split(";"): |
---|
141 | self.onecmd(arg) |
---|
142 | self.postcmd() |
---|
143 | sys.exit(0) |
---|
144 | |
---|
145 | def completedefault(self, *ignored): |
---|
146 | """Method called to complete an input line when no command-specific |
---|
147 | method is available. |
---|
148 | |
---|
149 | By default, it returns an empty list. |
---|
150 | |
---|
151 | """ |
---|
152 | return [] |
---|
153 | |
---|
154 | def completenames(self, text, *ignored): |
---|
155 | return [n for n in self._commands.keys() if n.startswith(text)] |
---|
156 | |
---|
157 | def complete(self, text, state): |
---|
158 | """Return the next possible completion for 'text'. |
---|
159 | If a command has not been entered, then complete against command list. |
---|
160 | Otherwise try to call complete_<command> to get list of completions. |
---|
161 | """ |
---|
162 | if state == 0: |
---|
163 | import readline |
---|
164 | origline = readline.get_line_buffer() |
---|
165 | line = origline.lstrip() |
---|
166 | stripped = len(origline) - len(line) |
---|
167 | begidx = readline.get_begidx() - stripped |
---|
168 | endidx = readline.get_endidx() - stripped |
---|
169 | if begidx>0: |
---|
170 | cmd = line.split()[0] |
---|
171 | if cmd == '': |
---|
172 | compfunc = self.completedefault |
---|
173 | else: |
---|
174 | try: |
---|
175 | compfunc = getattr(self._commands[cmd], 'complete') |
---|
176 | except AttributeError: |
---|
177 | compfunc = self.completedefault |
---|
178 | else: |
---|
179 | compfunc = self.completenames |
---|
180 | self.completion_matches = compfunc(text, line, begidx, endidx) |
---|
181 | try: |
---|
182 | return self.completion_matches[state] |
---|
183 | except IndexError: |
---|
184 | return None |
---|
185 | |
---|
186 | def preloop(self): |
---|
187 | pass |
---|
188 | |
---|
189 | def postloop(self): |
---|
190 | pass |
---|
191 | |
---|
192 | def precmd(self): |
---|
193 | pass |
---|
194 | |
---|
195 | def onecmd(self, line): |
---|
196 | if not line: |
---|
197 | return |
---|
198 | cmd, _, line = line.partition(' ') |
---|
199 | try: |
---|
200 | parser = self._commands[cmd].create_parser() |
---|
201 | except KeyError: |
---|
202 | print templates.ERROR('unknown command: %s' % cmd) |
---|
203 | return |
---|
204 | args = self._commands[cmd].split(line) |
---|
205 | options, args = parser.parse_args(args) |
---|
206 | if not getattr(options, '_exit', False): |
---|
207 | try: |
---|
208 | self._commands[cmd].handle(*args, **options.__dict__) |
---|
209 | except StopIteration, e: |
---|
210 | raise |
---|
211 | except Exception, e: |
---|
212 | print templates.ERROR(str(e)) |
---|
213 | |
---|
214 | def postcmd(self): |
---|
215 | client.force_call() |
---|
216 | |
---|
217 | def cmdloop(self): |
---|
218 | self.preloop() |
---|
219 | try: |
---|
220 | import readline |
---|
221 | self.old_completer = readline.get_completer() |
---|
222 | readline.set_completer(self.complete) |
---|
223 | readline.parse_and_bind("tab: complete") |
---|
224 | except ImportError: |
---|
225 | pass |
---|
226 | |
---|
227 | while True: |
---|
228 | try: |
---|
229 | line = raw_input(templates.prompt(self.prompt)).strip() |
---|
230 | except EOFError: |
---|
231 | break |
---|
232 | except Exception, e: |
---|
233 | print e |
---|
234 | continue |
---|
235 | try: |
---|
236 | self.precmd() |
---|
237 | self.onecmd(line) |
---|
238 | self.postcmd() |
---|
239 | except StopIteration: |
---|
240 | break |
---|
241 | self.postloop() |
---|
242 | print |
---|
243 | run = cmdloop |
---|
244 | |
---|
245 | if __name__ == '__main__': |
---|
246 | ui = ConsoleUI() |
---|
247 | ui.precmd() |
---|
248 | ui.onecmd(' '.join(sys.argv[1:])) |
---|
249 | ui.postcmd() |
---|