Mercurial > python-cmd2
comparison cmd2.py @ 287:1cd23003e8d5
refactoring, but something went wrong with comments
author | catherine@bothari |
---|---|
date | Mon, 19 Oct 2009 18:00:26 -0400 |
parents | 3c4ba65cb303 |
children | e743cf74c518 |
comparison
equal
deleted
inserted
replaced
286:3c4ba65cb303 | 287:1cd23003e8d5 |
---|---|
20 | 20 |
21 - Catherine Devlin, Jan 03 2008 - catherinedevlin.blogspot.com | 21 - Catherine Devlin, Jan 03 2008 - catherinedevlin.blogspot.com |
22 | 22 |
23 mercurial repository at http://www.assembla.com/wiki/show/python-cmd2 | 23 mercurial repository at http://www.assembla.com/wiki/show/python-cmd2 |
24 """ | 24 """ |
25 import cmd, re, os, sys, optparse, subprocess, tempfile, pyparsing, doctest | 25 import cmd |
26 import unittest, string, datetime, urllib, glob, traceback | 26 import re |
27 import os | |
28 import sys | |
29 import optparse | |
30 import subprocess | |
31 import tempfile | |
32 import doctest | |
33 import unittest | |
34 import datetime | |
35 import urllib | |
36 import glob | |
37 import traceback | |
27 from code import InteractiveConsole, InteractiveInterpreter, softspace | 38 from code import InteractiveConsole, InteractiveInterpreter, softspace |
28 from optparse import make_option | 39 from optparse import make_option |
40 | |
41 import pyparsing | |
29 __version__ = '0.5.6' | 42 __version__ = '0.5.6' |
30 | 43 |
31 class OptionParser(optparse.OptionParser): | 44 class OptionParser(optparse.OptionParser): |
32 def exit(self, status=0, msg=None): | 45 def exit(self, status=0, msg=None): |
33 self.values._exit = True | 46 self.values._exit = True |
48 If you override this in a subclass, it should not return -- it | 61 If you override this in a subclass, it should not return -- it |
49 should either exit or raise an exception. | 62 should either exit or raise an exception. |
50 """ | 63 """ |
51 raise | 64 raise |
52 | 65 |
53 def remainingArgs(oldArgs, newArgList): | 66 def remaining_args(oldArgs, newArgList): |
54 ''' | 67 ''' |
55 Preserves the spacing originally in the argument after | 68 Preserves the spacing originally in the argument after |
56 the removal of options. | 69 the removal of options. |
57 | 70 |
58 >>> remainingArgs('-f bar bar cow', ['bar', 'cow']) | 71 >>> remaining_args('-f bar bar cow', ['bar', 'cow']) |
59 'bar cow' | 72 'bar cow' |
60 ''' | 73 ''' |
61 pattern = '\s+'.join(re.escape(a) for a in newArgList) + '\s*$' | 74 pattern = '\s+'.join(re.escape(a) for a in newArgList) + '\s*$' |
62 matchObj = re.search(pattern, oldArgs) | 75 matchObj = re.search(pattern, oldArgs) |
63 return oldArgs[matchObj.start():] | 76 return oldArgs[matchObj.start():] |
73 return None | 86 return None |
74 | 87 |
75 optparse.Values.get = _attr_get_ | 88 optparse.Values.get = _attr_get_ |
76 | 89 |
77 def options(option_list): | 90 def options(option_list): |
91 '''Used as a decorator and passed a list of optparse-style options, | |
92 alters a cmd2 methodo populate its ``opts`` argument from its | |
93 raw text argument. | |
94 | |
95 Example: transform | |
96 def do_something(self, arg): | |
97 | |
98 into | |
99 @options([make_option('-q', '--quick', action="store_true", | |
100 help="Makes things fast")]) | |
101 def do_something(self, arg, opts): | |
102 if opts.quick: | |
103 self.fast_button = True | |
104 ''' | |
78 def option_setup(func): | 105 def option_setup(func): |
79 optionParser = OptionParser() | 106 optionParser = OptionParser() |
80 for opt in option_list: | 107 for opt in option_list: |
81 optionParser.add_option(opt) | 108 optionParser.add_option(opt) |
82 optionParser.set_usage("%s [options] arg" % func.__name__.strip('do_')) | 109 optionParser.set_usage("%s [options] arg" % func.__name__.strip('do_')) |
83 optionParser._func = func | 110 optionParser._func = func |
84 def newFunc(instance, arg): | 111 def new_func(instance, arg): |
85 try: | 112 try: |
86 if hasattr(arg, 'parsed'): | 113 if hasattr(arg, 'parsed'): |
87 args = arg.parsed.raw | 114 args = arg.parsed.raw |
88 else: | 115 else: |
89 args = arg | 116 args = arg |
90 opts, newArgList = optionParser.parse_args(args.split()) | 117 opts, newArgList = optionParser.parse_args(args.split()) |
91 # Must find the remaining args in the original argument list, but | 118 # Must find the remaining args in the original argument list, but |
92 # mustn't include the command itself | 119 # mustn't include the command itself |
93 if hasattr(arg, 'parsed') and newArgList[0] == arg.parsed.command: | 120 if hasattr(arg, 'parsed') and newArgList[0] == arg.parsed.command: |
94 newArgList = newArgList[1:] | 121 newArgList = newArgList[1:] |
95 newArgs = remainingArgs(args, newArgList) | 122 newArgs = remaining_args(args, newArgList) |
96 except (optparse.OptionValueError, optparse.BadOptionError, | 123 except (optparse.OptionValueError, optparse.BadOptionError, |
97 optparse.OptionError, optparse.AmbiguousOptionError, | 124 optparse.OptionError, optparse.AmbiguousOptionError, |
98 optparse.OptionConflictError), e: | 125 optparse.OptionConflictError), e: |
99 print e | 126 print e |
100 optionParser.print_help() | 127 optionParser.print_help() |
113 else: | 140 else: |
114 arg = newArgs | 141 arg = newArgs |
115 result = func(instance, arg, opts) | 142 result = func(instance, arg, opts) |
116 return result | 143 return result |
117 newFunc.__doc__ = '%s\n%s' % (func.__doc__, optionParser.format_help()) | 144 newFunc.__doc__ = '%s\n%s' % (func.__doc__, optionParser.format_help()) |
118 return newFunc | 145 return new_func |
119 return option_setup | 146 return option_setup |
120 | 147 |
121 class PasteBufferError(EnvironmentError): | 148 class PasteBufferError(EnvironmentError): |
122 if sys.platform[:3] == 'win': | 149 if sys.platform[:3] == 'win': |
123 errmsg = """Redirecting to or from paste buffer requires pywin32 | 150 errmsg = """Redirecting to or from paste buffer requires pywin32 |
135 %s""" | 162 %s""" |
136 | 163 |
137 if subprocess.mswindows: | 164 if subprocess.mswindows: |
138 try: | 165 try: |
139 import win32clipboard | 166 import win32clipboard |
140 def getPasteBuffer(): | 167 def get_paste_buffer(): |
141 win32clipboard.OpenClipboard(0) | 168 win32clipboard.OpenClipboard(0) |
142 try: | 169 try: |
143 result = win32clipboard.GetClipboardData() | 170 result = win32clipboard.GetClipboardData() |
144 except TypeError: | 171 except TypeError: |
145 result = '' #non-text | 172 result = '' #non-text |
146 win32clipboard.CloseClipboard() | 173 win32clipboard.CloseClipboard() |
147 return result | 174 return result |
148 def writeToPasteBuffer(txt): | 175 def write_to_paste_buffer(txt): |
149 win32clipboard.OpenClipboard(0) | 176 win32clipboard.OpenClipboard(0) |
150 win32clipboard.EmptyClipboard() | 177 win32clipboard.EmptyClipboard() |
151 win32clipboard.SetClipboardText(txt) | 178 win32clipboard.SetClipboardText(txt) |
152 win32clipboard.CloseClipboard() | 179 win32clipboard.CloseClipboard() |
153 except ImportError: | 180 except ImportError: |
154 def getPasteBuffer(*args): | 181 def get_paste_buffer(*args): |
155 raise OSError, pastebufferr % ('pywin32', 'Download from http://sourceforge.net/projects/pywin32/') | 182 raise OSError, pastebufferr % ('pywin32', 'Download from http://sourceforge.net/projects/pywin32/') |
156 setPasteBuffer = getPasteBuffer | 183 setPasteBuffer = get_paste_buffer |
157 else: | 184 else: |
158 can_clip = False | 185 can_clip = False |
159 try: | 186 try: |
160 subprocess.check_call('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) | 187 subprocess.check_call('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) |
161 can_clip = True | 188 can_clip = True |
168 if xclipproc.stdout.read() == teststring: | 195 if xclipproc.stdout.read() == teststring: |
169 can_clip = True | 196 can_clip = True |
170 except (subprocess.CalledProcessError, OSError, IOError): | 197 except (subprocess.CalledProcessError, OSError, IOError): |
171 pass | 198 pass |
172 if can_clip: | 199 if can_clip: |
173 def getPasteBuffer(): | 200 def get_paste_buffer(): |
174 xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) | 201 xclipproc = subprocess.Popen('xclip -o -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) |
175 return xclipproc.stdout.read() | 202 return xclipproc.stdout.read() |
176 def writeToPasteBuffer(txt): | 203 def write_to_paste_buffer(txt): |
177 xclipproc = subprocess.Popen('xclip -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) | 204 xclipproc = subprocess.Popen('xclip -sel clip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) |
178 xclipproc.stdin.write(txt) | 205 xclipproc.stdin.write(txt) |
179 xclipproc.stdin.close() | 206 xclipproc.stdin.close() |
180 # but we want it in both the "primary" and "mouse" clipboards | 207 # but we want it in both the "primary" and "mouse" clipboards |
181 xclipproc = subprocess.Popen('xclip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) | 208 xclipproc = subprocess.Popen('xclip', shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE) |
182 xclipproc.stdin.write(txt) | 209 xclipproc.stdin.write(txt) |
183 xclipproc.stdin.close() | 210 xclipproc.stdin.close() |
184 else: | 211 else: |
185 def getPasteBuffer(*args): | 212 def get_paste_buffer(*args): |
186 raise OSError, pastebufferr % ('xclip', 'On Debian/Ubuntu, install with "sudo apt-get install xclip"') | 213 raise OSError, pastebufferr % ('xclip', 'On Debian/Ubuntu, install with "sudo apt-get install xclip"') |
187 setPasteBuffer = getPasteBuffer | 214 setPasteBuffer = get_paste_buffer |
188 writeToPasteBuffer = getPasteBuffer | 215 writeToPasteBuffer = get_paste_buffer |
189 | 216 |
190 pyparsing.ParserElement.setDefaultWhitespaceChars(' \t') | 217 pyparsing.ParserElement.setDefaultWhitespaceChars(' \t') |
191 | 218 |
192 class ParsedString(str): | 219 class ParsedString(str): |
193 pass | 220 pass |
237 try: | 264 try: |
238 result = open(os.path.expanduser(fname[0])).read() | 265 result = open(os.path.expanduser(fname[0])).read() |
239 except IOError: | 266 except IOError: |
240 result = '< %s' % fname[0] # wasn't a file after all | 267 result = '< %s' % fname[0] # wasn't a file after all |
241 else: | 268 else: |
242 result = getPasteBuffer() | 269 result = get_paste_buffer() |
243 return result | 270 return result |
244 | 271 |
245 class EmbeddedConsoleExit(Exception): | 272 class EmbeddedConsoleExit(Exception): |
246 pass | 273 pass |
247 | 274 |
280 shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'} | 307 shortcuts = {'?': 'help', '!': 'shell', '@': 'load', '@@': '_relative_load'} |
281 excludeFromHistory = '''run r list l history hi ed edit li eof'''.split() | 308 excludeFromHistory = '''run r list l history hi ed edit li eof'''.split() |
282 noSpecialParse = 'set ed edit exit'.split() | 309 noSpecialParse = 'set ed edit exit'.split() |
283 defaultExtension = 'txt' # For ``save``, ``load``, etc. | 310 defaultExtension = 'txt' # For ``save``, ``load``, etc. |
284 default_file_name = 'command.txt' # For ``save``, ``load``, etc. | 311 default_file_name = 'command.txt' # For ``save``, ``load``, etc. |
285 abbrev = True | 312 abbrev = True # Abbreviated commands recognized |
286 nonpythoncommand = 'cmd' | |
287 current_script_dir = None | 313 current_script_dir = None |
288 reserved_words = [] | 314 reserved_words = [] |
289 feedback_to_output = False | 315 feedback_to_output = False # Do include nonessentials in >, | output |
290 quiet = False | 316 quiet = False # Do not suppress nonessential output |
291 debug = False | 317 debug = False |
292 settable = ['prompt', 'continuation_prompt', 'debug', 'default_file_name', 'editor', | 318 settable = ['prompt', 'continuation_prompt', 'debug', 'default_file_name', 'editor', |
293 'case_insensitive', 'feedback_to_output', 'quiet', 'echo', 'timing', | 319 'case_insensitive', 'feedback_to_output', 'quiet', 'echo', 'timing', |
294 'abbrev'] | 320 'abbrev'] |
295 settable.sort() | 321 settable.sort() |
309 if not self.quiet: | 335 if not self.quiet: |
310 if self.feedback_to_output: | 336 if self.feedback_to_output: |
311 self.poutput(msg) | 337 self.poutput(msg) |
312 else: | 338 else: |
313 print msg | 339 print msg |
340 _STOP_AND_EXIT = 2 | |
314 editor = os.environ.get('EDITOR') | 341 editor = os.environ.get('EDITOR') |
315 _STOP_AND_EXIT = 2 | |
316 if not editor: | 342 if not editor: |
317 if sys.platform[:3] == 'win': | 343 if sys.platform[:3] == 'win': |
318 editor = 'notepad' | 344 editor = 'notepad' |
319 else: | 345 else: |
320 for editor in ['gedit', 'kate', 'vim', 'emacs', 'nano', 'pico']: | 346 for editor in ['gedit', 'kate', 'vim', 'emacs', 'nano', 'pico']: |
360 | 386 |
361 prefixParser = pyparsing.Empty() | 387 prefixParser = pyparsing.Empty() |
362 commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment]) | 388 commentGrammars = pyparsing.Or([pyparsing.pythonStyleComment, pyparsing.cStyleComment]) |
363 commentGrammars.addParseAction(lambda x: '') | 389 commentGrammars.addParseAction(lambda x: '') |
364 commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd) | 390 commentInProgress = pyparsing.Literal('/*') + pyparsing.SkipTo(pyparsing.stringEnd) |
365 commentInProgress = pyparsing.NoMatch() | |
366 terminators = [';'] | 391 terminators = [';'] |
367 blankLinesAllowed = False | 392 blankLinesAllowed = False |
368 multilineCommands = [] | 393 multilineCommands = [] |
369 | 394 |
370 def _init_parser(self): | 395 def _init_parser(self): |
672 return self.postparsing_postcmd(stop=0) | 697 return self.postparsing_postcmd(stop=0) |
673 else: | 698 else: |
674 statekeeper = Statekeeper(self, ('stdout',)) | 699 statekeeper = Statekeeper(self, ('stdout',)) |
675 self.stdout = tempfile.TemporaryFile() | 700 self.stdout = tempfile.TemporaryFile() |
676 if statement.parsed.output == '>>': | 701 if statement.parsed.output == '>>': |
677 self.stdout.write(getPasteBuffer()) | 702 self.stdout.write(get_paste_buffer()) |
678 try: | 703 try: |
679 try: | 704 try: |
680 # "heart" of the command, replace's cmd's onecmd() | 705 # "heart" of the command, replace's cmd's onecmd() |
681 self.lastcmd = statement.parsed.expanded | 706 self.lastcmd = statement.parsed.expanded |
682 funcname = self.func_named(statement.parsed.command) | 707 funcname = self.func_named(statement.parsed.command) |
695 finally: | 720 finally: |
696 if statekeeper: | 721 if statekeeper: |
697 if statement.parsed.output and not statement.parsed.outputTo: | 722 if statement.parsed.output and not statement.parsed.outputTo: |
698 self.stdout.seek(0) | 723 self.stdout.seek(0) |
699 try: | 724 try: |
700 writeToPasteBuffer(self.stdout.read()) | 725 write_to_paste_buffer(self.stdout.read()) |
701 except Exception, e: | 726 except Exception, e: |
702 self.perror(e) | 727 self.perror(e) |
703 elif statement.parsed.pipeTo: | 728 elif statement.parsed.pipeTo: |
704 for result in redirect.communicate(): | 729 for result in redirect.communicate(): |
705 statekeeper.stdout.write(result or '') | 730 statekeeper.stdout.write(result or '') |
773 | 798 |
774 def do_EOF(self, arg): | 799 def do_EOF(self, arg): |
775 return True | 800 return True |
776 do_eof = do_EOF | 801 do_eof = do_EOF |
777 | 802 |
778 def showParam(self, param): | 803 def show_param(self, param): |
779 any_shown = False | 804 any_shown = False |
780 param = param.strip().lower() | 805 param = param.strip().lower() |
781 for p in self.settable: | 806 for p in self.settable: |
782 if p.startswith(param): | 807 if p.startswith(param): |
783 val = getattr(self, p) | |
784 self.stdout.write('%s: %s\n' % (p, str(getattr(self, p)))) | 808 self.stdout.write('%s: %s\n' % (p, str(getattr(self, p)))) |
785 any_shown = True | 809 any_shown = True |
786 if not any_shown: | 810 if not any_shown: |
787 self.perror("Parameter '%s' not supported (type 'show' for list of parameters)." % param) | 811 self.perror("Parameter '%s' not supported (type 'show' for list of parameters)." % param) |
788 | 812 |
792 do_q = do_quit | 816 do_q = do_quit |
793 | 817 |
794 def do_show(self, arg): | 818 def do_show(self, arg): |
795 '''Shows value of a parameter.''' | 819 '''Shows value of a parameter.''' |
796 if arg.strip(): | 820 if arg.strip(): |
797 self.showParam(arg) | 821 self.show_param(arg) |
798 else: | 822 else: |
799 for param in self.settable: | 823 for param in self.settable: |
800 self.showParam(param) | 824 self.show_param(param) |
801 | 825 |
802 def do_set(self, arg): | 826 def do_set(self, arg): |
803 '''Sets a cmd2 parameter. Accepts abbreviated parameter names so long as there is no ambiguity. | 827 '''Sets a cmd2 parameter. Accepts abbreviated parameter names so long as there is no ambiguity. |
804 Call without arguments for a list of settable parameters with their values.''' | 828 Call without arguments for a list of settable parameters with their values.''' |
805 try: | 829 try: |
852 raise EmbeddedConsoleExit | 876 raise EmbeddedConsoleExit |
853 def onecmd(arg): | 877 def onecmd(arg): |
854 return self.onecmd(arg + '\n') | 878 return self.onecmd(arg + '\n') |
855 self.pystate['quit'] = quit | 879 self.pystate['quit'] = quit |
856 self.pystate['exit'] = quit | 880 self.pystate['exit'] = quit |
857 self.pystate[self.nonpythoncommand] = onecmd | |
858 try: | 881 try: |
859 cprt = 'Type "help", "copyright", "credits" or "license" for more information.' | 882 cprt = 'Type "help", "copyright", "credits" or "license" for more information.' |
860 keepstate = Statekeeper(sys, ('stdin','stdout')) | 883 keepstate = Statekeeper(sys, ('stdin','stdout')) |
861 sys.stdout = self.stdout | 884 sys.stdout = self.stdout |
862 sys.stdin = self.stdin | 885 sys.stdin = self.stdin |
964 except Exception, e: | 987 except Exception, e: |
965 self.perror('Error saving %s: %s' % (fname, str(e))) | 988 self.perror('Error saving %s: %s' % (fname, str(e))) |
966 | 989 |
967 def read_file_or_url(self, fname): | 990 def read_file_or_url(self, fname): |
968 if isinstance(fname, file): | 991 if isinstance(fname, file): |
969 target = open(fname, 'r') | 992 result = open(fname, 'r') |
970 else: | 993 else: |
971 match = self.urlre.match(fname) | 994 match = self.urlre.match(fname) |
972 if match: | 995 if match: |
973 target = urllib.urlopen(match.group(1)) | 996 result = urllib.urlopen(match.group(1)) |
974 else: | 997 else: |
975 fname = os.path.expanduser(fname) | 998 fname = os.path.expanduser(fname) |
976 try: | 999 try: |
977 result = open(os.path.expanduser(fname), 'r') | 1000 result = open(os.path.expanduser(fname), 'r') |
978 except IOError, e: | 1001 except IOError: |
979 result = open('%s.%s' % (os.path.expanduser(fname), | 1002 result = open('%s.%s' % (os.path.expanduser(fname), |
980 self.defaultExtension), 'r') | 1003 self.defaultExtension), 'r') |
981 return result | 1004 return result |
982 | 1005 |
983 def do__relative_load(self, arg=None): | 1006 def do__relative_load(self, arg=None): |
1216 if self.CmdApp: | 1239 if self.CmdApp: |
1217 self.outputTrap.tearDown() | 1240 self.outputTrap.tearDown() |
1218 | 1241 |
1219 if __name__ == '__main__': | 1242 if __name__ == '__main__': |
1220 doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE) | 1243 doctest.testmod(optionflags = doctest.NORMALIZE_WHITESPACE) |
1244 | |
1221 | 1245 |
1222 ''' | 1246 ''' |
1223 To make your application transcript-testable, add text like this to your .py file | 1247 To make your application transcript-testable, add text like this to your .py file |
1224 (replacing CmdLineApp with your own application class's name). Then, a cut-and-pasted | 1248 (replacing CmdLineApp with your own application class's name). Then, a cut-and-pasted |
1225 version of a successful session with your application, saved as a text file, can serve | 1249 version of a successful session with your application, saved as a text file, can serve |