New library for reading, copying, making packages.
[AurShell.git] / pyshell.py
blobfb6f37b279dcd0b1456f4f9901e0e762084b852e
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 from showinfo import Put
21 __version__ = "0.1"
24 class AurShell(cmd.Cmd):
25 """
26 Interactive shell framework.
27 To use it, write some modules, becouse by default it's very poor shell.
28 """
29 def __init__(self):
30 cmd.Cmd.__init__(self)
31 # use it instead of plain `print`
32 self.put = Put()
33 self.conf = conf
34 # prefix for method callable by shell
35 self.cmdprefix = "do_"
36 self.prompt = conf.shell_prompt
37 # plugins dict
38 self.commands = self.load_plugins(conf.modules_path)
39 # intro message
40 self.intro = "\n\tWelcome to aurShell v%(__version__)s\n" % globals()
41 # load history, create empty file if history doesn't exist
42 if not os.path.isfile(conf.history_file):
43 open(conf.history_file, "w").close()
44 readline.read_history_file(conf.history_file)
45 readline.set_history_length(conf.history_length)
46 # create build_dir if doesn't exist
47 if not os.path.isdir(conf.build_dir):
48 os.mkdir(conf.build_dir)
51 def load_plugins(self, modules_path):
52 """Load all commands modules from modules_path direcotory."""
53 command_modules = []
54 # adding plugins path to sys.path
55 if modules_path not in sys.path:
56 sys.path.append(modules_path)
57 # importing all modules from modules_path
58 for module_name in os.listdir(modules_path):
59 module_path = os.path.abspath(
60 string.join(modules_path, module_name))
61 (module_name, module_extension) = os.path.splitext(module_name)
62 # if it shouldn't be load
63 if os.path.islink(module_path) or \
64 module_path == __file__ or \
65 module_extension != ".py":
66 continue
67 for key in sys.modules.keys():
68 if key == module_name:
69 del sys.modules[key]
70 module = __import__(module_name)
71 command_modules.append({"obj": module_name, "name": module,})
72 # search for class in each module
73 module_objs = {}
74 for module in command_modules:
75 for obj in dir(module["name"]):
76 # TODO
77 # better class serch 2008-01-27 16:56:42
78 if not obj.startswith("_"):
79 try:
80 # check if it's method
81 x = getattr(module['name'], obj)(self.put, conf)
82 # create instance for each class
83 # as agrument give it Put() class instance
84 module_objs[obj] = \
85 getattr(module['name'], obj)(self.put, conf)
86 except TypeError:
87 pass
88 return module_objs
91 def precmd(self, cmd):
92 # check if alias exists and if so, replace command
93 for alias in conf.alias.keys():
94 # command should start with alias, but:
95 # - next char should be white space, or
96 # - there's no next char
97 if cmd.startswith(alias) and \
98 (len(cmd) <= len(alias) or cmd[len(alias)] == " "):
99 cmd = cmd.replace(alias, conf.alias[alias], 1)
100 return cmd
103 def do_shell(self, cmd):
104 """System shell command, for commads which starts with !"""
105 os.system(cmd)
108 def default(self, cmd=None):
109 """Run commands.
111 cmd = (<class>, [method], [arg1], [arg2], ...)
113 # cmd[0] should be class name
114 # cmd[1] should be method name (or arugmet if class is callable)
115 # cmd[1] can be empty
116 # cmd[2:] should be method argumments, can be empty
117 cmd = cmd.split()
118 # operations for single command with no arguments
119 if len(cmd) == 1:
120 # if there's no such command (or plugin class)
121 if not cmd[0] in self.commands.keys():
122 self.put("%s : command not found." % cmd[0])
123 # for only one argument, try to run __call__() method with
124 # no arguments
125 elif "__call__" in dir(self.commands[cmd[0]]):
126 getattr(self.commands[cmd[0]], "__call__")()
127 else:
128 self.put("%s : bad usege. Try to run help." % cmd[0])
129 # if command was called with arguments
130 elif len(cmd) > 1:
131 cmd[1] = self.cmdprefix + cmd[1]
132 if not cmd[0] in self.commands.keys():
133 self.put("%s : command not found." % cmd[0])
134 # if method named arg[1] exist in class arg[0], try to run it
135 elif cmd[1] in dir(self.commands[cmd[0]]):
136 #print dir(self.commands[cmd[0]])
137 try:
138 getattr(self.commands[cmd[0]], cmd[1])(*cmd[2:])
139 except TypeError:
140 # show __doc__ if exist
141 doc = getattr(self.commands[cmd[0]], cmd[1])
142 if doc.__doc__:
143 self.put(doc.__doc__)
144 else:
145 self.put("%s : bad usage" % cmd[1][3:])
146 # if there's no such method arg[1] in class arg[0],
147 # try to run class.__call__(args..)
148 else:
149 try:
150 self.commands[cmd[0]](*cmd[1:])
151 except TypeError:
152 # object is not callable
153 self.put("%s : bad usage" % cmd[0])
156 def completenames(self, text, *ignored):
157 """Complete commands"""
158 dotext = self.cmdprefix + text
159 # local methods
160 local_cmd_list = \
161 [a[3:] + " " for a in self.get_names() if a.startswith(dotext)]
162 # + all metrods from modules
163 module_cmd_list = \
164 [a + " " for a in self.commands.keys() if a.startswith(text)]
165 # + all aliases
166 aliases_cmd_list = \
167 [a + " " for a in self.conf.alias.keys() if a.startswith(text)]
168 return local_cmd_list + module_cmd_list + aliases_cmd_list
171 def completedefault(self, text, line, begidx, endidx):
172 """Complete commands argument"""
173 dotext = self.cmdprefix + text
174 line = line.split()
175 # if only commands was given
176 if len(line) == 1:
177 cmds = [a[3:] + " " for a in dir(self.commands[line[0]]) \
178 if a.startswith(dotext)]
179 elif len(line) == 2:
180 cmds = [a[3:] + " " for a in dir(self.commands[line[0]]) \
181 if a.startswith(dotext)]
182 # else don't complete (or should I?)
183 else:
184 cmds = []
185 return cmds
188 def do_help(self, arg):
189 """Show help for commands"""
190 arg = arg.split()
191 if not arg:
192 self.put("Usage: help <command>")
193 elif len(arg) == 1:
194 # first - build-in methods
195 if self.cmdprefix + arg[0] in dir(self):
196 doc = getattr(self, self.cmdprefix + arg[0]).__doc__
197 if doc:
198 self.put(doc)
199 # modules help
200 else:
201 try:
202 # try to run help() method
203 self.put(getattr(self.commands[arg[0]], "help")())
204 except AttributeError:
205 # try to show __doc__
206 if self.commands[arg[0]].__doc__:
207 self.put(self.commands[arg[0]].__doc__)
208 else:
209 self.put("No help found.")
210 except KeyError:
211 self.put("No help found.")
213 elif len(arg) == 2:
214 try:
215 if arg[0] in self.commands.keys():
216 arg[1] = self.cmdprefix + arg[1]
217 doc = getattr(self.commands[arg[0]], arg[1]).__doc__
218 self.put(doc)
219 except AttributeError:
220 self.put("%s : no help found" % arg[1][3:])
221 else:
222 self.put("Try to do something else.\nThis option doesn't work well now.")
225 def do_clear(self, *ignored):
226 """Clear the screen"""
227 # TODO 2008-02-09 20:44:50
228 self.put("Todo, sorry")
231 def do_history(self, hnumb=None, *ignored):
232 """Show the history"""
233 # TODO better history listing
234 # 2008-01-27 18:51:22
235 # print whole history
236 if not hnumb:
237 for number in range(1, conf.history_length):
238 cmd = readline.get_history_item(number)
239 if not cmd:
240 break
241 self.put("%6d %s" % (number, cmd))
242 # for history range 12-22 or -22 or 22-
243 else:
244 try:
245 if "-" in hnumb:
246 if hnumb[-1] == "-" or hnumb[0] == "-":
247 start = int(hnumb.replace("-", " "))
248 end = conf.history_length
249 else:
250 start, end = hnumb.split("-")
251 start = int(start)
252 end = int(end) + 1
253 for number in range(start, end):
254 cmd = readline.get_history_item(number)
255 if not cmd:
256 break
257 self.put("%6d %s" % (number, cmd))
258 else:
259 hnumb = int(hnumb)
260 self.put(readline.get_history_item(hnumb))
261 except ValueError:
262 self.put("""Bad value.
263 Usage: history <number or range>
264 history 11-20 -> from 11 to 20
265 history 22- -> from 22 fo the end of history file
266 history -22 -> same as 22-""")
269 def do_quit(self, *ignored):
270 """Quit from shell"""
271 if conf.history_length:
272 readline.write_history_file(conf.history_file)
273 print ""
274 sys.exit(1)
276 # function aliases
277 do_EOF = do_quit
278 do_exit = do_quit
282 if __name__ == "__main__":
283 shell = AurShell()
284 try:
285 shell.cmdloop()
286 except KeyboardInterrupt:
287 # TODO 2008-01-27 16:56:31
288 sys.exit(shell.do_quit())