dHannasch <[email protected]> added the comment:
I've attached a file that can be run, but it's a simple script that I can
include here inline, too:
"""
Context:
I am trying to set up a cookiecutter so that newly-created packages will come
with a Jupyter notebook users can play with.
That is, python -m package_name jupyter would open up a Jupyter quickstart
notebook demonstrating the package's features.
argparse.REMAINDER as the first argument isn't important for a top-level
parser, since we can work around it by not using argparse at all,
but using argparse.REMAINDER in a subparser seems like a pretty straightforward
use case.
Any time we want to dispatch a subcommand to a separate tool --- forwarding all
following arguments --- we're going to need it.
"""
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('command', default='cmdname')
parser.add_argument('cmdname_args', nargs=argparse.REMAINDER)
args = parser.parse_args('cmdname --arg1 XX ZZ --foobar'.split())
if args != argparse.Namespace(cmdname_args=['--arg1', 'XX', 'ZZ', '--foobar'],
command='cmdname'):
raise Exception(args)
print('This is how argparse.REMAINDER works when there is an argument in
front.')
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
parser.add_argument('command', default='cmdname')
parser.add_argument('cmdname_args', nargs=argparse.REMAINDER)
args = parser.parse_args('--foo B cmdname --arg1 XX ZZ --foobar'.split())
if args != argparse.Namespace(cmdname_args=['--arg1', 'XX', 'ZZ', '--foobar'],
command='cmdname', foo='B'):
raise Exception(args)
print('This is how argparse.REMAINDER works there is an option in front.')
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
subparsers = parser.add_subparsers(dest='command')
commandParser = subparsers.add_parser('cmdname')
commandParser.add_argument('--filler-boundary-marker', dest='cmdname_args',
nargs=argparse.REMAINDER)
args = parser.parse_args('--foo B cmdname --filler-boundary-marker --arg1 XX ZZ
--foobar'.split())
if args != argparse.Namespace(cmdname_args=['--arg1', 'XX', 'ZZ', '--foobar'],
command='cmdname', foo='B'):
raise Exception(args)
print('This is how argparse.REMAINDER works with a visible "filler" name for
the list of arguments.')
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
subparsers = parser.add_subparsers(dest='command')
commandParser = subparsers.add_parser('cmdname')
commandParser.add_argument('--filler-boundary-marker', dest='cmdname_args',
nargs=argparse.REMAINDER)
args = parser.parse_args('cmdname --filler-boundary-marker --arg1 XX ZZ
--foobar --foo B'.split())
if args != argparse.Namespace(cmdname_args=['--arg1', 'XX', 'ZZ', '--foobar',
'--foo', 'B'], command='cmdname', foo=None):
raise Exception(args)
print("If an optional argument is provided after cmdname instead of before, it
will get interpreted as part of the argparse.REMAINDER instead of normally. And
that's great! We don't even need to be paranoid about other functions of our
command-line tool sharing arguments with the tool we want to wrap. Everything
will be forwarded.")
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
subparsers = parser.add_subparsers(dest='command')
commandParser = subparsers.add_parser('cmdname')
commandParser.add_argument('positional_arg')
commandParser.add_argument('cmdname_args', nargs=argparse.REMAINDER)
args = parser.parse_args('cmdname can_put_anything_here --arg1 XX ZZ --foobar
--foo B'.split())
if args != argparse.Namespace(cmdname_args=['--arg1', 'XX', 'ZZ', '--foobar',
'--foo', 'B'], command='cmdname', positional_arg='can_put_anything_here',
foo=None):
raise Exception(args)
print("If an optional argument is provided after cmdname instead of before, it
will get interpreted as part of the argparse.REMAINDER instead of normally. And
that's great! We don't even need to be paranoid about other functions of our
command-line tool sharing arguments with the tool we want to wrap. Everything
will be forwarded.")
"""
Note that this means we can fix the bug simply by,
whenever the cmdname subparser is invoked and the cmdname subparser uses
argparse.REMAINDER,
automatically adding an imaginary first positional argument to the subparser
and inserting that imaginary first positional argument into the stream before
parsing the arguments to cmdname.
https://github.com/python/cpython/blob/master/Lib/argparse.py#L1201
(Obviously it would be better to fix the underlying cause.)
"""
print('What we want to do is have a subparser that, in the case of one
particular selection, forwards all following arguments to another tool.')
print('script.py --foo B cmdname --arg1 XX ZZ --foobar should dispatch to
cmdname --arg1 XX ZZ --foobar.')
parser = argparse.ArgumentParser()
parser.add_argument('--foo')
subparsers = parser.add_subparsers(dest='command')
commandParser = subparsers.add_parser('cmdname')
commandParser.add_argument('cmdname_args', nargs=argparse.REMAINDER)
parser.parse_args('--foo B cmdname --arg1 XX ZZ --foobar'.split())
# error: unrecognized arguments: --arg1
----------
nosy: +dHannasch, rhettinger -aldencolerain, bethard, chris.jerdonek, danielsh,
paul.j3
versions: +Python 3.5, Python 3.6, Python 3.7
Added file: https://bugs.python.org/file48894/argparse_example.py
_______________________________________
Python tracker <[email protected]>
<https://bugs.python.org/issue17050>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe:
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com