Skip to content
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

Inconsistent validation of defaults in argparse #100949

Open
ton opened this issue Jan 11, 2023 · 3 comments
Open

Inconsistent validation of defaults in argparse #100949

ton opened this issue Jan 11, 2023 · 3 comments
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error

Comments

@ton
Copy link

ton commented Jan 11, 2023

Bug report

The following does not trigger an exception in ArgumentParser, where I would expect an exception since the default value for --choice is not in the list of choices:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--choice', choices=['a', 'b', 'c'], default='foo')

args = parser.parse_args()                    # args.choice == 'foo'

This is at least inconsistent behaviour, the following piece of code does trigger an exception for an invalid default value:

import argparse

parser = argparse.ArgumentParser()
parser.add_argument('--num', type=int, default='foo')

args = parser.parse_args(args=[])

Environment: Python 3.10.9, Arch Linux, x86_64.

@ton ton added the type-bug An unexpected behavior, bug, or error label Jan 11, 2023
@ton ton changed the title Inconsistent handling of validation of defaults in argparse Inconsistent validation of defaults in argparse Jan 11, 2023
@rudyardrichter
Copy link

If the first behavior is indeed a bug, and the error should be raised when parse_args is called, I think this tweak would fix that. Assuming it's a bug I'm not sure whether this, or moving the check into add_argument would be better practice. Either way happy to turn that into a PR

@hpaulj
Copy link

hpaulj commented Mar 8, 2023

choices is tested in _check_value, which is called by _get_values (after type is applied in _get_value) - this is done when processing the user provided value(s).

Defaults are put in the namespace at the start of parsing, and then at the end of parse_known_args it checks for defaults that haven't been overwritten by user inputs. The code is:

                # Convert action default now instead of doing it before
                # parsing arguments to avoid calling convert functions
                # twice (which may fail) if the argument was given, but
                # only if it was defined already in the namespace
                if (action.default is not None and
                    isinstance(action.default, str) and
                    hasattr(namespace, action.dest) and
                    action.default is getattr(namespace, action.dest)):
                    setattr(namespace, action.dest,
                            self._get_value(action, action.default))

This calls _get_value, which can apply the type, but it does not call _check_value.. So yes, the default can be something that isn't in the choices.

I don't think this is a problem. Some programmers might well want a default that isn't one of the choices. For example, that would make it easier to distinguish between the default and user input. If the default isn't among the choices, but you intended it to be, then you are being careless. You'll have plenty of opportunity to catch this problem during program testing.

@iritkatriel iritkatriel added the stdlib Python modules in the Lib dir label Nov 24, 2023
@henryruhs
Copy link

henryruhs commented Jan 6, 2024

I agree with the creator of the issue, it does not work like expected.

why not introduce parser.add_strict_argument that does internal validation or just value next to default ... value would act like the user input.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stdlib Python modules in the Lib dir type-bug An unexpected behavior, bug, or error
Projects
Status: Bugs
Development

No branches or pull requests

5 participants