Mercurial > python-cmd2
comparison cmd2.py @ 75:8b2603d1acc1
working except for combinations
author | catherine@Elli.myhome.westell.com |
---|---|
date | Fri, 27 Jun 2008 15:36:03 -0400 |
parents | 4e290d75e92e |
children | dcd5d13e5603 |
comparison
equal
deleted
inserted
replaced
74:4e290d75e92e | 75:8b2603d1acc1 |
---|---|
127 def getPasteBuffer(): | 127 def getPasteBuffer(): |
128 raise OSError, pastebufferr % ('xclip', 'On Debian/Ubuntu, install with "sudo apt-get install xclip"') | 128 raise OSError, pastebufferr % ('xclip', 'On Debian/Ubuntu, install with "sudo apt-get install xclip"') |
129 setPasteBuffer = getPasteBuffer | 129 setPasteBuffer = getPasteBuffer |
130 | 130 |
131 pyparsing.ParserElement.setDefaultWhitespaceChars(' \t') # see http://pyparsing.wikispaces.com/message/view/home/1352689 | 131 pyparsing.ParserElement.setDefaultWhitespaceChars(' \t') # see http://pyparsing.wikispaces.com/message/view/home/1352689 |
132 def punctuationParser(punctuators): | 132 |
133 """ | 133 class UserCommand(str): |
134 Produces a string parser based on a list of targets to search for. | 134 def __new__(cls, s, app): |
135 Output is a parser function. | 135 return str.__new__(cls, s) |
136 Parser's output is a tuple: (before the target, [elements of the target], after the target) | 136 def __init__(self, s, app): |
137 >>> p = punctuationParser([';', 'EOF']) | 137 self.terminator = None |
138 >>> p('is terminated;') | 138 self.terminator_suffix = None |
139 ('is terminated', [';'], '') | 139 self.searchable = self.asEntered = s |
140 >>> p('is terminated EOF after the end') | 140 self.app = app |
141 ('is terminated', ['EOF'], 'after the end') | 141 self.output_destination_pattern = self.punctuationPattern(['>>', '>']) |
142 >>> p('is not terminated') | 142 self.input_source_pattern = self.punctuationPattern(['<']) |
143 >>> pattern1 = pyparsing.Literal(';') + pyparsing.Optional(pyparsing.Word(pyparsing.nums)) | 143 self.pipe_destination_pattern = self.punctuationPattern(['|']) |
144 >>> p2 = punctuationParser([pattern1, 'EOF']) | 144 def punctuationPattern(self, punctuators): |
145 >>> p2('the quick brown fox;4') | 145 processed = punctuators[:] |
146 ('the quick brown fox', [';', '4'], '') | 146 if not hasattr(processed[0], 'parseString'): |
147 >>> p2('the quick brown foxEOF') | 147 processed[0] = pyparsing.Literal(processed[0]) |
148 ('the quick brown fox', ['EOF'], '') | 148 processed = reduce(lambda x, y: x ^ y, processed) |
149 >>> p2('nothing') | 149 processed.ignore(pyparsing.sglQuotedString) |
150 """ | 150 processed.ignore(pyparsing.dblQuotedString) |
151 processed = punctuators[:] | 151 pattern = pyparsing.SkipTo(processed) + processed + pyparsing.restOfLine |
152 if not hasattr(processed[0], 'parseString'): | 152 return pattern |
153 processed[0] = pyparsing.Literal(processed[0]) | 153 def find_punctuation(self): |
154 processed = reduce(lambda x, y: x ^ y, processed) | 154 punctuators = ['|','>','>>','<'] |
155 processed.ignore(pyparsing.sglQuotedString) | 155 punctuators.extend(self.app.terminators) |
156 processed.ignore(pyparsing.dblQuotedString) | 156 punctuated = self.punctuationPattern(punctuators).searchString(self.asEntered) |
157 pattern = pyparsing.SkipTo(processed) + processed + pyparsing.restOfLine | 157 if punctuated: |
158 def parser(txt): | 158 self.executable, self.searchable = punctuated[0][0], self.asEntered[len(punctuated[0][0]):] |
159 result = pattern.searchString(txt) | |
160 if result: | |
161 return result[0][0].strip(), result[0][1:-1], result[0][-1].strip() | |
162 else: | 159 else: |
163 return None | 160 self.executable, self.searchable = self.asEntered, '' |
164 return parser | 161 def complete(self): |
165 | 162 terminator_finder = self.punctuationPattern(self.app.terminators) |
166 class UserCommand(str): | 163 result = terminator_finder.searchString(self.asEntered) |
167 def __init__(self, s): | 164 while not result: |
168 str.__init__(self, s) | 165 inp = self.app.pseudo_raw_input(self.app.continuationPrompt) |
169 self.executable = s | 166 self.asEntered = '%s\n%s' % (self.asEntered, inp) |
170 self.searchable = s | 167 result = terminator_finder.searchString(self.asEntered) |
171 | 168 try: |
169 self.terminator = result[0][1][0] | |
170 self.terminator_suffix = result[0][1][1] | |
171 except IndexError: | |
172 self.terminator = result[0][1] | |
173 self.terminator_suffix = None | |
174 def redirectedInput(self): | |
175 inputFrom = self.input_source_pattern.searchString(self.searchable) | |
176 if inputFrom: | |
177 if inputFrom[0][-1].strip(): | |
178 input = self.app.fileimport(source=inputFrom[0][-1].strip()) | |
179 else: | |
180 input = getPasteBuffer() | |
181 if self.terminator: | |
182 self.executable = '%s %s' % (self.executable, input) | |
183 else: | |
184 self.executable = '%s %s %s' % (self.executable, inputFrom[0][0], input) | |
185 def pipeDestination(self): | |
186 pipeTo = self.pipe_destination_pattern.searchString(self.searchable) | |
187 return (pipeTo and pipeTo[0][-1]) or None | |
188 def redirectedOutput(self): | |
189 outputTo = self.output_destination_pattern.searchString(self.searchable) | |
190 if outputTo: | |
191 dest = outputTo[0][-1].strip() | |
192 if outputTo[0][1] == '>>': | |
193 mode = 'a' | |
194 else: | |
195 mode = 'w' | |
196 return dest, mode | |
197 return None, None | |
198 | |
199 | |
200 | |
201 """ | |
202 Produces a string parser based on a list of targets to search for. | |
203 Output is a parser function. | |
204 Parser's output is a tuple: (before the target, [elements of the target], after the target) | |
205 >>> p = punctuationParser([';', 'EOF']) | |
206 >>> p('is terminated;') | |
207 ('is terminated', [';'], '') | |
208 >>> p('is terminated EOF after the end') | |
209 ('is terminated', ['EOF'], 'after the end') | |
210 >>> p('is not terminated') | |
211 >>> pattern1 = pyparsing.Literal(';') + pyparsing.Optional(pyparsing.Word(pyparsing.nums)) | |
212 >>> p2 = punctuationParser([pattern1, 'EOF']) | |
213 >>> p2('the quick brown fox;4') | |
214 ('the quick brown fox', [';', '4'], '') | |
215 >>> p2('the quick brown foxEOF') | |
216 ('the quick brown fox', ['EOF'], '') | |
217 >>> p2('nothing') | |
218 """ | |
219 | |
172 class Cmd(cmd.Cmd): | 220 class Cmd(cmd.Cmd): |
173 caseInsensitive = True | 221 caseInsensitive = True |
174 terminators = [';', pyparsing.LineEnd() + pyparsing.LineEnd()] | |
175 multilineCommands = [] # commands that need a terminator to be finished | 222 multilineCommands = [] # commands that need a terminator to be finished |
223 terminators = [';', pyparsing.LineEnd() + pyparsing.LineEnd()] | |
176 terminatorKeepingCommands = [] # commands that expect to process their own terminators (else it will be stripped during parse) | 224 terminatorKeepingCommands = [] # commands that expect to process their own terminators (else it will be stripped during parse) |
177 continuationPrompt = '> ' | 225 continuationPrompt = '> ' |
178 shortcuts = {'?': 'help', '!': 'shell', '@': 'load'} | 226 shortcuts = {'?': 'help', '!': 'shell', '@': 'load'} |
179 excludeFromHistory = '''run r list l history hi ed edit li eof'''.split() | 227 excludeFromHistory = '''run r list l history hi ed edit li eof'''.split() |
180 defaultExtension = 'txt' | 228 defaultExtension = 'txt' |
212 pass | 260 pass |
213 | 261 |
214 def __init__(self, *args, **kwargs): | 262 def __init__(self, *args, **kwargs): |
215 cmd.Cmd.__init__(self, *args, **kwargs) | 263 cmd.Cmd.__init__(self, *args, **kwargs) |
216 self.history = History() | 264 self.history = History() |
217 self.commmand_terminator_finder = punctuationParser(self.terminators) | |
218 self.output_destination_finder = punctuationParser(['>>', '>']) | |
219 self.input_source_finder = punctuationParser(['<']) | |
220 self.pipe_destination_finder = punctuationParser(['|']) | |
221 | 265 |
222 def do_shortcuts(self, args): | 266 def do_shortcuts(self, args): |
223 """Lists single-key shortcuts available.""" | 267 """Lists single-key shortcuts available.""" |
224 result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in self.shortcuts.items()) | 268 result = "\n".join('%s: %s' % (sc[0], sc[1]) for sc in self.shortcuts.items()) |
225 self.stdout.write("Single-key shortcuts for other commands:\n%s\n" % (result)) | 269 self.stdout.write("Single-key shortcuts for other commands:\n%s\n" % (result)) |
237 (command, args) = statement, '' | 281 (command, args) = statement, '' |
238 if self.caseInsensitive: | 282 if self.caseInsensitive: |
239 command = command.lower() | 283 command = command.lower() |
240 return command, args | 284 return command, args |
241 | 285 |
242 def completedStatement(self, firstline): | |
243 statement = firstline | |
244 termination_found = self.commmand_terminator_finder(statement) | |
245 while not termination_found: | |
246 inp = self.pseudo_raw_input(self.continuationPrompt) | |
247 statement = '%s\n%s' % (statement, inp) | |
248 termination_found = self.commmand_terminator_finder(statement) | |
249 return termination_found[0], termination_found[-1] | |
250 # assembling a list of lines and joining them at the end would be faster, | |
251 # but statementHasEnded needs a string arg; anyway, we're getting | |
252 # user input and users are slow. | |
253 | |
254 def onecmd(self, line, assumeComplete=False): | 286 def onecmd(self, line, assumeComplete=False): |
255 """Interpret the argument as though it had been typed in response | 287 """Interpret the argument as though it had been typed in response |
256 to the prompt. | 288 to the prompt. |
257 | 289 |
258 This may be overridden, but should not normally need to be; | 290 This may be overridden, but should not normally need to be; |
262 | 294 |
263 """ | 295 """ |
264 statekeeper = None | 296 statekeeper = None |
265 stop = 0 | 297 stop = 0 |
266 command, args = self.extractCommand(line) | 298 command, args = self.extractCommand(line) |
267 statement = originalStatement = ' '.join([command, args]) | 299 originalStatement = ' '.join([command, args]) |
300 statement = UserCommand(originalStatement, self) | |
268 if (not assumeComplete) and (command in self.multilineCommands): | 301 if (not assumeComplete) and (command in self.multilineCommands): |
269 statement, afterTerminator = self.completedStatement(statement) | 302 statement.complete() |
270 inputFrom = self.input_source_finder(afterTerminator) | 303 statement.find_punctuation() |
271 if inputFrom: | 304 statement.redirectedInput() |
272 statement = "%s %s" % (statement, inputFrom[0]) | 305 pipeTo = statement.pipeDestination() |
273 else: | |
274 inputFrom = self.input_source_finder(statement) | |
275 if inputFrom: | |
276 statement = inputFrom[0] | |
277 | |
278 if inputFrom: | |
279 source = inputFrom[-1] | |
280 if source: | |
281 statement = '%s %s' % (statement, self.fileimport(source=source)) | |
282 else: | |
283 statement = '%sy %s' % (statement, getPasteBuffer()) | |
284 | |
285 pipeTo = self.pipe_destination_finder(afterTerminator) | |
286 if pipeTo: | 306 if pipeTo: |
287 pipeTo = pipeTo[-1] | |
288 statekeeper = Statekeeper(self, ('stdout',)) | 307 statekeeper = Statekeeper(self, ('stdout',)) |
289 pipeTo = subprocess.Popen(pipeTo, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) | 308 dest = subprocess.Popen(pipeTo, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) |
290 self.stdout = pipeTo.stdin | 309 self.stdout = dest.stdin |
291 else: # can't pipe output AND send it to a file | 310 else: # can't pipe output AND send it to a file |
292 outputTo = self.output_destination_finder(afterTerminator) | 311 outputTo, outputMode = statement.redirectedOutput() |
293 if outputTo: | 312 if outputMode: |
294 destination = outputTo[-1] | |
295 statekeeper = Statekeeper(self, ('stdout',)) | 313 statekeeper = Statekeeper(self, ('stdout',)) |
296 mode = ((outputTo[1][0] == '>>') and 'a') or 'w' | 314 if outputTo: |
297 if destination: | 315 self.stdout = open(outputTo, outputMode) |
298 self.stdout = open(destination, mode) | |
299 else: | 316 else: |
300 self.stdout = tempfile.TemporaryFile() | 317 self.stdout = tempfile.TemporaryFile() |
301 if mode == 'a': | 318 if outputMode == 'a': |
302 self.stdout.write(getPasteBuffer()) | 319 self.stdout.write(getPasteBuffer()) |
303 | 320 |
304 stop = cmd.Cmd.onecmd(self, statement) | 321 stop = cmd.Cmd.onecmd(self, statement.executable) |
305 try: | 322 try: |
306 if command not in self.excludeFromHistory: | 323 if command not in self.excludeFromHistory: |
307 self.history.append(originalStatement) | 324 self.history.append(originalStatement) |
308 finally: | 325 finally: |
309 if statekeeper: | 326 if statekeeper: |
310 if pipeTo: | 327 if pipeTo: |
311 for result in pipeTo.communicate(): | 328 for result in dest.communicate(): |
312 statekeeper.stdout.write(result or '') | 329 statekeeper.stdout.write(result or '') |
313 elif outputTo and not destination: | 330 elif outputMode and not outputTo: |
314 self.stdout.seek(0) | 331 self.stdout.seek(0) |
315 writeToPasteBuffer(self.stdout.read()) | 332 writeToPasteBuffer(self.stdout.read()) |
316 self.stdout.close() | 333 self.stdout.close() |
317 statekeeper.restore() | 334 statekeeper.restore() |
318 | 335 |
401 if shortcut and hasattr(self, 'do_%s' % shortcut): | 418 if shortcut and hasattr(self, 'do_%s' % shortcut): |
402 line = '%s %s' % (shortcut, line[1:]) | 419 line = '%s %s' % (shortcut, line[1:]) |
403 i, n = 0, len(line) | 420 i, n = 0, len(line) |
404 while i < n and line[i] in self.identchars: i = i+1 | 421 while i < n and line[i] in self.identchars: i = i+1 |
405 cmd, arg = line[:i], line[i:].strip() | 422 cmd, arg = line[:i], line[i:].strip() |
406 if cmd not in self.terminatorKeepingCommands: | |
407 arg = self.strip_terminators(arg) | |
408 return cmd, arg, line | 423 return cmd, arg, line |
409 | 424 |
410 def showParam(self, param): | 425 def showParam(self, param): |
411 param = self.clean(param) | 426 param = self.clean(param) |
412 if param in self.settable: | 427 if param in self.settable: |