1

I realize the title might be confusing, but I didn't know how to word my problem.

My program's command line syntax looks like this: conv.py val from to, where to is optional, but I don't think that should matter.

I'm trying to add a flag that forces my program to ignore cached data and update its database. It should work like this:

conv.py -f val from to

but also like this:

conv.py -f

I know it should be possible to do this because the inbuilt -h flag in argparse works in a similar manner where you can say conv.py val from to or conv.py -h or conv.py -h val. However, I am at a loss as to how to achieve this. My current code just has the -f flag as an optional argument:

def parse_args():
    parser = argparse.ArgumentParser(prog='conv')
    parser.add_argument('-f', action='store_true')
    parser.add_argument('value')
    parser.add_argument('from_', metavar='from' )
    parser.add_argument('to', nargs='?', default='neg')
    args = parser.parse_args()
    return args.from_, args.to, args.value, args.f

I would like to make it so the presence of the -f flag is acceptable by itself or with all the other arguments. Any help is appreciated.

SvbZ3r0
  • 638
  • 4
  • 19
  • Putting those 3 arguments in a `nargs=3` `optional` (maybe with mutually_exclusive group) is one way around this. Or subparsers. Using the `-h` behavior for something other than displaying a message (`version` also does this) is probably not a good idea. It limits what you can do with this '-f'. – hpaulj Dec 17 '20 at 03:46

1 Answers1

2

To do that you would create a custom action which exits the parsing:

import argparse

class MyAction(argparse.Action):

    def do_the_thing(self):
        print("hello from my action")

    def __call__(self, parser, namespace, values, option_string=None):
        self.do_the_thing()
        parser.exit()

parser = argparse.ArgumentParser()
parser.add_argument('value')
parser.add_argument('from_', metavar='from' )
parser.add_argument('to', nargs='?', default='neg')
parser.add_argument("-f", nargs=0, action=MyAction)
args = parser.parse_args()
print("after args parsed")

Now if -f was passed, then print("after args parsed") will not be reached regardless of whether required arguments were sent or not. You may access the parser namespace from within the action instance.

wim
  • 338,267
  • 99
  • 616
  • 750
  • Ah thanks that helps. Would it be possible to have multiple such flags that are not mutually exclusive? Say I want to add another flag that says "purge the current cache" that works both alone and alongside the `-f` flag. Would that be possible? Not required for my current project, just curious. – SvbZ3r0 Dec 16 '20 at 19:32
  • The reason this works as you want is that it does its thing right away, and then exits. The first such flag is the one that acts. Normally we let the parser parser all arguments, and then act based on the values in the `args`. – hpaulj Dec 16 '20 at 20:29
  • Yes, I understand that. Which is why I was wondering if there's some way to have multiple such flags. Using a different method, perhaps? – SvbZ3r0 Dec 16 '20 at 21:55
  • I can't think of any way to do that, you already have two exiting actions ("--help" and "--version") and they can't both be used together. You may be bending the tool too far out of its intended usage pattern here.. – wim Dec 16 '20 at 22:18
  • Got it. Thanks. Was just curious. – SvbZ3r0 Dec 17 '20 at 03:51