dHannasch <daha...@sandia.gov> 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 <rep...@bugs.python.org>
<https://bugs.python.org/issue17050>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to