3

Is there a way to produce the equivalent of const (that we can use with nargs='?', see reference question here for an example), but for nargs='*'. Meaning that I would want:

import argparse

argparser = argparse.ArgumentParser()
argparser.add_argument('--option', nargs='*', const=[1, 2, 3])
print(argparser.parse_args())

And then during usage:

my_script.py               #  Namespace(option=None)
my_script.py --option      #  Namespace(option=[1, 2, 3])
my_script.py --option 4 5  #  Namespace(option=[4, 5])

Currently I get ValueError: nargs must be '?' to supply const

5
  • 3
    Without the const the second case should produce option=[]. You could easily detect and replace that after parsing. Commented Jun 29, 2022 at 15:49
  • 1
    The error message you get comes from an explicit test in the store Action class "if const is not None and nargs != OPTIONAL:". In _get_values 'OPTIONAL' has a special step to handle const. Other nargs do not. _get_values is called before the Action's __call__. Commented Jun 29, 2022 at 15:55
  • @hpaulj Yes, but checking "empty but not None" feels a bit odd. const is more straightforward. Why is argparse not enabling const with *? It's just inconsistent... Commented Jan 14, 2024 at 18:56
  • @mara004, I used to follow the bug/issues religiously, and don't recall any suggesting expending the use of const. I don't know the thinking of the original developer, but my guess is the const` was included for use by 'store_const' and its sublclass 'store_true'.. Its use in the 3 way '?' is a bonus. If argparse doesn't do everything you want it's perfect ok (even encouraged) to write a custom action class, or to do your own post-parsing checking. Commented Jan 14, 2024 at 21:45
  • Part of the style issue with checking against [] is that a) we can't use default=[] anymore; b) We rely on the detail of nargs="*" producing a list, not a tuple. So you'd have to do if args.myarg is None: {default}; elif not args.myarg: {const}. Not very idiomatic IMO. Commented Jan 15, 2024 at 20:57

2 Answers 2

1

You can use a custom action to achieve this outcome:

import argparse

p = argparse.ArgumentParser()

class customAction(argparse.Action):
    """
    Customized argparse action, will set the
    value in the following way:

        1) If no option_string is supplied: set to None

        2) If option_string is supplied:

            2A) If values are supplied:
                set to list of values

            2B) If no values are supplied:
                set to default value (`self.const`)

    NOTES:
        If `const` is not set, default value (2A) will be None
    """
    def __call__(self, parser, namespace, values, option_string=None):
        if option_string:
            setattr(namespace, self.dest, self.const)
        elif not values:
            setattr(namespace, self.dest, None)
        else:
            setattr(namespace, self.dest, values)


p.add_argument('--option',
    nargs='*',
    action=customAction,
    dest='option',
    type=int,
    const=[1, 2, 3]
)
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! This was a little 'overkill' for the context of my work but it does work!
1

Simplification of @YaakovBressler's code:

class ConstWithMultiArgs (argparse.Action):
    def __call__(self, parser, namespace, values, option_string=None):
        obj = values if values else self.const if option_string else self.default
        setattr(namespace, self.dest, obj)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.