Mercurial > python-cmd2
comparison cmd2.py @ 157:10e917acf787
wow, parsing is broken
author | catherine@dellzilla |
---|---|
date | Mon, 24 Nov 2008 18:53:25 -0500 |
parents | 7e5a57df88aa |
children | 87d3f3203b96 |
comparison
equal
deleted
inserted
replaced
156:7e5a57df88aa | 157:10e917acf787 |
---|---|
49 for opt in option_list: | 49 for opt in option_list: |
50 optionParser.add_option(opt) | 50 optionParser.add_option(opt) |
51 optionParser.set_usage("%s [options] arg" % func.__name__.strip('do_')) | 51 optionParser.set_usage("%s [options] arg" % func.__name__.strip('do_')) |
52 def newFunc(instance, arg): | 52 def newFunc(instance, arg): |
53 try: | 53 try: |
54 opts, arg = optionParser.parse_args(arg.split()) | 54 opts, newArgs = optionParser.parse_args(arg.split()) |
55 arg = ' '.join(arg) | 55 arg.parsed['args'] = arg[arg.find(newArgs[0]):] |
56 except (optparse.OptionValueError, optparse.BadOptionError, | 56 except (optparse.OptionValueError, optparse.BadOptionError, |
57 optparse.OptionError, optparse.AmbiguousOptionError, | 57 optparse.OptionError, optparse.AmbiguousOptionError, |
58 optparse.OptionConflictError), e: | 58 optparse.OptionConflictError), e: |
59 print e | 59 print e |
60 optionParser.print_help() | 60 optionParser.print_help() |
165 return 0 | 165 return 0 |
166 start, end = self.redirectInPattern.scanString(statement.fullStatement).next()[1:] | 166 start, end = self.redirectInPattern.scanString(statement.fullStatement).next()[1:] |
167 return self.onecmd('%s%s%s' % (statement.fullStatement[:start], | 167 return self.onecmd('%s%s%s' % (statement.fullStatement[:start], |
168 newinput, statement.fullStatement[end:])) | 168 newinput, statement.fullStatement[end:])) |
169 | 169 |
170 class ParsedString(str): | |
171 pass | |
172 | |
170 class Cmd(cmd.Cmd): | 173 class Cmd(cmd.Cmd): |
171 echo = False | 174 echo = False |
172 caseInsensitive = True | 175 caseInsensitive = True |
173 continuationPrompt = '> ' | 176 continuationPrompt = '> ' |
177 legalChars = '!#$%.:?@_' # make sure your terminators are not in here! | |
174 shortcuts = {'?': 'help', '!': 'shell', '@': 'load' } | 178 shortcuts = {'?': 'help', '!': 'shell', '@': 'load' } |
175 excludeFromHistory = '''run r list l history hi ed edit li eof'''.split() | 179 excludeFromHistory = '''run r list l history hi ed edit li eof'''.split() |
176 noSpecialParse = 'set ed edit exit'.split() | 180 noSpecialParse = 'set ed edit exit'.split() |
177 defaultExtension = 'txt' | 181 defaultExtension = 'txt' |
178 defaultFileName = 'command.txt' | 182 defaultFileName = 'command.txt' |
227 ''' | 231 ''' |
228 >>> c = Cmd() | 232 >>> c = Cmd() |
229 >>> c.multilineCommands = ['multiline'] | 233 >>> c.multilineCommands = ['multiline'] |
230 >>> c.caseInsensitive = True | 234 >>> c.caseInsensitive = True |
231 >>> c._init_parser() | 235 >>> c._init_parser() |
236 >>> print c.parser.parseString('termbare;').dump() | |
237 >>> print c.parser.parseString('termbare; suffx').dump() | |
232 >>> print c.parser.parseString('barecommand').dump() | 238 >>> print c.parser.parseString('barecommand').dump() |
233 ['barecommand', ''] | 239 ['barecommand', ''] |
234 - args: | |
235 - command: barecommand | 240 - command: barecommand |
236 - statement: ['barecommand', ''] | 241 - statement: ['barecommand', ''] |
237 - args: | |
238 - command: barecommand | 242 - command: barecommand |
239 >>> print c.parser.parseString('COMmand with args').dump() | 243 >>> print c.parser.parseString('COMmand with args').dump() |
240 ['command', ' with args'] | 244 ['command', 'with args'] |
241 - args: with args | 245 - args: with args |
242 - command: command | 246 - command: command |
243 - statement: ['command', ' with args'] | 247 - statement: ['command', 'with args'] |
244 - args: with args | 248 - args: with args |
245 - command: command | 249 - command: command |
246 >>> print c.parser.parseString('command with args and terminator; and suffix').dump() | 250 >>> print c.parser.parseString('command with args and terminator; and suffix').dump() |
247 ['command', 'with args and terminator', ';', ' and suffix'] | 251 ['command', 'with args and terminator', ';', ' and suffix'] |
248 - args: with args and terminator | 252 - args: with args and terminator |
249 - command: command | 253 - command: command |
252 - command: command | 256 - command: command |
253 - terminator: ; | 257 - terminator: ; |
254 - suffix: and suffix | 258 - suffix: and suffix |
255 - terminator: ; | 259 - terminator: ; |
256 >>> print c.parser.parseString('simple | piped').dump() | 260 >>> print c.parser.parseString('simple | piped').dump() |
257 ['simple', '', '|', 'piped'] | 261 ['simple', '', '|', ' piped'] |
258 - args: | |
259 - command: simple | 262 - command: simple |
260 - pipeTo: piped | 263 - pipeTo: piped |
261 - statement: ['simple', ''] | 264 - statement: ['simple', ''] |
262 - args: | |
263 - command: simple | 265 - command: simple |
264 >>> print c.parser.parseString('command with args, terminator;sufx | piped').dump() | 266 >>> print c.parser.parseString('command with args, terminator;sufx | piped').dump() |
265 ['command', 'with args, terminator', ';', 'sufx', '|', 'piped'] | 267 ['command', 'with args, terminator', ';', 'sufx', '|', ' piped'] |
266 - args: with args, terminator | 268 - args: with args, terminator |
267 - command: command | 269 - command: command |
268 - pipeTo: piped | 270 - pipeTo: piped |
269 - statement: ['command', 'with args, terminator', ';'] | 271 - statement: ['command', 'with args, terminator', ';'] |
270 - args: with args, terminator | 272 - args: with args, terminator |
271 - command: command | 273 - command: command |
272 - terminator: ; | 274 - terminator: ; |
273 - suffix: sufx | 275 - suffix: sufx |
274 - terminator: ; | 276 - terminator: ; |
275 >>> print c.parser.parseString('output into > afile.txt').dump() | 277 >>> print c.parser.parseString('output into > afile.txt').dump() |
276 ['output', ' into', '>', 'afile.txt'] | 278 ['output', 'into', '>', 'afile.txt'] |
277 - args: into | |
278 - command: output | |
279 - output: > | |
280 - outputTo: afile.txt | |
281 - statement: ['output', ' into'] | |
282 - args: into | |
283 - command: output | |
284 >>> print c.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump() | |
285 ['output', 'into', ';', 'sufx', '|', 'pipethrume plz', '>', 'afile.txt'] | |
286 - args: into | 279 - args: into |
287 - command: output | 280 - command: output |
288 - output: > | 281 - output: > |
289 - outputTo: afile.txt | 282 - outputTo: afile.txt |
290 - pipeTo: pipethrume plz | 283 - statement: ['output', 'into'] |
284 - args: into | |
285 - command: output | |
286 >>> print c.parser.parseString('output into;sufx | pipethrume plz > afile.txt').dump() | |
287 ['output', 'into', ';', 'sufx', '|', ' pipethrume plz', '>', 'afile.txt'] | |
288 - args: into | |
289 - command: output | |
290 - output: > | |
291 - outputTo: afile.txt | |
292 - pipeTo: pipethrume plz | |
291 - statement: ['output', 'into', ';'] | 293 - statement: ['output', 'into', ';'] |
292 - args: into | 294 - args: into |
293 - command: output | 295 - command: output |
294 - terminator: ; | 296 - terminator: ; |
295 - suffix: sufx | 297 - suffix: sufx |
296 - terminator: ; | 298 - terminator: ; |
297 >>> print c.parser.parseString('output to paste buffer >> ').dump() | 299 >>> print c.parser.parseString('output to paste buffer >> ').dump() |
298 ['output', ' to paste buffer', '>>', ''] | 300 ['output', 'to paste buffer', '>>', ''] |
299 - args: to paste buffer | 301 - args: to paste buffer |
300 - command: output | 302 - command: output |
301 - output: >> | 303 - output: >> |
302 - statement: ['output', ' to paste buffer'] | 304 - statement: ['output', 'to paste buffer'] |
303 - args: to paste buffer | 305 - args: to paste buffer |
304 - command: output | 306 - command: output |
305 >>> print c.parser.parseString('ignore the /* commented | > */ stuff;').dump() | 307 >>> print c.parser.parseString('ignore the /* commented | > */ stuff;').dump() |
306 ['ignore', 'the /* commented | > */ stuff', ';', ''] | 308 ['ignore', 'the /* commented | > */ stuff', ';', ''] |
307 - args: the /* commented | > */ stuff | 309 - args: the /* commented | > */ stuff |
308 - command: ignore | 310 - command: ignore |
351 ''' | 353 ''' |
352 outputParser = pyparsing.oneOf(['>>','>'])('output') | 354 outputParser = pyparsing.oneOf(['>>','>'])('output') |
353 terminatorParser = pyparsing.oneOf(self.terminators)('terminator') | 355 terminatorParser = pyparsing.oneOf(self.terminators)('terminator') |
354 stringEnd = pyparsing.stringEnd ^ '\nEOF' | 356 stringEnd = pyparsing.stringEnd ^ '\nEOF' |
355 multilineCommand = pyparsing.Or([pyparsing.Keyword(c, caseless=self.caseInsensitive) for c in self.multilineCommands])('multilineCommand') | 357 multilineCommand = pyparsing.Or([pyparsing.Keyword(c, caseless=self.caseInsensitive) for c in self.multilineCommands])('multilineCommand') |
356 oneLineCommand = pyparsing.Word(pyparsing.printables)('command') | 358 oneLineCommand = pyparsing.Word(self.legalChars)('command') |
357 afterElements = \ | 359 afterElements = \ |
358 pyparsing.Optional('|' + pyparsing.SkipTo(outputParser ^ stringEnd)('pipeTo')) + \ | 360 pyparsing.Optional('|' + pyparsing.SkipTo(outputParser ^ stringEnd)('pipeTo')) + \ |
359 pyparsing.Optional(outputParser + pyparsing.SkipTo(stringEnd).setParseAction(lambda x: x[0].strip())('outputTo')) | 361 pyparsing.Optional(outputParser + pyparsing.SkipTo(stringEnd).setParseAction(lambda x: x[0].strip())('outputTo')) |
360 if self.caseInsensitive: | 362 if self.caseInsensitive: |
361 multilineCommand.setParseAction(lambda x: x[0].lower()) | 363 multilineCommand.setParseAction(lambda x: x[0].lower()) |
362 oneLineCommand.setParseAction(lambda x: x[0].lower()) | 364 oneLineCommand.setParseAction(lambda x: x[0].lower()) |
363 self.parser = ( | 365 self.parser = ( |
364 (((multilineCommand ^ oneLineCommand) + pyparsing.SkipTo(terminatorParser)('args') + terminatorParser)('statement') + | 366 (((multilineCommand ^ oneLineCommand) + pyparsing.SkipTo(terminatorParser).setParseAction(lambda x: x[0].strip())('args') + terminatorParser)('statement') + |
365 pyparsing.SkipTo(outputParser ^ '|' ^ stringEnd)('suffix') + afterElements) | 367 pyparsing.SkipTo(outputParser ^ '|' ^ stringEnd)('suffix') + afterElements) |
366 ^ | 368 ^ |
367 multilineCommand + pyparsing.SkipTo(pyparsing.stringEnd) | 369 multilineCommand + pyparsing.SkipTo(pyparsing.stringEnd) |
368 ^ | 370 ^ |
369 ((oneLineCommand + pyparsing.SkipTo(terminatorParser ^ stringEnd ^ '|' ^ outputParser)('args'))('statement') + | 371 ((oneLineCommand + pyparsing.SkipTo(terminatorParser ^ stringEnd ^ '|' ^ outputParser).setParseAction(lambda x:x[0].strip())('args'))('statement') + |
370 afterElements) | 372 pyparsing.Optional(terminatorParser) + afterElements) |
371 ) | 373 ) |
372 self.commentGrammars.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).setParseAction(lambda x: '') | 374 self.commentGrammars.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).setParseAction(lambda x: '') |
373 self.commentInProgress.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(pyparsing.cStyleComment) | 375 self.commentInProgress.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(pyparsing.cStyleComment) |
374 self.parser.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(self.commentGrammars).ignore(self.commentInProgress) | 376 self.parser.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(self.commentGrammars).ignore(self.commentInProgress) |
375 | 377 |
376 inputMark = pyparsing.Literal('<') | 378 inputMark = pyparsing.Literal('<') |
377 inputMark.setParseAction(lambda x: '') | 379 inputMark.setParseAction(lambda x: '') |
378 inputFrom = pyparsing.Word(pyparsing.printables)('inputFrom') | 380 inputFrom = pyparsing.Word(self.legalChars)('inputFrom') |
379 inputFrom.setParseAction(lambda x: (x and open(x[0]).read()) or getPasteBuffer()) | 381 inputFrom.setParseAction(lambda x: (x and open(x[0]).read()) or getPasteBuffer()) |
380 self.inputParser = inputMark + pyparsing.Optional(inputFrom) | 382 self.inputParser = inputMark + pyparsing.Optional(inputFrom) |
381 self.inputParser.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(self.commentGrammars).ignore(self.commentInProgress) | 383 self.inputParser.ignore(pyparsing.sglQuotedString).ignore(pyparsing.dblQuotedString).ignore(self.commentGrammars).ignore(self.commentInProgress) |
382 | 384 |
383 def parsed(self, raw): | 385 def parsed(self, raw, useTerminatorFrom=None): |
384 s = self.inputParser.transformString(raw) | 386 s = self.inputParser.transformString(raw) |
385 for (shortcut, expansion) in self.shortcuts.items(): | 387 for (shortcut, expansion) in self.shortcuts.items(): |
386 if s.startswith(shortcut): | 388 if s.startswith(shortcut): |
387 s = s.replace(shortcut, expansion + ' ', 1) | 389 s = s.replace(shortcut, expansion + ' ', 1) |
388 break | 390 break |
389 result = self.parser.parseString(s) | 391 result = self.parser.parseString(s) |
390 result['command'] = result.multilineCommand or result.command | 392 result['command'] = result.multilineCommand or result.command |
391 result['statement'] = ' '.join(result.statement) | 393 #result['statement'] = ' '.join(result.statement) |
392 result['raw'] = raw | 394 result['raw'] = raw |
393 result['clean'] = self.commentGrammars.transformString(result.statement) | 395 #result['clean'] = self.commentGrammars.transformString(result.statement) |
394 result['cleanArgs'] = self.commentGrammars.transformString(result.args) | 396 result['clean'] = self.commentGrammars.transformString(result.args) |
395 return result | 397 result['expanded'] = s |
396 | 398 if useTerminatorFrom: |
397 def extractCommand(self, statement): | 399 result['terminator'] = useTerminatorFrom.parsed.terminator |
398 try: | 400 result['suffix'] = useTerminatorFrom.parsed.suffix |
399 (command, args) = statement.split(None,1) | 401 p = ParsedString(result.args) |
400 except ValueError: | 402 p.parsed = result |
401 (command, args) = statement, '' | 403 return p |
402 if self.caseInsensitive: | 404 |
403 command = command.lower() | |
404 return command, args | |
405 | |
406 def onecmd(self, line, assumeComplete=False): | 405 def onecmd(self, line, assumeComplete=False): |
407 """Interpret the argument as though it had been typed in response | 406 """Interpret the argument as though it had been typed in response |
408 to the prompt. | 407 to the prompt. |
409 | 408 |
410 This may be overridden, but should not normally need to be; | 409 This may be overridden, but should not normally need to be; |
411 see the precmd() and postcmd() methods for useful execution hooks. | 410 see the precmd() and postcmd() methods for useful execution hooks. |
412 The return value is a flag indicating whether interpretation of | 411 The return value is a flag indicating whether interpretation of |
413 commands by the interpreter should stop. | 412 commands by the interpreter should stop. |
413 | |
414 This (`cmd2`) version of `onecmd` already override's `cmd`'s `onecmd`. | |
414 | 415 |
415 """ | 416 """ |
416 line = line.strip() | 417 line = line.strip() |
417 if not line: | 418 if not line: |
418 return | 419 return self.emptyline() |
419 if not pyparsing.Or(self.commentGrammars).setParseAction(lambda x: '').transformString(line): | 420 if not pyparsing.Or(self.commentGrammars).setParseAction(lambda x: '').transformString(line): |
420 return | 421 return 0 |
421 try: | 422 try: |
422 statement = self.parsed(line) | 423 statement = self.parsed(line) |
423 if assumeComplete: | 424 if assumeComplete: |
424 if statement.multilineCommand and not statement.terminator: | 425 if statement.parsed.multilineCommand and not statement.parsed.terminator: |
425 statement = self.parsed(statement.raw + self.terminators[0]) | 426 statement.parsed.terminator = self.terminators[0] |
426 else: | 427 else: |
427 while statement.multilineCommand and not statement.terminator: | 428 while statement.parsed.multilineCommand and not statement.parsed.terminator: |
428 statement = self.parsed('%s\n%s' % (statement.raw, | 429 statement = self.parsed('%s\n%s' % (statement.parsed.raw, |
429 self.pseudo_raw_input(self.continuationPrompt))) | 430 self.pseudo_raw_input(self.continuationPrompt))) |
430 except Exception, e: | 431 except Exception, e: |
431 print e | 432 print e |
432 return 0 | 433 return 0 |
433 | 434 |
434 statekeeper = None | 435 statekeeper = None |
435 stop = 0 | 436 stop = 0 |
436 | 437 |
437 if statement.pipeTo: | 438 if statement.parsed.pipeTo: |
438 redirect = subprocess.Popen(statement.pipeTo, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) | 439 redirect = subprocess.Popen(statement.parsed.pipeTo, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) |
439 statekeeper = Statekeeper(self, ('stdout',)) | 440 statekeeper = Statekeeper(self, ('stdout',)) |
440 self.stdout = redirect.stdin | 441 self.stdout = redirect.stdin |
441 elif statement.output: | 442 elif statement.parsed.output: |
442 statekeeper = Statekeeper(self, ('stdout',)) | 443 statekeeper = Statekeeper(self, ('stdout',)) |
443 if statement.outputTo: | 444 if statement.parsed.outputTo: |
444 mode = 'w' | 445 mode = 'w' |
445 if statement.output == '>>': | 446 if statement.parsed.output == '>>': |
446 mode = 'a' | 447 mode = 'a' |
447 try: | 448 try: |
448 self.stdout = open(statement.outputTo, mode) | 449 self.stdout = open(statement.parsed.outputTo, mode) |
449 except OSError, e: | 450 except OSError, e: |
450 print e | 451 print e |
451 return 0 | 452 return 0 |
452 else: | 453 else: |
453 statekeeper = Statekeeper(self, ('stdout',)) | 454 statekeeper = Statekeeper(self, ('stdout',)) |
454 self.stdout = tempfile.TemporaryFile() | 455 self.stdout = tempfile.TemporaryFile() |
455 if statement.output == '>>': | 456 if statement.parsed.output == '>>': |
456 self.stdout.write(getPasteBuffer()) | 457 self.stdout.write(getPasteBuffer()) |
457 try: | 458 try: |
458 stop = cmd.Cmd.onecmd(self, statement.clean) | 459 # "heart" of the command, replace's cmd's onecmd() |
460 self.lastcmd = statement.parsed.expanded | |
461 try: | |
462 func = getattr(self, 'do_' + statement.parsed.command) | |
463 except AttributeError: | |
464 return self.default(statement) | |
465 stop = func(statement) | |
459 except Exception, e: | 466 except Exception, e: |
460 print e | 467 print e |
461 try: | 468 try: |
462 if statement.command not in self.excludeFromHistory: | 469 if statement.parsed.command not in self.excludeFromHistory: |
463 self.history.append(statement.raw) | 470 self.history.append(statement.parsed.raw) |
464 finally: | 471 finally: |
465 if statekeeper: | 472 if statekeeper: |
466 if statement.output and not statement.outputTo: | 473 if statement.parsed.output and not statement.parsed.outputTo: |
467 self.stdout.seek(0) | 474 self.stdout.seek(0) |
468 try: | 475 try: |
469 writeToPasteBuffer(self.stdout.read()) | 476 writeToPasteBuffer(self.stdout.read()) |
470 except Exception, e: | 477 except Exception, e: |
471 print str(e) | 478 print str(e) |
472 elif statement.pipeTo: | 479 elif statement.parsed.pipeTo: |
473 for result in redirect.communicate(): | 480 for result in redirect.communicate(): |
474 statekeeper.stdout.write(result or '') | 481 statekeeper.stdout.write(result or '') |
475 self.stdout.close() | 482 self.stdout.close() |
476 statekeeper.restore() | 483 statekeeper.restore() |
477 | 484 |
652 os.system('%s %s' % (self.editor, filename)) | 659 os.system('%s %s' % (self.editor, filename)) |
653 self.do__load(filename) | 660 self.do__load(filename) |
654 do_edit = do_ed | 661 do_edit = do_ed |
655 | 662 |
656 saveparser = (pyparsing.Optional(pyparsing.Word(pyparsing.nums)^'*')("idx") + | 663 saveparser = (pyparsing.Optional(pyparsing.Word(pyparsing.nums)^'*')("idx") + |
657 pyparsing.Optional(pyparsing.Word(pyparsing.printables))("fname") + | 664 pyparsing.Optional(pyparsing.Word(legalChars + '/\\'))("fname") + |
658 pyparsing.stringEnd) | 665 pyparsing.stringEnd) |
659 def do_save(self, arg): | 666 def do_save(self, arg): |
660 """`save [N] [filename.ext]` | 667 """`save [N] [filename.ext]` |
661 Saves command from history to file. | 668 Saves command from history to file. |
662 N => Number of command (from history), or `*`; | 669 N => Number of command (from history), or `*`; |
903 def tearDown(self): | 910 def tearDown(self): |
904 if self.CmdApp: | 911 if self.CmdApp: |
905 self.outputTrap.tearDown() | 912 self.outputTrap.tearDown() |
906 | 913 |
907 if __name__ == '__main__': | 914 if __name__ == '__main__': |
908 doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE) | 915 #doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE) |
916 c = Cmd() |