Attachment 'lwickjr.Module.Alias.py'
Download
Toggle line numbers
1 """Extend I.D.L.E. with an Alias facility for the command shell.
2
3 This I.D.L.E. extension provides the following functionality enhancements:
4 * an Alias facility for I.D.L.E.'s command shell
5 * an 'alias' command for the Alias facility
6 * an 'unalias' command for the Alias facility
7
8 The Alias facility, implemented by the function runsource, enhances
9 the I.D.L.E. command shell by promoting all fully-qualified callable
10 objects and the string keys of all callable objects stored in the
11 module-global dictionary "GlobalAliases" into "commands", UNDER
12 SPECIFIC CIRCUMSTANCES.
13
14 At the command prompt ONLY [i.e., NOT in module source code!], before
15 a line is submitted to the normal I.D.L.E. execution machinery,
16 several checks are performed:
17
18 * The first 'word' is split from the rest of the string, and the two
19 chunks are saved as Command and Args.
20 * Command is then looked up in the module-global dictionary
21 GlobalAliases.
22 If found, the corresponding value becomes the Command, else the
23 command string is evaluated in the context of the module __main__,
24 and the result of the execution becomes the Command.
25 * If the preceeding operation yeilds a callable, Args is also evaluated
26 in the context of module __main__. If the evaluation succeeds, the
27 result becomes the Args.
28 * If Args is not by now a tuple, it is wrapped in one.
29
30 Up to this point, any errors are trapped and silently dealt with,
31 diverting evaluation of the command to "normal" channels if unable
32 to proceed.
33
34 ## XXX TO DO: before handing a command off to the "normal" evaluator,
35 ## attempt to import the presumed module named by the command, and, if
36 ## successful, update the dictionary of defined Alises from a
37 ## specially-named dictionary found there, or from the dictionary
38 ## returned by a specially-named callable found there, then enter the
39 ## module into the namespace of module __main__.
40
41 * The Command callable is then called, and the Args tuple is used to
42 supply positional arguments.
43 If an error occurs, a traceback is printed, else the result of the
44 call, if non-None, is printed.
45 In either case, a new command line is solicited.
46
47 If the fully-qualified callable is followed immediately by an open
48 paren and a space, IN THAT ORDER, the "normal" command processing
49 takes over. This is useful for situations when you want to prevent the
50 Alias mechanism from evaluating the result of a function that returns
51 a callable. Another method is to put a space BEFORE the open paren,
52 letting Alias evaluate the argstring as a parenthasis-grouped
53 expression to be passed to the callable. Examples:
54
55 >>> type {}
56 <type 'dict'>
57 >>> type({})
58 {}
59 >>> type ({})
60 <type 'dict'>
61 >>> type( {})
62 <type 'dict'>
63 >>>
64
65 The first three examples are evaluated by the Alias evaluator; the
66 fourth is evaluated by the "normal" evaluator. The second example is
67 likely a source for confusion: Note that <type 'dict'> is callable,
68 and that calling it without arguments yeilds an empty dictionary!
69
70 The alias command, implemented by the function alias, provides four
71 features:
72
73 * If called with no arguments, or with a first argument equal to an
74 empty string, the current dictionary of defined GlobalAliases is
75 displayed nicely formatted.
76 * If called with a first argument that is [a subclass of] a dictionary,
77 the dictionary is used to update the dictionary of defined GlobalAliases.
78 * If called with a non-None second argument, the second argument is
79 inserted into the dictionary of defined GlobalAliases using the first
80 argument as the key.
81 * Otherwise, the first [and presumably only] argument is looked up in
82 the dictionary of defined GlobalAliases, and the pair is displayed.
83
84 The unalias command, implemented by the function unalias, provides a
85 single feature: if the only argument matches a key in the
86 module-global dictionary GlobalAliases, the key:value pair is deleted, else
87 a simple error message is displayed.
88 """
89
90 import UT, code, os, sys, types, __builtin__
91
92 class Alias:
93 def __init__(Self, editwin):
94 pass
95
96 def runsource(self, source, filename="<input>", symbol="single"):
97 """Compile and run some source in the interpreter.
98
99 Arguments are as for compile_command().
100
101 One several things can happen:
102
103 1) The input is empty. Nothing happens.
104
105 2) The input matches a registered Alias. The matching callable is
106 called with the supplied arguments, if any.
107
108 3) The input evaluates to a callable. The callable is called with the
109 supplied arguments, if any.
110
111 4) The input matches an importable module. The module is imported,
112 bound in the source context, and checked for any of several
113 signatures:
114
115 4a) The module defines a mapping named __Aliases__. The dictionary of
116 registered Aliases is updated from this dictionary.
117
118 4b) The module defines a callable named __Alias__. The callable
119 becomes the command [and the module is NOT bound].
120
121 Note: 4a and 4b are not mutually exclusive, but the dictionary update
122 is performed _before_ the function call.
123
124 5) The input is incorrect; compile_command() raised an exception
125 (SyntaxError or OverflowError). A syntax traceback will be printed
126 by calling the showsyntaxerror() method.
127
128 6) The input is incomplete, and more input is required;
129 compile_command() returned None. Nothing happens.
130
131 7) The input is complete; compile_command() returned a code object.
132 The code is executed by calling self.runcode() (which also handles
133 run-time exceptions, except for SystemExit).
134
135 The return value is True in case 6, False in the other cases (unless
136 an exception is raised). The return value can be used to decide
137 whether to use sys.ps1 or sys.ps2 to prompt the next line.
138
139 """
140 ## from UT import ThisLine
141 ## print >> sys.stderr, "Here goes!"
142 ## print >> sys.stderr, '### %r %r' % (ThisLine(), source)
143 if not source.strip(): source = 'pass' # Case 1: Empty line; do nothing.
144 ## print >> sys.stderr, '### %r %r' % (ThisLine(), source.split())
145 ## print >> sys.stderr, '### %r %r' % (ThisLine(), source.split()[0])
146 Command, Args = (source.split(' ', 1) + [''])[:2]
147 ## print >> sys.stderr, '### %r %r %r' % (ThisLine(), Command, Args)
148 import __main__
149 # Case 2: Is this a REGISTERED Alias?
150 if Command in GlobalAliases.keys():
151 Command = GlobalAliases[Command]
152 else:
153 # Case 3: Ok, it isn't registered, so try to evaluate it.
154 try: Command = eval(Command, self.locals)
155 except: pass
156 ## print >> sys.stderr, '### %r %r %r' % (ThisLine(), Command, Args)
157
158 # Case 4: It wasn't a callable, so try to import it as a module.
159 if isinstance(Command, types.StringType): # and (Args == ''):
160 try: exec 'import %s as Command' % Command #; print "Import succeeded."
161 except: pass
162 if isinstance(Command, types.ModuleType):
163 ## print >> sys.stderr, '### %r import %r' % (ThisLine(), Command.__name__)
164
165 # Case 4a: The module defines a mapping named __Aliases__.
166 try: GlobalAliases.update(Command.__Aliases__)
167 except: pass
168
169 # Case 4b: The module defines a callable named __Alias__.
170 try: Command = Command.__Alias__
171 except: pass
172 ## print >> sys.stderr, '### %r %r %r' % (ThisLine(), Command, Args)
173 if isinstance(Command, types.ModuleType):
174 source = 'import %s;%s' % (source, source,)
175
176 # If we have a callable, we're going to need to evaluate the
177 # arguments, if any.
178 if callable(Command):
179
180 # I know, I know, the code below makes it awkward to pass a
181 # single empty string to commands; use
182 # >>> blah '',
183 # [It's still easier than typing two parenthases.]
184 if Args == '':
185 Args = ()
186 else:
187 try: Args = eval(Args, self.locals)
188 except: pass
189 if not isinstance(Args, types.TupleType):
190 Args = (Args,)
191 ## print >> sys.stderr, '### %r %r %r' % (ThisLine(), Command, Args)
192 ## print >> sys.stderr, '### %r call %r%r' % (ThisLine(), Command, Args)
193 __builtin__._AliasCommand = Command
194 __builtin__._AliasArgs = Args
195 source = '_AliasCommand(*_AliasArgs)'
196
197 try:
198 code = self.compile(source, filename, symbol)
199 except (OverflowError, SyntaxError, ValueError):
200 # Case 5
201 self.showsyntaxerror(filename)
202 return False
203
204 if code is None:
205 # Case 6
206 return True
207
208 # Case 7
209 self.runcode(code)
210 return False
211
212 ## if Args is not '':
213 ## # It's a module and we have Args; try to call its __init__ function.
214 ## print >> sys.stderr, '### 181 call %s.__init__%s' % (Command, Args)
215 ## try: Command.__init__(*Args)
216 ## except: self.showtraceback()
217 ## return 0
218 ##
219 ## if 'Aliases' in Command.__dict__.keys() \
220 ## and isinstance(Command.Aliases, types.DictType):
221 ## # It's a module and we don't have args, but we DO have a dictionary
222 ## # named Aliases.
223 ## print >> sys.stderr, '### 190 bind', Command
224 ## if Module: self.locals[Module] = Command
225 ## GlobalAliases.update(Command.Aliases)
226 ## return 0
227 ##
228 ## if 'MakeAliases' in Command.__dict__.keys() \
229 ## and isinstance(Command.MakeAliases, types.FunctionType):
230 ## # It's a module and we don't have args or an Aliases dictionry, but
231 ## # we DO have a function named MakeAliases...
232 ## Aliases = Command.MakeAliases()
233 ## if isinstance(Aliases, types.DictType):
234 ## # ...AND it returnes a dictionary.
235 ## print >> sys.stderr, '### 202 bind', Command
236 ## self.locals[Command] = Module
237 ## GlobalAliases.update(Aliases)
238 ## return 0
239 ##
240 ## print >> sys.stderr, '### 207', `source`
241 ## return PyShell.InteractiveInterpreter.runsource(self, source, filename)
242 ## return oldrunsource(self, source)
243 ## finally:
244 ## print >> sys.stderr, "How's that?"
245 ## self.tkconsole.endexecuting()
246 ## self.tkconsole.resetoutput()
247 ## if self.save_warnings_filters is not None:
248 ## PyShell.warnings.filters[:] = self.save_warnings_filters
249 ## self.save_warnings_filters = None
250
251 def _help(Topic=None):
252 if Topic in GlobalAliases.keys():
253 help(GlobalAliases[Topic])
254 else:
255 help(Topic)
256
257 _help.__doc__ = help.__doc__
258
259 def alias(Name='', Func=None):
260 # Don't bother with sanity checks; runsource takes care of that.
261 print `Name, Func`
262 if Name == '':
263 import UT
264 try:
265 UT.dump(dict(map(lambda (x, y): (x, '.'.join([y.__module__, y.__name__])),
266 GlobalAliases.items())))
267 except:
268 UT.dump(GlobalAliases)
269 elif isinstance(Name, types.DictType):
270 GlobalAliases.update(Name)
271 elif Func is None:
272 print ' %s: %s' % (Name, GlobalAliases.get(Name, "Undefined"))
273 else:
274 GlobalAliases[Name] = Func
275
276 def unalias(Name):
277 try:
278 del GlobalAliases[Name]
279 except:
280 print "Error: %s is not a defined alias." % `Name`
281
282 def usage(Topic):
283 if Topic in GlobalAliases.keys():
284 Topic = GlobalAliases[Topic]
285 try:
286 if Topic.__doc__: print Topic.__doc__
287 else: print 'None.'
288 except: print 'None'
289
290 __Aliases__ = {
291 'alias': alias,
292 'help': _help,
293 'unalias': unalias,
294 'usage': usage,
295 }
296
297 GlobalAliases = {}
298 code.InteractiveInterpreter.runsource = runsource
299 sys.modules['Alias'] = sys.modules['idlelib.Alias']
300 print "Alias installed."
Attached Files
To refer to attachments on a page, use attachment:filename, as shown below in the list of files. Do NOT use the URL of the [get] link, since this is subject to change and can break easily.You are not allowed to attach a file to this page.