Skip to content

Commit

Permalink
Merge pull request #1 from MichalMazurek/master
Browse files Browse the repository at this point in the history
argparse instead of optparse
  • Loading branch information
icanpool authored Feb 13, 2021
2 parents e3cb9b2 + 256d5dd commit 5b2588e
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 86 deletions.
15 changes: 8 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

# Why cmdln.py?

`cmdln.py` fixes some of the design flaws in `cmd.py` and uses the
`optparse` module so that it is more useful (and convenient) for
`cmdln.py` fixes some of the design flaws in `cmd.py` and uses the
`argparse` module so that it is more useful (and convenient) for
implementing command-line scripts/shells.

The main differences are:
Expand All @@ -29,9 +29,11 @@ Install notes and intro docs are below. Please send any feedback to

# Python Versions

cmdln 2.x versions support Python 2.6, 2.7, 3.3 and 3.4.
cmdln 3.x versions support Python 2.7 and >=3.2 (uses argparse)

cmdln 1.x versions support Python 2.5, 2.6 and 2.7.
cmdln 2.x versions support Python 2.6, 2.7, 3.3 and 3.4. (uses optparse)

cmdln 1.x versions support Python 2.5, 2.6 and 2.7. (uses optparse)


# Install
Expand All @@ -55,7 +57,7 @@ interpreters". The idea (with both `cmd.py` and `cmdln.py`) is to be
able to quickly build multi-sub-command tools (think `cvs` or `svn`)
and/or simple interactive shells (think `gdb` or `pdb`). `cmdln.py`'s
extensions make it more natural to write sub-commands, integrate
[optparse](http://docs.python.org/lib/module-optparse.html) for simple
[argparse](https://docs.python.org/3/library/argparse.html) for simple
option processing, and make having good command documentation easier.

For example, here is most of the scaffolding for the [svn
Expand Down Expand Up @@ -120,7 +122,7 @@ control system).
-v, --verbose print extra information
-u, --show-updates display update information

(3) It makes parsing the command line easy (with `optparse`
(3) It makes parsing the command line easy (with `argparse`
integration):

$ python svn.py status -v foo bar baz
Expand Down Expand Up @@ -150,4 +152,3 @@ To attempt to run with all supported versions:
On a particular version:

make test34

54 changes: 36 additions & 18 deletions lib/cmdln.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def do_status(self, subcmd, opts, *paths):

from __future__ import print_function

__version_info__ = (2, 0, 1)
__version_info__ = (3, 0, 0)
__version__ = '.'.join(map(str, __version_info__))


Expand All @@ -47,6 +47,7 @@ def do_status(self, subcmd, opts, *paths):
import re
import cmd
import optparse
import argparse
from pprint import pprint
import sys
import datetime
Expand Down Expand Up @@ -187,7 +188,7 @@ def get_optparser(self):
version = (self.version is not None
and "%s %s" % (self._name_str, self.version)
or None)
return CmdlnOptionParser(self, version=version)
return CmdlnOptionParser(self, formatter_class=CmdlnFormatter)

def postoptparse(self):
"""Hook method executed just after `.main()' parses top-level
Expand Down Expand Up @@ -237,7 +238,7 @@ class MyCmd(cmdln.Cmdln):
self.optparser = self.get_optparser()
if self.optparser: # i.e. optparser=None means don't process for opts
try:
self.options, args = self.optparser.parse_args(argv[1:])
self.options, args = self.optparser.parse_known_args(argv[1:])
except CmdlnUserError as ex:
msg = "%s: %s\nTry '%s help' for info.\n"\
% (self.name, ex, self.name)
Expand Down Expand Up @@ -624,8 +625,8 @@ def _help_preprocess_option_list(self, help, cmdname=None):
# - Indentation of 4 is better than optparse default of 2.
# C.f. Damian Conway's discussion of this in Perl Best
# Practices.
self.optparser.formatter.indent_increment = 4
self.optparser.formatter.current_indent = indent_width
# self.optparser.formatter_class.indent_increment = 4
# self.optparser.formatter_class.current_indent = indent_width
block = self.optparser.format_option_help() + '\n'
else:
block = ""
Expand Down Expand Up @@ -834,8 +835,8 @@ def _help_preprocess_cmd_option_list(self, help, cmdname=None):
# - Indentation of 4 is better than optparse default of 2.
# C.f. Damian Conway's discussion of this in Perl Best
# Practices.
handler.optparser.formatter.indent_increment = 4
handler.optparser.formatter.current_indent = indent_width
# handler.optparser.formatter.indent_increment = 4
# handler.optparser.formatter.current_indent = indent_width
block = handler.optparser.format_option_help() + '\n'
else:
block = ""
Expand Down Expand Up @@ -938,18 +939,26 @@ class StopOptionProcessing(Exception):
sys.exit(0)
"""

class _OptionParserEx(optparse.OptionParser):
"""An optparse.OptionParser that uses exceptions instead of sys.exit.

This class is an extension of optparse.OptionParser that differs
class CmdlnFormatter(argparse.HelpFormatter):

def __init__(self, prog, indent_increment=4, max_help_position=24,
width=None):
super(CmdlnFormatter, self).__init__(prog, 4, 24, width)


class _OptionParserEx(argparse.ArgumentParser):
"""An argparse.OptionParser that uses exceptions instead of sys.exit.
This class is an extension of argparse.OptionParser that differs
as follows:
- Correct (IMO) the default OptionParser error handling to never
sys.exit(). Instead OptParseError exceptions are passed through.
- Add the StopOptionProcessing exception (a la StopIteration) to
indicate normal termination of option processing.
See StopOptionProcessing's docstring for details.
I'd also like to see the following in the core optparse.py, perhaps
I'd also like to see the following in the core argparse.py, perhaps
as a RawOptionParser which would serve as a base class for the more
generally used OptionParser (that works as current):
- Remove the implicit addition of the -h|--help and --version
Expand All @@ -961,15 +970,25 @@ class _OptionParserEx(optparse.OptionParser):
get in the way.
"""
def error(self, msg):
raise optparse.OptParseError(msg)
raise argparse.ArgumentError(msg)

def exit(self, status=0, msg=None):
if status == 0:
raise StopOptionProcessing(msg)
else:
#TODO: don't lose status info here
raise optparse.OptParseError(msg)
raise argparse.ArgumentError(msg)

def format_option_help(self):

formatter = self._get_formatter()
for action_group in self._action_groups:
formatter.start_section(action_group.title)
formatter.add_text(action_group.description)
formatter.add_arguments(action_group._group_actions)
formatter.end_section()

return formatter.format_help()


#---- optparse.py-based option processing support
Expand All @@ -992,8 +1011,7 @@ class CmdlnOptionParser(_OptionParserEx):
def __init__(self, cmdln, **kwargs):
self.cmdln = cmdln
kwargs["prog"] = self.cmdln.name
_OptionParserEx.__init__(self, **kwargs)
self.disable_interspersed_args()
super(CmdlnOptionParser, self).__init__(**kwargs)

def print_help(self, file=None):
self.cmdln.onecmd(["help"])
Expand Down Expand Up @@ -1031,8 +1049,8 @@ def do_remove(self, subcmd, opts, *args):
# large stack depth here?
def decorate(f):
if not hasattr(f, "optparser"):
f.optparser = SubCmdOptionParser()
f.optparser.add_option(*args, **kwargs)
f.optparser = SubCmdOptionParser(formatter_class=CmdlnFormatter)
f.optparser.add_argument(*args, **kwargs)
return f
return decorate

Expand Down Expand Up @@ -1114,7 +1132,7 @@ def do_bar2(self, subcmd, opts, bar_one, bar_two):
assert isinstance(optparser, SubCmdOptionParser)
optparser.set_cmdln_info(self, argv[0])
try:
opts, args = optparser.parse_args(argv[1:])
opts, args = optparser.parse_known_args(argv[1:])
except StopOptionProcessing:
#TODO: this doesn't really fly for a replacement of
# optparse.py behaviour, does it?
Expand Down
18 changes: 10 additions & 8 deletions test/cmdln_aliases.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
#!/usr/bin/env python

r"""
$ python cmdln_aliases.py foo
spawn python cmdln_aliases.py foo
hello from foo
$ python cmdln_aliases.py f
spawn python cmdln_aliases.py f
hello from foo
$ python cmdln_aliases.py !
spawn python cmdln_aliases.py !
hello from foo
$ python cmdln_aliases.py help
spawn python cmdln_aliases.py help
Usage:
cmdln_aliases.py COMMAND [ARGS...]
cmdln_aliases.py help [COMMAND]
<BLANKLINE>
Options:
usage: cmdln_aliases.py [-h]
optional arguments:
-h, --help show this help message and exit
<BLANKLINE>
Commands:
foo (!, f) shazam!
help (?) give detailed help on a specific sub-command
"""

import sys
Expand Down
24 changes: 12 additions & 12 deletions test/cmdln_aliases_deco.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
#!/usr/bin/env python

"""
$ python cmdln_aliases_deco.py foo
spawn python cmdln_aliases_deco.py foo
hello from foo
$ python cmdln_aliases_deco.py f
spawn python cmdln_aliases_deco.py f
hello from foo
$ python cmdln_aliases_deco.py !
spawn python cmdln_aliases_deco.py !
hello from foo
$ python cmdln_aliases_deco.py bar
spawn python cmdln_aliases_deco.py bar
hello from bar
$ python cmdln_aliases_deco.py ba
spawn python cmdln_aliases_deco.py ba
hello from bar
$ python cmdln_aliases_deco.py b
spawn python cmdln_aliases_deco.py b
hello from bar
$ python cmdln_aliases_deco.py help
spawn python cmdln_aliases_deco.py help
Usage:
cmdln_aliases_deco.py COMMAND [ARGS...]
cmdln_aliases_deco.py help [COMMAND]
<BLANKLINE>
Options:
usage: cmdln_aliases_deco.py [-h]
optional arguments:
-h, --help show this help message and exit
<BLANKLINE>
Commands:
bar (b, ba) whopee!
foo (!, f) shazam!
Expand Down
34 changes: 13 additions & 21 deletions test/cmdln_help2.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,41 @@
#!/usr/bin/env python

"""
$ python cmdln_help2.py help
spawn python cmdln_help2.py help
Usage:
cmdln_help2.py COMMAND [ARGS...]
cmdln_help2.py help [COMMAND]
<BLANKLINE>
Options:
optional arguments:
-h, --help show this help message and exit
<BLANKLINE>
Commands:
documented blah blah blah
hashelpfunc gobbledy gook
help (?) give detailed help on a specific sub-command
undocumented
$ python cmdln_help2.py help documented
spawn python cmdln_help2.py help documented
documented: blah blah blah
$ python cmdln_help2.py ? documented
spawn python cmdln_help2.py ? documented
documented: blah blah blah
$ python cmdln_help2.py help hashelpfunc
spawn python cmdln_help2.py help hashelpfunc
hashelpfunc: gobbledy gook
$ python cmdln_help2.py help undocumented
spawn python cmdln_help2.py help undocumented
cmdln_help2.py: I don't know how to use 'undocumented'
$ python cmdln_help2.py help undefined
spawn python cmdln_help2.py help undefined
cmdln_help2.py: wassup with this: 'undefined'
Try 'cmdln_help2.py help' for info.
$ python cmdln_help2.py #expecttest: INTERACTIVE, PROMPT="help-test> "
spawn python cmdln_help2.py
help-test> help
Usage:
COMMAND [ARGS...]
help [COMMAND]
<BLANKLINE>
Options:
optional arguments:
-h, --help show this help message and exit
<BLANKLINE>
Commands:
documented blah blah blah
hashelpfunc gobbledy gook
Expand Down
Loading

0 comments on commit 5b2588e

Please sign in to comment.