- update cmdln.py to planned 1.0 version. The main changes are
[opensuse:osc.git] / osc / cmdln.py
1 #!/usr/bin/env python
2 # Copyright (c) 2002-2005 ActiveState Corp.
3 # License: MIT (see LICENSE.txt for license details)
4 # Author:  Trent Mick (TrentM@ActiveState.com)
5 # Home:    http://trentm.com/projects/cmdln/
6
7 """An improvement on Python's standard cmd.py module.
8
9 As with cmd.py, this module provides "a simple framework for writing
10 line-oriented command intepreters."  This module provides a 'RawCmdln'
11 class that fixes some design flaws in cmd.Cmd, making it more scalable
12 and nicer to use for good 'cvs'- or 'svn'-style command line interfaces
13 or simple shells.  And it provides a 'Cmdln' class that add
14 optparse-based option processing. Basically you use it like this:
15
16     import cmdln
17
18     class MySVN(cmdln.Cmdln):
19         name = "svn"
20
21         @cmdln.alias('stat', 'st')
22         @cmdln.option('-v', '--verbose', action='store_true'
23                       help='print verbose information')
24         def do_status(self, subcmd, opts, *paths):
25             print "handle 'svn status' command"
26
27         #...
28
29     if __name__ == "__main__":
30         shell = MySVN()
31         retval = shell.main()
32         sys.exit(retval)
33
34 See the README.txt or <http://trentm.com/projects/cmdln/> for more
35 details.
36 """
37
38 __revision__ = "$Id: cmdln.py 1666 2007-05-09 03:13:03Z trentm $"
39 __version_info__ = (1, 0, 0)
40 __version__ = '.'.join(map(str, __version_info__))
41
42 import os
43 import re
44 import cmd
45 import optparse
46 from pprint import pprint
47
48
49
50
51 #---- globals
52
53 LOOP_ALWAYS, LOOP_NEVER, LOOP_IF_EMPTY = range(3)
54
55 # An unspecified optional argument when None is a meaningful value.
56 _NOT_SPECIFIED = ("Not", "Specified")
57
58 # Pattern to match a TypeError message from a call that
59 # failed because of incorrect number of arguments (see
60 # Python/getargs.c).
61 _INCORRECT_NUM_ARGS_RE = re.compile(
62     r"(takes [\w ]+ )(\d+)( arguments? \()(\d+)( given\))")
63
64
65
66 #---- exceptions
67
68 class CmdlnError(Exception):
69     """A cmdln.py usage error."""
70     def __init__(self, msg):
71         self.msg = msg
72     def __str__(self):
73         return self.msg
74
75 class CmdlnUserError(Exception):
76     """An error by a user of a cmdln-based tool/shell."""
77     pass
78
79
80
81 #---- public methods and classes
82
83 def alias(*aliases):
84     """Decorator to add aliases for Cmdln.do_* command handlers.
85     
86     Example:
87         class MyShell(cmdln.Cmdln):
88             @cmdln.alias("!", "sh")
89             def do_shell(self, argv):
90                 #...implement 'shell' command
91     """
92     def decorate(f):
93         if not hasattr(f, "aliases"):
94             f.aliases = []
95         f.aliases += aliases
96         return f
97     return decorate
98
99
100 class RawCmdln(cmd.Cmd):
101     """An improved (on cmd.Cmd) framework for building multi-subcommand
102     scripts (think "svn" & "cvs") and simple shells (think "pdb" and
103     "gdb").
104
105     A simple example:
106
107         import cmdln
108
109         class MySVN(cmdln.RawCmdln):
110             name = "svn"
111
112             @cmdln.aliases('stat', 'st')
113             def do_status(self, argv):
114                 print "handle 'svn status' command"
115
116         if __name__ == "__main__":
117             shell = MySVN()
118             retval = shell.main()
119             sys.exit(retval)
120
121     See <http://trentm.com/projects/cmdln> for more information.
122     """
123     name = None      # if unset, defaults basename(sys.argv[0])
124     prompt = None    # if unset, defaults to self.name+"> "
125     version = None   # if set, default top-level options include --version
126
127     # Default messages for some 'help' command error cases.
128     # They are interpolated with one arg: the command.
129     nohelp = "no help on '%s'"
130     unknowncmd = "unknown command: '%s'"
131
132     helpindent = '' # string with which to indent help output
133
134     def __init__(self, completekey='tab', 
135                  stdin=None, stdout=None, stderr=None):
136         """Cmdln(completekey='tab', stdin=None, stdout=None, stderr=None)
137
138         The optional argument 'completekey' is the readline name of a
139         completion key; it defaults to the Tab key. If completekey is
140         not None and the readline module is available, command completion
141         is done automatically.
142         
143         The optional arguments 'stdin', 'stdout' and 'stderr' specify
144         alternate input, output and error output file objects; if not
145         specified, sys.* are used.
146         
147         If 'stdout' but not 'stderr' is specified, stdout is used for
148         error output. This is to provide least surprise for users used
149         to only the 'stdin' and 'stdout' options with cmd.Cmd.
150         """
151         import sys
152         if self.name is None:
153             self.name = os.path.basename(sys.argv[0])
154         if self.prompt is None:
155             self.prompt = self.name+"> "
156         self._name_str = self._str(self.name)
157         self._prompt_str = self._str(self.prompt)
158         if stdin is not None:
159             self.stdin = stdin
160         else:
161             self.stdin = sys.stdin
162         if stdout is not None:
163             self.stdout = stdout
164         else:
165             self.stdout = sys.stdout
166         if stderr is not None:
167             self.stderr = stderr
168         elif stdout is not None:
169             self.stderr = stdout
170         else:
171             self.stderr = sys.stderr
172         self.cmdqueue = []
173         self.completekey = completekey
174         self.cmdlooping = False
175
176     def get_optparser(self):
177         """Hook for subclasses to set the option parser for the
178         top-level command/shell.
179
180         This option parser is used retrieved and used by `.main()' to
181         handle top-level options.
182
183         The default implements a single '-h|--help' option. Sub-classes
184         can return None to have no options at the top-level. Typically
185         an instance of CmdlnOptionParser should be returned.
186         """
187         version = (self.version is not None 
188                     and "%s %s" % (self._name_str, self.version)
189                     or None)
190         return CmdlnOptionParser(self, version=version)
191
192     def postoptparse(self):
193         """Hook method executed just after `.main()' parses top-level
194         options.
195
196         When called `self.values' holds the results of the option parse.
197         """
198         pass
199
200     def main(self, argv=None, loop=LOOP_NEVER):
201         """A possible mainline handler for a script, like so:
202
203             import cmdln
204             class MyCmd(cmdln.Cmdln):
205                 name = "mycmd"
206                 ...
207             
208             if __name__ == "__main__":
209                 MyCmd().main()
210
211         By default this will use sys.argv to issue a single command to
212         'MyCmd', then exit. The 'loop' argument can be use to control
213         interactive shell behaviour.
214         
215         Arguments:
216             "argv" (optional, default sys.argv) is the command to run.
217                 It must be a sequence, where the first element is the
218                 command name and subsequent elements the args for that
219                 command.
220             "loop" (optional, default LOOP_NEVER) is a constant
221                 indicating if a command loop should be started (i.e. an
222                 interactive shell). Valid values (constants on this module):
223                     LOOP_ALWAYS     start loop and run "argv", if any
224                     LOOP_NEVER      run "argv" (or .emptyline()) and exit
225                     LOOP_IF_EMPTY   run "argv", if given, and exit;
226                                     otherwise, start loop
227         """
228         if argv is None:
229             import sys
230             argv = sys.argv
231         else:
232             argv = argv[:] # don't modify caller's list
233
234         self.optparser = self.get_optparser()
235         if self.optparser: # i.e. optparser=None means don't process for opts
236             try:
237                 self.options, args = self.optparser.parse_args(argv[1:])
238             except CmdlnUserError, ex:
239                 msg = "%s: %s\nTry '%s help' for info.\n"\
240                       % (self.name, ex, self.name)
241                 self.stderr.write(self._str(msg))
242                 self.stderr.flush()
243                 return 1
244             except StopOptionProcessing, ex:
245                 return 0
246         else:
247             self.options, args = None, argv[1:]
248         self.postoptparse()
249
250         if loop == LOOP_ALWAYS:
251             if args:
252                 self.cmdqueue.append(args)
253             return self.cmdloop()
254         elif loop == LOOP_NEVER:
255             if args:
256                 return self.cmd(args)
257             else:
258                 return self.emptyline()
259         elif loop == LOOP_IF_EMPTY:
260             if args:
261                 return self.cmd(args)
262             else:
263                 return self.cmdloop()
264
265     def cmd(self, argv):
266         """Run one command and exit.
267         
268             "argv" is the arglist for the command to run. argv[0] is the
269                 command to run. If argv is an empty list then the
270                 'emptyline' handler is run.
271
272         Returns the return value from the command handler.
273         """
274         assert (isinstance(argv, (list, tuple)), 
275                 "'argv' is not a sequence: %r" % argv)
276         retval = None
277         try:
278             argv = self.precmd(argv)
279             retval = self.onecmd(argv)
280             self.postcmd(argv)
281         except:
282             if not self.cmdexc(argv):
283                 raise
284             retval = 1
285         return retval
286
287     def _str(self, s):
288         """Safely convert the given str/unicode to a string for printing."""
289         try:
290             return str(s)
291         except UnicodeError:
292             #XXX What is the proper encoding to use here? 'utf-8' seems
293             #    to work better than "getdefaultencoding" (usually
294             #    'ascii'), on OS X at least.
295             #import sys
296             #return s.encode(sys.getdefaultencoding(), "replace")
297             return s.encode("utf-8", "replace")
298
299     def cmdloop(self, intro=None):
300         """Repeatedly issue a prompt, accept input, parse into an argv, and
301         dispatch (via .precmd(), .onecmd() and .postcmd()), passing them
302         the argv. In other words, start a shell.
303         
304             "intro" (optional) is a introductory message to print when
305                 starting the command loop. This overrides the class
306                 "intro" attribute, if any.
307         """
308         self.cmdlooping = True
309         self.preloop()
310         if intro is None:
311             intro = self.intro
312         if intro:
313             intro_str = self._str(intro)
314             self.stdout.write(intro_str+'\n')
315         self.stop = False
316         retval = None
317         while not self.stop:
318             if self.cmdqueue:
319                 argv = self.cmdqueue.pop(0)
320                 assert (isinstance(argv, (list, tuple)), 
321                         "item on 'cmdqueue' is not a sequence: %r" % argv)
322             else:
323                 if self.use_rawinput:
324                     try:
325                         line = raw_input(self._prompt_str)
326                     except EOFError:
327                         line = 'EOF'
328                 else:
329                     self.stdout.write(self._prompt_str)
330                     self.stdout.flush()
331                     line = self.stdin.readline()
332                     if not len(line):
333                         line = 'EOF'
334                     else:
335                         line = line[:-1] # chop '\n'
336                 argv = line2argv(line)
337             try:
338                 argv = self.precmd(argv)
339                 retval = self.onecmd(argv)
340                 self.postcmd(argv)
341             except:
342                 if not self.cmdexc(argv):
343                     raise
344                 retval = 1
345             self.lastretval = retval
346         self.postloop()
347         self.cmdlooping = False
348         return retval
349
350     def precmd(self, argv):
351         """Hook method executed just before the command argv is
352         interpreted, but after the input prompt is generated and issued.
353
354             "argv" is the cmd to run.
355             
356         Returns an argv to run (i.e. this method can modify the command
357         to run).
358         """
359         return argv
360
361     def postcmd(self, argv):
362         """Hook method executed just after a command dispatch is finished.
363         
364             "argv" is the command that was run.
365         """
366         pass
367
368     def cmdexc(self, argv):
369         """Called if an exception is raised in any of precmd(), onecmd(),
370         or postcmd(). If True is returned, the exception is deemed to have
371         been dealt with. Otherwise, the exception is re-raised.
372
373         The default implementation handles CmdlnUserError's, which
374         typically correspond to user error in calling commands (as
375         opposed to programmer error in the design of the script using
376         cmdln.py).
377         """
378         import sys
379         type, exc, traceback = sys.exc_info()
380         if isinstance(exc, CmdlnUserError):
381             msg = "%s %s: %s\nTry '%s help %s' for info.\n"\
382                   % (self.name, argv[0], exc, self.name, argv[0])
383             self.stderr.write(self._str(msg))
384             self.stderr.flush()
385             return True
386
387     def onecmd(self, argv):
388         if not argv:
389             return self.emptyline()
390         self.lastcmd = argv
391         cmdname = self._get_canonical_cmd_name(argv[0])
392         if cmdname:
393             handler = self._get_cmd_handler(cmdname)
394             if handler:
395                 return self._dispatch_cmd(handler, argv)
396         return self.default(argv)
397
398     def _dispatch_cmd(self, handler, argv):
399         return handler(argv)
400
401     def default(self, argv):
402         """Hook called to handle a command for which there is no handler.
403
404             "argv" is the command and arguments to run.
405         
406         The default implementation writes and error message to stderr
407         and returns an error exit status.
408
409         Returns a numeric command exit status.
410         """
411         errmsg = self._str(self.unknowncmd % (argv[0],))
412         if self.cmdlooping:
413             self.stderr.write(errmsg+"\n")
414         else:
415             self.stderr.write("%s: %s\nTry '%s help' for info.\n"
416                               % (self._name_str, errmsg, self._name_str))
417         self.stderr.flush()
418         return 1
419
420     def parseline(self, line):
421         # This is used by Cmd.complete (readline completer function) to
422         # massage the current line buffer before completion processing.
423         # We override to drop special '!' handling.
424         line = line.strip()
425         if not line:
426             return None, None, line
427         elif line[0] == '?':
428             line = 'help ' + line[1:]
429         i, n = 0, len(line)
430         while i < n and line[i] in self.identchars: i = i+1
431         cmd, arg = line[:i], line[i:].strip()
432         return cmd, arg, line
433
434     def helpdefault(self, cmd, known):
435         """Hook called to handle help on a command for which there is no
436         help handler.
437
438             "cmd" is the command name on which help was requested.
439             "known" is a boolean indicating if this command is known
440                 (i.e. if there is a handler for it).
441         
442         Returns a return code.
443         """
444         if known:
445             msg = self._str(self.nohelp % (cmd,))
446             if self.cmdlooping:
447                 self.stderr.write(msg + '\n')
448             else:
449                 self.stderr.write("%s: %s\n" % (self.name, msg))
450         else:
451             msg = self.unknowncmd % (cmd,)
452             if self.cmdlooping:
453                 self.stderr.write(msg + '\n')
454             else:
455                 self.stderr.write("%s: %s\n"
456                                   "Try '%s help' for info.\n"
457                                   % (self.name, msg, self.name))
458         self.stderr.flush()
459         return 1
460
461     def do_help(self, argv):
462         """${cmd_name}: give detailed help on a specific sub-command
463
464         usage:
465             ${name} help [SUBCOMMAND]
466         """
467         if len(argv) > 1: # asking for help on a particular command
468             doc = None
469             cmdname = self._get_canonical_cmd_name(argv[1]) or argv[1]
470             if not cmdname:
471                 return self.helpdefault(argv[1], False)
472             else:
473                 helpfunc = getattr(self, "help_"+cmdname, None)
474                 if helpfunc:
475                     doc = helpfunc()
476                 else:
477                     handler = self._get_cmd_handler(cmdname)
478                     if handler:
479                         doc = handler.__doc__
480                     if doc is None:
481                         return self.helpdefault(argv[1], handler != None)
482         else: # bare "help" command
483             doc = self.__class__.__doc__  # try class docstring
484             if doc is None:
485                 # Try to provide some reasonable useful default help.
486                 if self.cmdlooping: prefix = ""
487                 else:               prefix = self.name+' '
488                 doc = """usage:
489                     %sSUBCOMMAND [ARGS...]
490                     %shelp [SUBCOMMAND]
491
492                 ${option_list}
493                 ${command_list}
494                 ${help_list}
495                 """ % (prefix, prefix)
496             cmdname = None
497
498         if doc: # *do* have help content, massage and print that
499             doc = self._help_reindent(doc)
500             doc = self._help_preprocess(doc, cmdname)
501             doc = doc.rstrip() + '\n' # trim down trailing space
502             self.stdout.write(self._str(doc))
503             self.stdout.flush()
504     do_help.aliases = ["?"]
505
506     def _help_reindent(self, help, indent=None):
507         """Hook to re-indent help strings before writing to stdout.
508
509             "help" is the help content to re-indent
510             "indent" is a string with which to indent each line of the
511                 help content after normalizing. If unspecified or None
512                 then the default is use: the 'self.helpindent' class
513                 attribute. By default this is the empty string, i.e.
514                 no indentation.
515
516         By default, all common leading whitespace is removed and then
517         the lot is indented by 'self.helpindent'. When calculating the
518         common leading whitespace the first line is ignored -- hence
519         help content for Conan can be written as follows and have the
520         expected indentation:
521
522             def do_crush(self, ...):
523                 '''${cmd_name}: crush your enemies, see them driven before you...
524
525                 c.f. Conan the Barbarian'''
526         """
527         if indent is None:
528             indent = self.helpindent
529         lines = help.splitlines(0)
530         _dedentlines(lines, skip_first_line=True)
531         lines = [(indent+line).rstrip() for line in lines]
532         return '\n'.join(lines)
533
534     def _help_preprocess(self, help, cmdname):
535         """Hook to preprocess a help string before writing to stdout.
536
537             "help" is the help string to process.
538             "cmdname" is the canonical sub-command name for which help
539                 is being given, or None if the help is not specific to a
540                 command.
541
542         By default the following template variables are interpolated in
543         help content. (Note: these are similar to Python 2.4's
544         string.Template interpolation but not quite.)
545
546         ${name}
547             The tool's/shell's name, i.e. 'self.name'.
548         ${option_list}
549             A formatted table of options for this shell/tool.
550         ${command_list}
551             A formatted table of available sub-commands.
552         ${help_list}
553             A formatted table of additional help topics (i.e. 'help_*'
554             methods with no matching 'do_*' method).
555         ${cmd_name}
556             The name (and aliases) for this sub-command formatted as:
557             "NAME (ALIAS1, ALIAS2, ...)".
558         ${cmd_usage}
559             A formatted usage block inferred from the command function
560             signature.
561         ${cmd_option_list}
562             A formatted table of options for this sub-command. (This is
563             only available for commands using the optparse integration,
564             i.e.  using @cmdln.option decorators or manually setting the
565             'optparser' attribute on the 'do_*' method.)
566
567         Returns the processed help. 
568         """
569         preprocessors = {
570             "${name}":            self._help_preprocess_name,
571             "${option_list}":     self._help_preprocess_option_list,
572             "${command_list}":    self._help_preprocess_command_list,
573             "${help_list}":       self._help_preprocess_help_list,
574             "${cmd_name}":        self._help_preprocess_cmd_name,
575             "${cmd_usage}":       self._help_preprocess_cmd_usage,
576             "${cmd_option_list}": self._help_preprocess_cmd_option_list,
577         }
578
579         for marker, preprocessor in preprocessors.items():
580             if marker in help:
581                 help = preprocessor(help, cmdname)
582         return help
583
584     def _help_preprocess_name(self, help, cmdname=None):
585         return help.replace("${name}", self.name)
586
587     def _help_preprocess_option_list(self, help, cmdname=None):
588         marker = "${option_list}"
589         indent, indent_width = _get_indent(marker, help)
590         suffix = _get_trailing_whitespace(marker, help)
591
592         if self.optparser:
593             # Setup formatting options and format.
594             # - Indentation of 4 is better than optparse default of 2.
595             #   C.f. Damian Conway's discussion of this in Perl Best
596             #   Practices.
597             self.optparser.formatter.indent_increment = 4
598             self.optparser.formatter.current_indent = indent_width
599             block = self.optparser.format_option_help() + '\n'
600         else:
601             block = ""
602             
603         help = help.replace(indent+marker+suffix, block, 1)
604         return help
605
606
607     def _help_preprocess_command_list(self, help, cmdname=None):
608         marker = "${command_list}"
609         indent, indent_width = _get_indent(marker, help)
610         suffix = _get_trailing_whitespace(marker, help)
611
612         # Find any aliases for commands.
613         token2canonical = self._get_canonical_map()
614         aliases = {}
615         for token, cmdname in token2canonical.items():
616             if token == cmdname: continue
617             aliases.setdefault(cmdname, []).append(token)
618
619         # Get the list of (non-hidden) commands and their
620         # documentation, if any.
621         cmdnames = {} # use a dict to strip duplicates
622         for attr in self.get_names():
623             if attr.startswith("do_"):
624                 cmdnames[attr[3:]] = True
625         cmdnames = cmdnames.keys()
626         cmdnames.sort()
627         linedata = []
628         for cmdname in cmdnames:
629             if aliases.get(cmdname):
630                 a = aliases[cmdname]
631                 a.sort()
632                 cmdstr = "%s (%s)" % (cmdname, ", ".join(a))
633             else:
634                 cmdstr = cmdname
635             doc = None
636             try:
637                 helpfunc = getattr(self, 'help_'+cmdname)
638             except AttributeError:
639                 handler = self._get_cmd_handler(cmdname)
640                 if handler:
641                     doc = handler.__doc__
642             else:
643                 doc = helpfunc()
644                 
645             # Strip "${cmd_name}: " from the start of a command's doc. Best
646             # practice dictates that command help strings begin with this, but
647             # it isn't at all wanted for the command list.
648             to_strip = "${cmd_name}:"
649             if doc and doc.startswith(to_strip):
650                 #log.debug("stripping %r from start of %s's help string",
651                 #          to_strip, cmdname)
652                 doc = doc[len(to_strip):].lstrip()
653             linedata.append( (cmdstr, doc) )
654
655         if linedata:
656             subindent = indent + ' '*4
657             lines = _format_linedata(linedata, subindent, indent_width+4)
658             block = indent + "commands:\n" \
659                     + '\n'.join(lines) + "\n\n"
660             help = help.replace(indent+marker+suffix, block, 1)
661         return help
662
663     def _help_preprocess_help_list(self, help, cmdname=None):
664         marker = "${help_list}"
665         indent, indent_width = _get_indent(marker, help)
666         suffix = _get_trailing_whitespace(marker, help)
667
668         # Determine the additional help topics, if any.
669         helpnames = {}
670         token2cmdname = self._get_canonical_map()
671         for attr in self.get_names():
672             if not attr.startswith("help_"): continue
673             helpname = attr[5:]
674             if helpname not in token2cmdname:
675                 helpnames[helpname] = True
676
677         if helpnames:
678             helpnames = helpnames.keys()
679             helpnames.sort()
680             linedata = [(self.name+" help "+n, "") for n in helpnames]
681
682             subindent = indent + ' '*4
683             lines = _format_linedata(linedata, subindent, indent_width+4)
684             block = indent + "additional help topics:\n" \
685                     + '\n'.join(lines) + "\n\n"
686         else:
687             block = ''
688         help = help.replace(indent+marker+suffix, block, 1)
689         return help
690
691     def _help_preprocess_cmd_name(self, help, cmdname=None):
692         marker = "${cmd_name}"
693         handler = self._get_cmd_handler(cmdname)
694         if not handler:
695             raise CmdlnError("cannot preprocess '%s' into help string: "
696                              "could not find command handler for %r" 
697                              % (marker, cmdname))
698         s = cmdname
699         if hasattr(handler, "aliases"):
700             s += " (%s)" % (", ".join(handler.aliases))
701         help = help.replace(marker, s)
702         return help
703
704     #TODO: this only makes sense as part of the Cmdln class.
705     #      Add hooks to add help preprocessing template vars and put
706     #      this one on that class.
707     def _help_preprocess_cmd_usage(self, help, cmdname=None):
708         marker = "${cmd_usage}"
709         handler = self._get_cmd_handler(cmdname)
710         if not handler:
711             raise CmdlnError("cannot preprocess '%s' into help string: "
712                              "could not find command handler for %r" 
713                              % (marker, cmdname))
714         indent, indent_width = _get_indent(marker, help)
715         suffix = _get_trailing_whitespace(marker, help)
716
717         # Extract the introspection bits we need.
718         func = handler.im_func
719         if func.func_defaults:
720             func_defaults = list(func.func_defaults)
721         else:
722             func_defaults = []
723         co_argcount = func.func_code.co_argcount
724         co_varnames = func.func_code.co_varnames
725         co_flags = func.func_code.co_flags
726         CO_FLAGS_ARGS = 4
727         CO_FLAGS_KWARGS = 8
728
729         # Adjust argcount for possible *args and **kwargs arguments.
730         argcount = co_argcount
731         if co_flags & CO_FLAGS_ARGS:   argcount += 1
732         if co_flags & CO_FLAGS_KWARGS: argcount += 1
733
734         # Determine the usage string.
735         usage = "%s %s" % (self.name, cmdname)
736         if argcount <= 2:   # handler ::= do_FOO(self, argv)
737             usage += " [ARGS...]"
738         elif argcount >= 3: # handler ::= do_FOO(self, subcmd, opts, ...)
739             argnames = list(co_varnames[3:argcount])
740             tail = ""
741             if co_flags & CO_FLAGS_KWARGS:
742                 name = argnames.pop(-1)
743                 import warnings
744                 # There is no generally accepted mechanism for passing
745                 # keyword arguments from the command line. Could
746                 # *perhaps* consider: arg=value arg2=value2 ...
747                 warnings.warn("argument '**%s' on '%s.%s' command "
748                               "handler will never get values" 
749                               % (name, self.__class__.__name__,
750                                  func.func_name))
751             if co_flags & CO_FLAGS_ARGS:
752                 name = argnames.pop(-1)
753                 tail = "[%s...]" % name.upper()
754             while func_defaults:
755                 func_defaults.pop(-1)
756                 name = argnames.pop(-1)
757                 tail = "[%s%s%s]" % (name.upper(), (tail and ' ' or ''), tail)
758             while argnames:
759                 name = argnames.pop(-1)
760                 tail = "%s %s" % (name.upper(), tail)
761             usage += ' ' + tail
762
763         block_lines = [
764             self.helpindent + "usage:",
765             self.helpindent + ' '*4 + usage
766         ]
767         block = '\n'.join(block_lines) + '\n\n'
768
769         help = help.replace(indent+marker+suffix, block, 1)
770         return help
771
772     #TODO: this only makes sense as part of the Cmdln class.
773     #      Add hooks to add help preprocessing template vars and put
774     #      this one on that class.
775     def _help_preprocess_cmd_option_list(self, help, cmdname=None):
776         marker = "${cmd_option_list}"
777         handler = self._get_cmd_handler(cmdname)
778         if not handler:
779             raise CmdlnError("cannot preprocess '%s' into help string: "
780                              "could not find command handler for %r" 
781                              % (marker, cmdname))
782         indent, indent_width = _get_indent(marker, help)
783         suffix = _get_trailing_whitespace(marker, help)
784         if hasattr(handler, "optparser"):
785             # Setup formatting options and format.
786             # - Indentation of 4 is better than optparse default of 2.
787             #   C.f. Damian Conway's discussion of this in Perl Best
788             #   Practices.
789             handler.optparser.formatter.indent_increment = 4
790             handler.optparser.formatter.current_indent = indent_width
791             block = handler.optparser.format_option_help() + '\n'
792         else:
793             block = ""
794
795         help = help.replace(indent+marker+suffix, block, 1)
796         return help
797
798     def _get_canonical_cmd_name(self, token):
799         map = self._get_canonical_map()
800         return map.get(token, None)
801
802     def _get_canonical_map(self):
803         """Return a mapping of available command names and aliases to
804         their canonical command name.
805         """
806         cacheattr = "_token2canonical"
807         if not hasattr(self, cacheattr):
808             # Get the list of commands and their aliases, if any.
809             token2canonical = {}
810             cmd2funcname = {} # use a dict to strip duplicates
811             for attr in self.get_names():
812                 if attr.startswith("do_"):    cmdname = attr[3:]
813                 elif attr.startswith("_do_"): cmdname = attr[4:]
814                 else:
815                     continue
816                 cmd2funcname[cmdname] = attr
817                 token2canonical[cmdname] = cmdname
818             for cmdname, funcname in cmd2funcname.items(): # add aliases
819                 func = getattr(self, funcname)
820                 aliases = getattr(func, "aliases", [])
821                 for alias in aliases:
822                     if alias in cmd2funcname:
823                         import warnings
824                         warnings.warn("'%s' alias for '%s' command conflicts "
825                                       "with '%s' handler"
826                                       % (alias, cmdname, cmd2funcname[alias]))
827                         continue
828                     token2canonical[alias] = cmdname
829             setattr(self, cacheattr, token2canonical)
830         return getattr(self, cacheattr)
831
832     def _get_cmd_handler(self, cmdname):
833         handler = None
834         try:
835             handler = getattr(self, 'do_' + cmdname)
836         except AttributeError:
837             try:
838                 # Private command handlers begin with "_do_".
839                 handler = getattr(self, '_do_' + cmdname)
840             except AttributeError:
841                 pass
842         return handler
843
844     def _do_EOF(self, argv):
845         # Default EOF handler
846         # Note: an actual EOF is redirected to this command.
847         #TODO: separate name for this. Currently it is available from
848         #      command-line. Is that okay?
849         self.stdout.write('\n')
850         self.stdout.flush()
851         self.stop = True
852
853     def emptyline(self):
854         # Different from cmd.Cmd: don't repeat the last command for an
855         # emptyline.
856         if self.cmdlooping:
857             pass
858         else:
859             return self.do_help(["help"])
860
861
862 #---- optparse.py extension to fix (IMO) some deficiencies
863 #
864 # See the class _OptionParserEx docstring for details.
865 #
866
867 class StopOptionProcessing(Exception):
868     """Indicate that option *and argument* processing should stop
869     cleanly. This is not an error condition. It is similar in spirit to
870     StopIteration. This is raised by _OptionParserEx's default "help"
871     and "version" option actions and can be raised by custom option
872     callbacks too.
873     
874     Hence the typical CmdlnOptionParser (a subclass of _OptionParserEx)
875     usage is:
876
877         parser = CmdlnOptionParser(mycmd)
878         parser.add_option("-f", "--force", dest="force")
879         ...
880         try:
881             opts, args = parser.parse_args()
882         except StopOptionProcessing:
883             # normal termination, "--help" was probably given
884             sys.exit(0)
885     """
886
887 class _OptionParserEx(optparse.OptionParser):
888     """An optparse.OptionParser that uses exceptions instead of sys.exit.
889
890     This class is an extension of optparse.OptionParser that differs
891     as follows:
892     - Correct (IMO) the default OptionParser error handling to never
893       sys.exit(). Instead OptParseError exceptions are passed through.
894     - Add the StopOptionProcessing exception (a la StopIteration) to
895       indicate normal termination of option processing.
896       See StopOptionProcessing's docstring for details.
897
898     I'd also like to see the following in the core optparse.py, perhaps
899     as a RawOptionParser which would serve as a base class for the more
900     generally used OptionParser (that works as current):
901     - Remove the implicit addition of the -h|--help and --version
902       options. They can get in the way (e.g. if want '-?' and '-V' for
903       these as well) and it is not hard to do:
904         optparser.add_option("-h", "--help", action="help")
905         optparser.add_option("--version", action="version")
906       These are good practices, just not valid defaults if they can
907       get in the way.
908     """
909     def error(self, msg):
910         raise optparse.OptParseError(msg)
911
912     def exit(self, status=0, msg=None):
913         if status == 0:
914             raise StopOptionProcessing(msg)
915         else:
916             #TODO: don't lose status info here
917             raise optparse.OptParseError(msg)
918
919
920
921 #---- optparse.py-based option processing support
922
923 class CmdlnOptionParser(_OptionParserEx):
924     """An optparse.OptionParser class more appropriate for top-level
925     Cmdln options. For parsing of sub-command options, see
926     SubCmdOptionParser.
927
928     Changes:
929     - disable_interspersed_args() by default, because a Cmdln instance
930       has sub-commands which may themselves have options.
931     - Redirect print_help() to the Cmdln.do_help() which is better
932       equiped to handle the "help" action.
933     - error() will raise a CmdlnUserError: OptionParse.error() is meant
934       to be called for user errors. Raising a well-known error here can
935       make error handling clearer.
936     - Also see the changes in _OptionParserEx.
937     """
938     def __init__(self, cmdln, **kwargs):
939         self.cmdln = cmdln
940         kwargs["prog"] = self.cmdln.name
941         _OptionParserEx.__init__(self, **kwargs)
942         self.disable_interspersed_args()
943
944     def print_help(self, file=None):
945         self.cmdln.onecmd(["help"])
946
947     def error(self, msg):
948         raise CmdlnUserError(msg)
949
950
951 class SubCmdOptionParser(_OptionParserEx):
952     def set_cmdln_info(self, cmdln, subcmd):
953         """Called by Cmdln to pass relevant info about itself needed
954         for print_help().
955         """
956         self.cmdln = cmdln
957         self.subcmd = subcmd
958
959     def print_help(self, file=None):
960         self.cmdln.onecmd(["help", self.subcmd])
961
962     def error(self, msg):
963         raise CmdlnUserError(msg)
964
965
966 def option(*args, **kwargs):
967     """Decorator to add an option to the optparser argument of a Cmdln
968     subcommand.
969     
970     Example:
971         class MyShell(cmdln.Cmdln):
972             @cmdln.option("-f", "--force", help="force removal")
973             def do_remove(self, subcmd, opts, *args):
974                 #...
975     """
976     #XXX Is there a possible optimization for many options to not have a
977     #    large stack depth here?
978     def decorate(f):
979         if not hasattr(f, "optparser"):
980             f.optparser = SubCmdOptionParser()
981         f.optparser.add_option(*args, **kwargs)
982         return f
983     return decorate
984
985
986 class Cmdln(RawCmdln):
987     """An improved (on cmd.Cmd) framework for building multi-subcommand
988     scripts (think "svn" & "cvs") and simple shells (think "pdb" and
989     "gdb").
990
991     A simple example:
992
993         import cmdln
994
995         class MySVN(cmdln.Cmdln):
996             name = "svn"
997
998             @cmdln.aliases('stat', 'st')
999             @cmdln.option('-v', '--verbose', action='store_true'
1000                           help='print verbose information')
1001             def do_status(self, subcmd, opts, *paths):
1002                 print "handle 'svn status' command"
1003
1004             #...
1005
1006         if __name__ == "__main__":
1007             shell = MySVN()
1008             retval = shell.main()
1009             sys.exit(retval)
1010
1011     'Cmdln' extends 'RawCmdln' by providing optparse option processing
1012     integration.  See this class' _dispatch_cmd() docstring and
1013     <http://trentm.com/projects/cmdln> for more information.
1014     """
1015     def _dispatch_cmd(self, handler, argv):
1016         """Introspect sub-command handler signature to determine how to
1017         dispatch the command. The raw handler provided by the base
1018         'RawCmdln' class is still supported:
1019
1020             def do_foo(self, argv):
1021                 # 'argv' is the vector of command line args, argv[0] is
1022                 # the command name itself (i.e. "foo" or an alias)
1023                 pass
1024
1025         In addition, if the handler has more than 2 arguments option
1026         processing is automatically done (using optparse):
1027
1028             @cmdln.option('-v', '--verbose', action='store_true')
1029             def do_bar(self, subcmd, opts, *args):
1030                 # subcmd = <"bar" or an alias>
1031                 # opts = <an optparse.Values instance>
1032                 if opts.verbose:
1033                     print "lots of debugging output..."
1034                 # args = <tuple of arguments>
1035                 for arg in args:
1036                     bar(arg)
1037
1038         TODO: explain that "*args" can be other signatures as well.
1039
1040         The `cmdln.option` decorator corresponds to an `add_option()`
1041         method call on an `optparse.OptionParser` instance.
1042
1043         You can declare a specific number of arguments:
1044
1045             @cmdln.option('-v', '--verbose', action='store_true')
1046             def do_bar2(self, subcmd, opts, bar_one, bar_two):
1047                 #...
1048
1049         and an appropriate error message will be raised/printed if the
1050         command is called with a different number of args.
1051         """
1052         co_argcount = handler.im_func.func_code.co_argcount
1053         if co_argcount == 2:   # handler ::= do_foo(self, argv)
1054             return handler(argv)
1055         elif co_argcount >= 3: # handler ::= do_foo(self, subcmd, opts, ...)
1056             try:
1057                 optparser = handler.optparser
1058             except AttributeError:
1059                 optparser = handler.im_func.optparser = SubCmdOptionParser()
1060             assert isinstance(optparser, SubCmdOptionParser)
1061             optparser.set_cmdln_info(self, argv[0])
1062             try:
1063                 opts, args = optparser.parse_args(argv[1:])
1064             except StopOptionProcessing:
1065                 #TODO: this doesn't really fly for a replacement of
1066                 #      optparse.py behaviour, does it?
1067                 return 0 # Normal command termination
1068
1069             try:
1070                 return handler(argv[0], opts, *args)
1071             except TypeError, ex:
1072                 # Some TypeError's are user errors:
1073                 #   do_foo() takes at least 4 arguments (3 given)
1074                 #   do_foo() takes at most 5 arguments (6 given)
1075                 #   do_foo() takes exactly 5 arguments (6 given)
1076                 # Raise CmdlnUserError for these with a suitably
1077                 # massaged error message.
1078                 import sys
1079                 tb = sys.exc_info()[2] # the traceback object
1080                 if tb.tb_next is not None:
1081                     # If the traceback is more than one level deep, then the
1082                     # TypeError do *not* happen on the "handler(...)" call
1083                     # above. In that we don't want to handle it specially
1084                     # here: it would falsely mask deeper code errors.
1085                     raise
1086                 msg = ex.args[0]
1087                 match = _INCORRECT_NUM_ARGS_RE.search(msg)
1088                 if match:
1089                     msg = list(match.groups())
1090                     msg[1] = int(msg[1]) - 3
1091                     if msg[1] == 1:
1092                         msg[2] = msg[2].replace("arguments", "argument")
1093                     msg[3] = int(msg[3]) - 3
1094                     msg = ''.join(map(str, msg))
1095                     raise CmdlnUserError(msg)
1096                 else:
1097                     raise
1098         else:
1099             raise CmdlnError("incorrect argcount for %s(): takes %d, must "
1100                              "take 2 for 'argv' signature or 3+ for 'opts' "
1101                              "signature" % (handler.__name__, co_argcount))
1102         
1103
1104
1105 #---- internal support functions
1106
1107 def _format_linedata(linedata, indent, indent_width):
1108     """Format specific linedata into a pleasant layout.
1109     
1110         "linedata" is a list of 2-tuples of the form:
1111             (<item-display-string>, <item-docstring>)
1112         "indent" is a string to use for one level of indentation
1113         "indent_width" is a number of columns by which the
1114             formatted data will be indented when printed.
1115
1116     The <item-display-string> column is held to 15 columns.
1117     """
1118     lines = []
1119     WIDTH = 78 - indent_width
1120     SPACING = 3
1121     MAX_NAME_WIDTH = 15
1122
1123     NAME_WIDTH = min(max([len(s) for s,d in linedata]), MAX_NAME_WIDTH)
1124     DOC_WIDTH = WIDTH - NAME_WIDTH - SPACING
1125     for namestr, doc in linedata:
1126         line = indent + namestr
1127         if len(namestr) <= NAME_WIDTH:
1128             line += ' ' * (NAME_WIDTH + SPACING - len(namestr))
1129         else:
1130             lines.append(line)
1131             line = indent + ' ' * (NAME_WIDTH + SPACING)
1132         line += _summarize_doc(doc, DOC_WIDTH)
1133         lines.append(line.rstrip())
1134     return lines
1135
1136 def _summarize_doc(doc, length=60):
1137     r"""Parse out a short one line summary from the given doclines.
1138     
1139         "doc" is the doc string to summarize.
1140         "length" is the max length for the summary
1141
1142     >>> _summarize_doc("this function does this")
1143     'this function does this'
1144     >>> _summarize_doc("this function does this", 10)
1145     'this fu...'
1146     >>> _summarize_doc("this function does this\nand that")
1147     'this function does this and that'
1148     >>> _summarize_doc("this function does this\n\nand that")
1149     'this function does this'
1150     """
1151     import re
1152     if doc is None:
1153         return ""
1154     assert length > 3, "length <= 3 is absurdly short for a doc summary"
1155     doclines = doc.strip().splitlines(0)
1156     if not doclines:
1157         return ""
1158
1159     summlines = []
1160     for i, line in enumerate(doclines):
1161         stripped = line.strip()
1162         if not stripped:
1163             break
1164         summlines.append(stripped)
1165         if len(''.join(summlines)) >= length:
1166             break
1167
1168     summary = ' '.join(summlines)
1169     if len(summary) > length:
1170         summary = summary[:length-3] + "..." 
1171     return summary
1172
1173
1174 def line2argv(line):
1175     r"""Parse the given line into an argument vector.
1176     
1177         "line" is the line of input to parse.
1178
1179     This may get niggly when dealing with quoting and escaping. The
1180     current state of this parsing may not be completely thorough/correct
1181     in this respect.
1182     
1183     >>> from cmdln import line2argv
1184     >>> line2argv("foo")
1185     ['foo']
1186     >>> line2argv("foo bar")
1187     ['foo', 'bar']
1188     >>> line2argv("foo bar ")
1189     ['foo', 'bar']
1190     >>> line2argv(" foo bar")
1191     ['foo', 'bar']
1192
1193     Quote handling:
1194     
1195     >>> line2argv("'foo bar'")
1196     ['foo bar']
1197     >>> line2argv('"foo bar"')
1198     ['foo bar']
1199     >>> line2argv(r'"foo\"bar"')
1200     ['foo"bar']
1201     >>> line2argv("'foo bar' spam")
1202     ['foo bar', 'spam']
1203     >>> line2argv("'foo 'bar spam")
1204     ['foo bar', 'spam']
1205     >>> line2argv("'foo")
1206     Traceback (most recent call last):
1207         ...
1208     ValueError: command line is not terminated: unfinished single-quoted segment
1209     >>> line2argv('"foo')
1210     Traceback (most recent call last):
1211         ...
1212     ValueError: command line is not terminated: unfinished double-quoted segment
1213     >>> line2argv('some\tsimple\ttests')
1214     ['some', 'simple', 'tests']
1215     >>> line2argv('a "more complex" test')
1216     ['a', 'more complex', 'test']
1217     >>> line2argv('a more="complex test of " quotes')
1218     ['a', 'more=complex test of ', 'quotes']
1219     >>> line2argv('a more" complex test of " quotes')
1220     ['a', 'more complex test of ', 'quotes']
1221     >>> line2argv('an "embedded \\"quote\\""')
1222     ['an', 'embedded "quote"']
1223     """
1224     import string
1225     line = line.strip()
1226     argv = []
1227     state = "default"
1228     arg = None  # the current argument being parsed
1229     i = -1
1230     while 1:
1231         i += 1
1232         if i >= len(line): break
1233         ch = line[i]
1234
1235         if ch == "\\": # escaped char always added to arg, regardless of state
1236             if arg is None: arg = ""
1237             i += 1
1238             arg += line[i]
1239             continue
1240
1241         if state == "single-quoted":
1242             if ch == "'":
1243                 state = "default"
1244             else:
1245                 arg += ch
1246         elif state == "double-quoted":
1247             if ch == '"':
1248                 state = "default"
1249             else:
1250                 arg += ch
1251         elif state == "default":
1252             if ch == '"':
1253                 if arg is None: arg = ""
1254                 state = "double-quoted"
1255             elif ch == "'":
1256                 if arg is None: arg = ""
1257                 state = "single-quoted"
1258             elif ch in string.whitespace:
1259                 if arg is not None:
1260                     argv.append(arg)
1261                 arg = None
1262             else:
1263                 if arg is None: arg = ""
1264                 arg += ch
1265     if arg is not None:
1266         argv.append(arg)
1267     if state != "default":
1268         raise ValueError("command line is not terminated: unfinished %s "
1269                          "segment" % state)
1270     return argv
1271
1272
1273 def argv2line(argv):
1274     r"""Put together the given argument vector into a command line.
1275     
1276         "argv" is the argument vector to process.
1277     
1278     >>> from cmdln import argv2line
1279     >>> argv2line(['foo'])
1280     'foo'
1281     >>> argv2line(['foo', 'bar'])
1282     'foo bar'
1283     >>> argv2line(['foo', 'bar baz'])
1284     'foo "bar baz"'
1285     >>> argv2line(['foo"bar'])
1286     'foo"bar'
1287     >>> print argv2line(['foo" bar'])
1288     'foo" bar'
1289     >>> print argv2line(["foo' bar"])
1290     "foo' bar"
1291     >>> argv2line(["foo'bar"])
1292     "foo'bar"
1293     """
1294     escapedArgs = []
1295     for arg in argv:
1296         if ' ' in arg and '"' not in arg:
1297             arg = '"'+arg+'"'
1298         elif ' ' in arg and "'" not in arg:
1299             arg = "'"+arg+"'"
1300         elif ' ' in arg:
1301             arg = arg.replace('"', r'\"')
1302             arg = '"'+arg+'"'
1303         escapedArgs.append(arg)
1304     return ' '.join(escapedArgs)
1305
1306
1307 # Recipe: dedent (0.1) in /Users/trentm/tm/recipes/cookbook
1308 def _dedentlines(lines, tabsize=8, skip_first_line=False):
1309     """_dedentlines(lines, tabsize=8, skip_first_line=False) -> dedented lines
1310     
1311         "lines" is a list of lines to dedent.
1312         "tabsize" is the tab width to use for indent width calculations.
1313         "skip_first_line" is a boolean indicating if the first line should
1314             be skipped for calculating the indent width and for dedenting.
1315             This is sometimes useful for docstrings and similar.
1316     
1317     Same as dedent() except operates on a sequence of lines. Note: the
1318     lines list is modified **in-place**.
1319     """
1320     DEBUG = False
1321     if DEBUG: 
1322         print "dedent: dedent(..., tabsize=%d, skip_first_line=%r)"\
1323               % (tabsize, skip_first_line)
1324     indents = []
1325     margin = None
1326     for i, line in enumerate(lines):
1327         if i == 0 and skip_first_line: continue
1328         indent = 0
1329         for ch in line:
1330             if ch == ' ':
1331                 indent += 1
1332             elif ch == '\t':
1333                 indent += tabsize - (indent % tabsize)
1334             elif ch in '\r\n':
1335                 continue # skip all-whitespace lines
1336             else:
1337                 break
1338         else:
1339             continue # skip all-whitespace lines
1340         if DEBUG: print "dedent: indent=%d: %r" % (indent, line)
1341         if margin is None:
1342             margin = indent
1343         else:
1344             margin = min(margin, indent)
1345     if DEBUG: print "dedent: margin=%r" % margin
1346
1347     if margin is not None and margin > 0:
1348         for i, line in enumerate(lines):
1349             if i == 0 and skip_first_line: continue
1350             removed = 0
1351             for j, ch in enumerate(line):
1352                 if ch == ' ':
1353                     removed += 1
1354                 elif ch == '\t':
1355                     removed += tabsize - (removed % tabsize)
1356                 elif ch in '\r\n':
1357                     if DEBUG: print "dedent: %r: EOL -> strip up to EOL" % line
1358                     lines[i] = lines[i][j:]
1359                     break
1360                 else:
1361                     raise ValueError("unexpected non-whitespace char %r in "
1362                                      "line %r while removing %d-space margin"
1363                                      % (ch, line, margin))
1364                 if DEBUG:
1365                     print "dedent: %r: %r -> removed %d/%d"\
1366                           % (line, ch, removed, margin)
1367                 if removed == margin:
1368                     lines[i] = lines[i][j+1:]
1369                     break
1370                 elif removed > margin:
1371                     lines[i] = ' '*(removed-margin) + lines[i][j+1:]
1372                     break
1373     return lines
1374
1375 def _dedent(text, tabsize=8, skip_first_line=False):
1376     """_dedent(text, tabsize=8, skip_first_line=False) -> dedented text
1377
1378         "text" is the text to dedent.
1379         "tabsize" is the tab width to use for indent width calculations.
1380         "skip_first_line" is a boolean indicating if the first line should
1381             be skipped for calculating the indent width and for dedenting.
1382             This is sometimes useful for docstrings and similar.
1383     
1384     textwrap.dedent(s), but don't expand tabs to spaces
1385     """
1386     lines = text.splitlines(1)
1387     _dedentlines(lines, tabsize=tabsize, skip_first_line=skip_first_line)
1388     return ''.join(lines)
1389
1390
1391 def _get_indent(marker, s, tab_width=8):
1392     """_get_indent(marker, s, tab_width=8) ->
1393         (<indentation-of-'marker'>, <indentation-width>)"""
1394     # Figure out how much the marker is indented.
1395     INDENT_CHARS = tuple(' \t')
1396     start = s.index(marker)
1397     i = start
1398     while i > 0:
1399         if s[i-1] not in INDENT_CHARS:
1400             break
1401         i -= 1
1402     indent = s[i:start]
1403     indent_width = 0
1404     for ch in indent:
1405         if ch == ' ':
1406             indent_width += 1
1407         elif ch == '\t':
1408             indent_width += tab_width - (indent_width % tab_width)
1409     return indent, indent_width
1410
1411 def _get_trailing_whitespace(marker, s):
1412     """Return the whitespace content trailing the given 'marker' in string 's',
1413     up to and including a newline.
1414     """
1415     suffix = ''
1416     start = s.index(marker) + len(marker)
1417     i = start
1418     while i < len(s):
1419         if s[i] in ' \t':
1420             suffix += s[i]
1421         elif s[i] in '\r\n':
1422             suffix += s[i]
1423             if s[i] == '\r' and i+1 < len(s) and s[i+1] == '\n':
1424                 suffix += s[i+1]
1425             break
1426         else:
1427             break
1428         i += 1
1429     return suffix
1430