Some new bug fixes.
[AurShell.git] / pyshell.py
blob6d439f548daca71ce85653c4e189933b2718f07b
1 #!/usr/bin/python
2 # -*- coding: utf-8-*-
4 """
5 Aur Shell is simple shell framework.
7 To use it, write some plugins that Aur Shell would use.
8 """
10 import cmd
11 import string
12 import sys
13 import os
14 import os.path
15 import types
16 import readline
18 import conf
19 import plugload
20 from showinfo import Put
22 __version__ = "0.1"
25 class AurShell(cmd.Cmd):
26 """
27 Interactive shell framework.
28 To use it, write some modules, becouse by default it's very poor shell.
29 """
30 def __init__(self, init_message=""):
31 cmd.Cmd.__init__(self)
32 self.put = Put()
33 # prefix for method callable by shell
34 self.cmdprefix = "do_"
35 self.prompt = conf.shell_prompt
36 self.commands = self.load_plugins(conf.modules_path)
37 # load history, create empty file if history doesn't exist
38 if not os.path.isfile(conf.history_file):
39 open(conf.history_file, "w").close()
40 readline.read_history_file(conf.history_file)
41 readline.set_history_length(conf.history_length)
43 if init_message:
44 self.intro = init_message
45 else:
46 self.intro = "\n\tWelcome to aurShell v%(__version__)s\n" % globals()
49 def load_plugins(self, modules_path):
50 """Load all commands modules from modules_path direcotory."""
51 command_modules = []
52 # adding plugins path to sys.path
53 if modules_path not in sys.path:
54 sys.path.append(modules_path)
55 # importing all modules from modules_path
56 for module_name in os.listdir(modules_path):
57 module_path = os.path.abspath(
58 string.join(modules_path, module_name))
59 (module_name, module_extension) = os.path.splitext(module_name)
60 # if it shouldn't be load
61 if os.path.islink(module_path) or \
62 module_path == __file__ or \
63 module_extension != ".py":
64 continue
65 for key in sys.modules.keys():
66 if key == module_name:
67 del sys.modules[key]
68 module = __import__(module_name)
69 command_modules.append({"obj": module_name, "name": module,})
70 # search for class in each module
71 module_objs = {}
72 for module in command_modules:
73 for obj in dir(module["name"]):
74 # TODO
75 # better class serch 2008-01-27 16:56:42
76 if not obj.startswith("_"):
77 try:
78 # check if it's method
79 x = getattr(module['name'], obj)(self.put)
80 # create instance for each class
81 # as agrument give it Put() class instance
82 module_objs[obj] = \
83 getattr(module['name'], obj)(self.put)
84 except TypeError:
85 pass
86 return module_objs
88 def default(self, cmd=None):
89 """Run commands.
91 cmd = (<class>, [method], [arg1], [arg2], ...)
92 """
93 # cmd[0] should be class name
94 # cmd[1] should be method name (or arugmet if class is callable)
95 # cmd[1] can be empty
96 # cmd[2:] should be method argumments, can be empty
97 cmd = cmd.split()
98 # check if alias exists and if so, replace command
99 if cmd[0] in conf.alias.keys():
100 cmd[0] = conf.alias[cmd[0]]
101 self.onecmd(" ".join(cmd))
102 # operations for single command with no arguments
103 elif len(cmd) == 1:
104 # if there's no such command (or plugin class)
105 if not cmd[0] in self.commands.keys():
106 self.put("%s : command not found." % cmd[0])
107 # for only one argument, try to run __call__() method with
108 # no arguments
109 elif "__call__" in dir(self.commands[cmd[0]]):
110 getattr(self.commands[cmd[0]], "__call__")()
111 else:
112 self.put("%s : bad usege. Try to run help." % cmd[0])
113 # if command was called with arguments
114 elif len(cmd) > 1:
115 cmd[1] = self.cmdprefix + cmd[1]
116 if not cmd[0] in self.commands.keys():
117 self.put("%s : command not found." % cmd[0])
118 # if method named arg[1] exist in class arg[0], try to run it
119 elif cmd[1] in dir(self.commands[cmd[0]]):
120 #print dir(self.commands[cmd[0]])
121 try:
122 getattr(self.commands[cmd[0]], cmd[1])(*cmd[2:])
123 except TypeError:
124 # show __doc__ if exist
125 doc = getattr(self.commands[cmd[0]], cmd[1])
126 if doc.__doc__:
127 self.put(doc.__doc__)
128 else:
129 self.put("%s : bad usage" % cmd[1][3:])
130 # if there's no such method arg[1] in class arg[0],
131 # try to run class.__call__(args..)
132 else:
133 try:
134 self.commands[cmd[0]](*cmd[1:])
135 except TypeError:
136 # object is not callable
137 self.put("%s : bad usage" % cmd[0])
140 def completenames(self, text, *ignored):
141 """Complete commands"""
142 dotext = self.cmdprefix + text
143 # local methods
144 local_cmd_list = [a[3:] + " " for a in self.get_names() if a.startswith(dotext)]
145 # + all metrods from modules
146 module_cmd_list = [a + " " for a in self.commands.keys() if a.startswith(text)]
147 return local_cmd_list + module_cmd_list
150 def completedefault(self, text, line, begidx, endidx):
151 """Complete commands argument"""
152 dotext = self.cmdprefix + text
153 line = line.split()
154 # if only commands was given
155 if len(line) == 1:
156 cmds = [a[3:] + " " for a in dir(self.commands[line[0]]) \
157 if a.startswith(dotext)]
158 elif len(line) == 2:
159 cmds = [a[3:] + " " for a in dir(self.commands[line[0]]) \
160 if a.startswith(dotext)]
161 # else don't complete (or should I?)
162 else:
163 cmds = []
164 return cmds
167 def do_help(self, arg):
168 """Show help for commands"""
169 arg = arg.split()
170 if not arg:
171 self.put("Usage: help <command>")
172 else:
173 try:
174 # try to run help() method
175 self.put(getattr(self.commands[arg[0]], "help")())
176 except AttributeError:
177 # try to show __doc__
178 if self.commands[arg[0]].__doc__:
179 self.put(self.commands[arg[0]].__doc__)
180 else:
181 self.put("No help found.")
182 except KeyError:
183 self.put("No help found.")
186 def do_clear(self, *ignored):
187 """Clear the screen"""
188 # TODO 2008-02-09 20:44:50
189 self.put("Todo, sorry")
192 def do_history(self, hnumb=None, *ignored):
193 """Show the history"""
194 # TODO better history listing
195 # 2008-01-27 18:51:22
196 # print whole history
197 if not hnumb:
198 for number in range(1, conf.history_length):
199 cmd = readline.get_history_item(number)
200 if not cmd:
201 break
202 self.put("%6d %s" % (number, cmd))
203 # for history range 12-22 or -22 or 22-
204 else:
205 try:
206 if "-" in hnumb:
207 if hnumb[-1] == "-" or hnumb[0] == "-":
208 start = int(hnumb.replace("-", " "))
209 end = conf.history_length
210 else:
211 start, end = hnumb.split("-")
212 start = int(start)
213 end = int(end) + 1
214 for number in range(start, end):
215 cmd = readline.get_history_item(number)
216 if not cmd:
217 break
218 self.put("%6d %s" % (number, cmd))
219 else:
220 hnumb = int(hnumb)
221 self.put(readline.get_history_item(hnumb))
222 except ValueError:
223 self.put("""Bad value.
224 Usage: history <number or range>
225 history 11-20 -> from 11 to 20
226 history 22- -> from 22 fo the end of history file
227 history -22 -> same as 22-""")
230 def do_quit(self, *ignored):
231 """Quit from shell"""
232 if conf.history_length:
233 readline.write_history_file(conf.history_file)
234 print ""
235 sys.exit(1)
237 # function aliases
238 do_EOF = do_quit
239 do_exit = do_quit
243 if __name__ == "__main__":
244 shell = AurShell()
245 try:
246 shell.cmdloop()
247 except KeyboardInterrupt:
248 # TODO 2008-01-27 16:56:31
249 sys.exit(shell.do_quit())