Skip to content

bpo-14074: argparse doesn't allow metavar to be a tuple #10847

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from

Conversation

cykerway
Copy link
Contributor

@cykerway cykerway commented Dec 1, 2018

Currently argparse allows nargs>1 for positional arguments but doesn't
allow metavar to be a tuple:

import argparse
parser = argparse.ArgumentParser()
parser.add_argument('foo', nargs=2, metavar=('bar', 'baz'))
args = parser.parse_args()

The above code will raise exception both with and without '--help'.

This patch fixed some functions about metavar processing so the above
code will pass.

https://bugs.python.org/issue14074

@the-knights-who-say-ni
Copy link

Hello, and thanks for your contribution!

I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA).

Unfortunately we couldn't find an account corresponding to your GitHub username on bugs.python.org (b.p.o) to verify you have signed the CLA (this might be simply due to a missing "GitHub Name" entry in your b.p.o account settings). This is necessary for legal reasons before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue.

You can check yourself to see if the CLA has been received.

Thanks again for your contribution, we look forward to reviewing it!

@embray
Copy link
Contributor

embray commented Apr 7, 2019

Thanks for the PR. I can confirm that this fixes this (very old) bug...

@csabella csabella requested a review from rhettinger January 28, 2020 02:28
@rhettinger rhettinger self-assigned this Jan 28, 2020
@rhettinger
Copy link
Contributor

Consider restricting this to the case where nargs is an integer. It doesn't make much sense with * or REMAINDER.

Lib/argparse.py Outdated
Comment on lines 729 to 737
if isinstance(argument.nargs, int) and \
isinstance(argument.metavar, tuple):
return ' '.join(argument.metavar)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CPython follows PEP8, so I believe the style is to prefer open parentheses over \.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There it is: c0204fd

Nothing else changed.

Currently argparse allows nargs>1 for positional arguments but doesn't
allow metavar to be a tuple:

    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('foo', nargs=2, metavar=('bar', 'baz'))
    args = parser.parse_args()

The above code will raise exception both with and without '--help'.

This patch fixed some functions about metavar processing so the above
code will pass.
@rhettinger rhettinger removed their request for review May 3, 2022 06:22
@rhettinger
Copy link
Contributor

For:

 parser.add_argument('foo', nargs=2, metavar=('bar', 'baz'))

Wouldn't it be better to specify two separate arguments?

 parser.add_argument('foo')
 parser.add_argument('baz')

Having two different meta variables implies that the arguments have different meanings. Collapsing them into a single argument seems like an anti-pattern to me. Both warrant their own error messages, possible type conversions, and possible default values.

@cykerway
Copy link
Contributor Author

cykerway commented May 12, 2022

Having two different meta variables implies that the arguments have different meanings. Collapsing them into a single argument seems like an anti-pattern to me. Both warrant their own error messages, possible type conversions, and possible default values.

That's the matter of nargs. nargs=N will not exist if each action consumes exactly one command-line argument.

nargs=N comes in handy when an argument is inherently a tuple. For example, RGB color components, monthly salaries. The tuple items often have the same type and just need a display name for each. The tuple items may never be used individually and probably do not worth a variable for each. nargs=N can create the tuple, saving application code.

If we assume nargs=N should exist, then having a tuple metavar looks better than repeating a string metavar.

Copy link
Member

@serhiy-storchaka serhiy-storchaka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wondered why TestHelpTupleMetavarPositional tests only argument with numerical value of nartgs, while TestHelpTupleMetavar tests with all nargs values. So I tried to change the test. It failed, but a simple change in _format_action_invocation() can fix this. I am nor sure that this is a best representation for nargs='*' and nargs='+', but this at least fixes failure.

The change in _get_action_name() is not tested. We need a test for it, and it should be generalized to support other nargs values.

Comment on lines +709 to +711
if isinstance(
argument.nargs, int) and isinstance(argument.metavar, tuple):
return ' '.join(argument.metavar)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change is not covered by tests.


parser_signature = Sig(prog='PROG')
argument_signatures = [
Sig('foo', help='foo help', nargs=2, metavar=('bar', 'baz'))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Sig('foo', help='foo help', nargs=2, metavar=('bar', 'baz'))
Sig('w', help='w help', nargs='+', metavar=('W1', 'W2')),
Sig('x', help='x help', nargs='*', metavar=('X1', 'X2')),
Sig('y', help='y help', nargs=3, metavar=('Y1', 'Y2', 'Y3')),
Sig('z', help='z help', nargs='?', metavar=('Z1', )),

]
argument_group_signatures = []
usage = '''\
usage: PROG [-h] bar baz
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
usage: PROG [-h] bar baz
usage: PROG [-h] W1 [W2 ...] [X1 [X2 ...]] Y1 Y2 Y3 [Z1]

help = usage + '''\

positional arguments:
bar baz foo help
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
bar baz foo help
W1 W2 w help
X1 X2 x help
Y1 Y2 Y3 y help
Z1 z help

Comment on lines +531 to 534
if isinstance(action.nargs, int):
return ' '.join(self._metavar_formatter(action, default)(1))
metavar, = self._metavar_formatter(action, default)(1)
return metavar
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if isinstance(action.nargs, int):
return ' '.join(self._metavar_formatter(action, default)(1))
metavar, = self._metavar_formatter(action, default)(1)
return metavar
return ' '.join(self._metavar_formatter(action, default)(1))

@serhiy-storchaka
Copy link
Member

Thank you for your contribution @cykerway and sorry for a lack of response.
#124782 generalizes this PR to support other nargs values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants