Skip to content

gh-97848: argparse: Disallow misbehaving actions for positional arguments #98367

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 9 commits into from
7 changes: 7 additions & 0 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1540,6 +1540,13 @@ def _get_positional_kwargs(self, dest, **kwargs):
msg = _("'required' is an invalid argument for positionals")
raise TypeError(msg)

# make sure 'store_true', 'store_false', or 'store_const' action
Copy link
Member

Choose a reason for hiding this comment

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

This feels inelegant because we're hardcoding the names of individual actions. I'm not familiar enough with argparse to propose an alternative implementation though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@JelleZijlstra thank you for the review.

You say the change feels inelegant, because the names of individual actions are hardcoded. Do I understand correctly that you would prefer some named values or enumeration? Or did you mean something else?

I also am not a huge fan of using these strings directly, but this is the interface exposed by the ArgumentParser class and as such I think the names are committed to and won't change.

Anyway, I am open to all suggestions on how to improve this PR.

Copy link
Member

Choose a reason for hiding this comment

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

I think that more elegant and general solution would be

  • either add an optional boolean parameter for registry()
  • or add an optional class attribute in _StoreTrueAction etc.

It can be tested in add_argument(), after testing that the action is a callable.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, such flag already exists. You can check if nargs is 0 to detect actions that do not consume arguments.

# is not specified
action = kwargs.get('action')
if action in ('store_true', 'store_false', 'store_const'):
msg = _("{action} is an invalid action for positionals", {'action': action})
Copy link
Member

Choose a reason for hiding this comment

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

The argument of _() should be a string, not a tuple. I afraid that your tests are passed because a wrong TypeError is raised. You should test also the error message to be sure that this is the correct error.

Copy link
Member

Choose a reason for hiding this comment

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

And these error messages should not be internationalized, as they are programming errors, not user input errors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the review and comments. I will get back to this PR later this week.

Copy link
Member

Choose a reason for hiding this comment

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

Actually, I already created a PR with more general solution: #124839.

raise TypeError(msg)

# mark positional arguments as required if at least one is
# always required
if kwargs.get('nargs') not in [OPTIONAL, ZERO_OR_MORE]:
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -4714,6 +4714,15 @@ def test_parsers_action_missing_params(self):
def test_required_positional(self):
self.assertTypeError('foo', required=True)

def test_positionals_with_store_true_action(self):
self.assertTypeError('foo', action='store_true')

def test_positionals_with_store_false_action(self):
self.assertTypeError('foo', action='store_false')

def test_positionals_with_store_const_action(self):
self.assertTypeError('foo', action='store_const', const='bar')

def test_user_defined_action(self):

class Success(Exception):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Disallow ``store_true``, ``store_false``, and ``store_const`` actions for positional arguments in :mod:`argparse` module.
Loading