Mercurial > python-cmd2
comparison cmd2.py @ 406:cc5c68e15a83
merged
author | Catherine Devlin <catherine.devlin@gmail.com> |
---|---|
date | Sun, 07 Nov 2010 09:20:33 -0500 |
parents | 3bef4253cf1b be18c88a0fc8 |
children | f5aa16a22b52 |
comparison
equal
deleted
inserted
replaced
405:3bef4253cf1b | 406:cc5c68e15a83 |
---|---|
44 if sys.version_info[0] > 2: | 44 if sys.version_info[0] > 2: |
45 import pyparsing_py3 as pyparsing | 45 import pyparsing_py3 as pyparsing |
46 raw_input = input | 46 raw_input = input |
47 else: | 47 else: |
48 import pyparsing | 48 import pyparsing |
49 | 49 |
50 __version__ = '0.6.0' | 50 __version__ = '0.6.2' |
51 | 51 |
52 class OptionParser(optparse.OptionParser): | 52 class OptionParser(optparse.OptionParser): |
53 def exit(self, status=0, msg=None): | 53 def exit(self, status=0, msg=None): |
54 self.values._exit = True | 54 self.values._exit = True |
55 if msg: | 55 if msg: |
203 def get_paste_buffer(): | 203 def get_paste_buffer(): |
204 xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) | 204 xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) |
205 return xclipproc.stdout.read() | 205 return xclipproc.stdout.read() |
206 def write_to_paste_buffer(txt): | 206 def write_to_paste_buffer(txt): |
207 xclipproc = subprocess.Popen('xclip -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) | 207 xclipproc = subprocess.Popen('xclip -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) |
208 xclipproc.stdin.write(txt) | 208 xclipproc.stdin.write(txt.encode()) |
209 xclipproc.stdin.close() | 209 xclipproc.stdin.close() |
210 # but we want it in both the "primary" and "mouse" clipboards | 210 # but we want it in both the "primary" and "mouse" clipboards |
211 xclipproc = subprocess.Popen('xclip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) | 211 xclipproc = subprocess.Popen('xclip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) |
212 xclipproc.stdin.write(txt) | 212 xclipproc.stdin.write(txt.encode()) |
213 xclipproc.stdin.close() | 213 xclipproc.stdin.close() |
214 else: | 214 else: |
215 def get_paste_buffer(*args): | 215 def get_paste_buffer(*args): |
216 raise OSError, pastebufferr % ('xclip', 'On Debian/Ubuntu, install with "sudo apt-get install xclip"') | 216 raise OSError, pastebufferr % ('xclip', 'On Debian/Ubuntu, install with "sudo apt-get install xclip"') |
217 write_to_paste_buffer = get_paste_buffer | 217 write_to_paste_buffer = get_paste_buffer |
397 timing Report execution times | 397 timing Report execution times |
398 abbrev Accept abbreviated commands | 398 abbrev Accept abbreviated commands |
399 ''') | 399 ''') |
400 | 400 |
401 def poutput(self, msg): | 401 def poutput(self, msg): |
402 '''Convenient shortcut for self.stdout.write(); adds newline if necessary.''' | |
402 if msg: | 403 if msg: |
403 self.stdout.write(msg) | 404 self.stdout.write(msg) |
404 if msg[-1] != '\n': | 405 if msg[-1] != '\n': |
405 self.stdout.write('\n') | 406 self.stdout.write('\n') |
406 def perror(self, errmsg, statement=None): | 407 def perror(self, errmsg, statement=None): |
699 pyparsing.Optional(fileName) + (pyparsing.stringEnd | '|') | 700 pyparsing.Optional(fileName) + (pyparsing.stringEnd | '|') |
700 self.inputParser.ignore(pyparsing.quotedString).ignore(self.commentGrammars).ignore(self.commentInProgress) | 701 self.inputParser.ignore(pyparsing.quotedString).ignore(self.commentGrammars).ignore(self.commentInProgress) |
701 | 702 |
702 def preparse(self, raw, **kwargs): | 703 def preparse(self, raw, **kwargs): |
703 return raw | 704 return raw |
704 | 705 def postparse(self, parseResult): |
706 return parseResult | |
707 | |
705 def parsed(self, raw, **kwargs): | 708 def parsed(self, raw, **kwargs): |
706 if isinstance(raw, ParsedString): | 709 if isinstance(raw, ParsedString): |
707 p = raw | 710 p = raw |
708 else: | 711 else: |
709 # preparse is an overridable hook; default makes no changes | 712 # preparse is an overridable hook; default makes no changes |
715 s = s.replace(shortcut, expansion + ' ', 1) | 718 s = s.replace(shortcut, expansion + ' ', 1) |
716 break | 719 break |
717 result = self.parser.parseString(s) | 720 result = self.parser.parseString(s) |
718 result['raw'] = raw | 721 result['raw'] = raw |
719 result['command'] = result.multilineCommand or result.command | 722 result['command'] = result.multilineCommand or result.command |
723 result = self.postparse(result) | |
720 p = ParsedString(result.args) | 724 p = ParsedString(result.args) |
721 p.parsed = result | 725 p.parsed = result |
722 p.parser = self.parsed | 726 p.parser = self.parsed |
723 for (key, val) in kwargs.items(): | 727 for (key, val) in kwargs.items(): |
724 p.parsed[key] = val | 728 p.parsed[key] = val |
765 except Exception, e: | 769 except Exception, e: |
766 self.perror(str(e), statement) | 770 self.perror(str(e), statement) |
767 finally: | 771 finally: |
768 return self.postparsing_postcmd(stop) | 772 return self.postparsing_postcmd(stop) |
769 def complete_statement(self, line): | 773 def complete_statement(self, line): |
774 """Keep accepting lines of input until the command is complete.""" | |
770 if (not line) or ( | 775 if (not line) or ( |
771 not pyparsing.Or(self.commentGrammars). | 776 not pyparsing.Or(self.commentGrammars). |
772 setParseAction(lambda x: '').transformString(line)): | 777 setParseAction(lambda x: '').transformString(line)): |
773 raise EmptyStatement | 778 raise EmptyStatement |
774 statement = self.parsed(line) | 779 statement = self.parsed(line) |
785 self.kept_state = Statekeeper(self, ('stdout',)) | 790 self.kept_state = Statekeeper(self, ('stdout',)) |
786 self.kept_sys = Statekeeper(sys, ('stdout',)) | 791 self.kept_sys = Statekeeper(sys, ('stdout',)) |
787 self.redirect = subprocess.Popen(statement.parsed.pipeTo, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) | 792 self.redirect = subprocess.Popen(statement.parsed.pipeTo, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) |
788 sys.stdout = self.stdout = self.redirect.stdin | 793 sys.stdout = self.stdout = self.redirect.stdin |
789 elif statement.parsed.output: | 794 elif statement.parsed.output: |
795 if (not statement.parsed.outputTo) and (not can_clip): | |
796 self.perror('Cannot redirect to paste buffer; install ``xclip`` and re-run to enable') | |
797 return | |
790 self.kept_state = Statekeeper(self, ('stdout',)) | 798 self.kept_state = Statekeeper(self, ('stdout',)) |
791 self.kept_sys = Statekeeper(sys, ('stdout',)) | 799 self.kept_sys = Statekeeper(sys, ('stdout',)) |
792 if statement.parsed.outputTo: | 800 if statement.parsed.outputTo: |
793 mode = 'w' | 801 mode = 'w' |
794 if statement.parsed.output == '>>': | 802 if statement.parsed.output == '>>': |
795 mode = 'a' | 803 mode = 'a' |
796 sys.stdout = self.stdout = open(os.path.expanduser(statement.parsed.outputTo), mode) | 804 sys.stdout = self.stdout = open(os.path.expanduser(statement.parsed.outputTo), mode) |
797 else: | 805 else: |
798 sys.stdout = self.stdout = tempfile.TemporaryFile() | 806 sys.stdout = self.stdout = tempfile.TemporaryFile(mode="w+") |
799 if statement.parsed.output == '>>': | 807 if statement.parsed.output == '>>': |
800 self.stdout.write(get_paste_buffer()) | 808 self.stdout.write(get_paste_buffer()) |
801 | 809 |
802 def restore_output(self, statement): | 810 def restore_output(self, statement): |
803 if self.kept_state: | 811 if self.kept_state: |
1380 """Tries to force a new value into the same type as the current.""" | 1388 """Tries to force a new value into the same type as the current.""" |
1381 typ = type(current) | 1389 typ = type(current) |
1382 if typ == bool: | 1390 if typ == bool: |
1383 try: | 1391 try: |
1384 return bool(int(new)) | 1392 return bool(int(new)) |
1385 except ValueError, TypeError: | 1393 except (ValueError, TypeError): |
1386 pass | 1394 pass |
1387 try: | 1395 try: |
1388 new = new.lower() | 1396 new = new.lower() |
1389 except: | 1397 except: |
1390 pass | 1398 pass |
1451 for fname in glob.glob(fileset): | 1459 for fname in glob.glob(fileset): |
1452 tfile = open(fname) | 1460 tfile = open(fname) |
1453 self.transcripts[fname] = iter(tfile.readlines()) | 1461 self.transcripts[fname] = iter(tfile.readlines()) |
1454 tfile.close() | 1462 tfile.close() |
1455 if not len(self.transcripts): | 1463 if not len(self.transcripts): |
1456 raise StandardError, "No test files found - nothing to test." | 1464 raise (StandardError,), "No test files found - nothing to test." |
1457 def setUp(self): | 1465 def setUp(self): |
1458 if self.CmdApp: | 1466 if self.CmdApp: |
1459 self.outputTrap = OutputTrap() | 1467 self.outputTrap = OutputTrap() |
1460 self.cmdapp = self.CmdApp() | 1468 self.cmdapp = self.CmdApp() |
1461 self.fetchTranscripts() | 1469 self.fetchTranscripts() |
1472 anyWhitespace = re.compile(r'\s', re.DOTALL | re.MULTILINE) | 1480 anyWhitespace = re.compile(r'\s', re.DOTALL | re.MULTILINE) |
1473 def _test_transcript(self, fname, transcript): | 1481 def _test_transcript(self, fname, transcript): |
1474 lineNum = 0 | 1482 lineNum = 0 |
1475 try: | 1483 try: |
1476 line = transcript.next() | 1484 line = transcript.next() |
1485 lineNum += 1 | |
1477 while True: | 1486 while True: |
1478 while not line.startswith(self.cmdapp.prompt): | 1487 while not line.startswith(self.cmdapp.prompt): |
1479 line = transcript.next() | 1488 line = transcript.next() |
1489 lineNum += 1 | |
1480 command = [line[len(self.cmdapp.prompt):]] | 1490 command = [line[len(self.cmdapp.prompt):]] |
1481 line = transcript.next() | 1491 line = transcript.next() |
1482 while line.startswith(self.cmdapp.continuation_prompt): | 1492 while line.startswith(self.cmdapp.continuation_prompt): |
1483 command.append(line[len(self.cmdapp.continuation_prompt):]) | 1493 command.append(line[len(self.cmdapp.continuation_prompt):]) |
1484 line = transcript.next() | 1494 line = transcript.next() |
1495 lineNum += 1 | |
1485 command = ''.join(command) | 1496 command = ''.join(command) |
1486 stop = self.cmdapp.onecmd_plus_hooks(command) | 1497 stop = self.cmdapp.onecmd_plus_hooks(command) |
1487 #TODO: should act on ``stop`` | 1498 #TODO: should act on ``stop`` |
1488 result = self.outputTrap.read() | 1499 result = self.outputTrap.read() |
1489 if line.startswith(self.cmdapp.prompt): | 1500 if line.startswith(self.cmdapp.prompt): |
1493 continue | 1504 continue |
1494 expected = [] | 1505 expected = [] |
1495 while not line.startswith(self.cmdapp.prompt): | 1506 while not line.startswith(self.cmdapp.prompt): |
1496 expected.append(line) | 1507 expected.append(line) |
1497 line = transcript.next() | 1508 line = transcript.next() |
1509 lineNum += 1 | |
1498 expected = ''.join(expected) | 1510 expected = ''.join(expected) |
1499 message = '\nFile %s, line %d\nCommand was:\n%s\nExpected:\n%s\nGot:\n%s\n'%\ | 1511 message = '\nFile %s, line %d\nCommand was:\n%s\nExpected:\n%s\nGot:\n%s\n'%\ |
1500 (fname, lineNum, command, expected, result) | 1512 (fname, lineNum, command, expected, result) |
1501 expected = self.expectationParser.transformString(expected) | 1513 expected = self.expectationParser.transformString(expected) |
1502 # checking whitespace is a pain - let's skip it | 1514 # checking whitespace is a pain - let's skip it |
1503 expected = self.anyWhitespace.sub('', expected) | 1515 expected = self.anyWhitespace.sub('', expected) |
1504 result = self.anyWhitespace.sub('', result) | 1516 result = self.anyWhitespace.sub('', result) |
1505 self.assert_(re.match(expected, result, re.MULTILINE | re.DOTALL), message) | 1517 self.assert_(re.match(expected, result, re.MULTILINE | re.DOTALL), message) |
1506 except StopIteration: | 1518 except StopIteration: |
1507 pass | 1519 message = 'Last %d lines never seen, beginning with\n%s' % (len(expected), expected[0]) |
1520 self.assert_(len(expected) < 3, message) | |
1508 def tearDown(self): | 1521 def tearDown(self): |
1509 if self.CmdApp: | 1522 if self.CmdApp: |
1510 self.outputTrap.tearDown() | 1523 self.outputTrap.tearDown() |
1511 | 1524 |
1512 if __name__ == '__main__': | 1525 if __name__ == '__main__': |