7

I am trying to write a daemon service, which can be controlled in the command line. For example, to start the service

python3 test.py -c start -d /mydownloadfolder/ -j /myconfig.json

to stop the service,

python3 test.py -c stop

The -d -j parameters are required only when I start the service. Therefore, I need to implement conditionally required arguments based on the value of another argument.

I did some search and found this useful post Python Argparse conditionally required arguments The difference is: instead of checking the existence of '--command', I need to check the value of the '--command'.

Here is my tentative solution:

PARSER.add_argument('-c', '--command', required=True, help='provide a valid command: start, stop, restart, or status')
NEED_MORE_ARGS = PARSER.parse_args().command.lower().startswith('start')
PARSER.add_argument('-d', '--download', required=NEED_MORE_ARGS , default=LOCAL_DOWNLOAD, help='set account download folder')
PARSER.add_argument('-j', '--input',  required=NEED_MORE_ARGS, default=JSON_INPUT, help='set input json file')

I parsed the args in the middle to get NEED_MORE_ARGS(boolean), and then add other args. The code seems not clean. Is there a better way to do this?

==============

Updated: The tentative solution does not work. :(

Boyang
  • 93
  • 1
  • 6
  • I think the link pretty well covers the options. There's nothing built into `argparse` that does this kind of cross checking. I prefer the post-parsing testing option. But also consider - what kind of `help` do you want to provide? – hpaulj Aug 29 '18 at 22:34
  • 1
    I might add that if you don't need the '-c' flag, you could use the subparsers mechanism. 'stop' could then be a subparser without any added arguments, 'start' one with 2 required arguments. – hpaulj Aug 29 '18 at 22:43
  • 1
    In the link you mentioned the best answer talks about looking at sys.argv. That makes sense to me. Forget about argparse at first and just check for -c and stop or start in argv. Then use argparse based on what you found in sys.argv. Or just forget argparse and handle sys.argv yourself. Seems simpler to me. – Bobby Durrett Aug 29 '18 at 23:20

1 Answers1

6

I think you can use two parsers to do that:

import argparse

if __name__ == '__main__':
    command_parser = argparse.ArgumentParser()
    command_parser.add_argument('-c', '--command', required=True,
                                help='provide a valid command: start, stop, restart, or status')

    if command_parser.parse_known_args()[0].command.lower().startswith('start'):
        option_parser = argparse.ArgumentParser()
        option_parser.add_argument('-d', '--download', required=True, help='set account download folder')
        option_parser.add_argument('-j', '--input', required=True, help='set input json file')
        option_parser.parse_known_args()

or you can use a subparser, which is probably better in your case:

import argparse

if __name__ == '__main__':
    command_parser = argparse.ArgumentParser()

    subparsers = command_parser.add_subparsers(help='Choose a command')

    start_parser = subparsers.add_parser('start', help='"start" help')
    start_parser.add_argument('-d', '--download', required=True, help='set account download folder')
    start_parser.add_argument('-j', '--input', required=True, help='set input json file')
    start_parser.set_defaults(action=lambda: 'start')

    stop_parser = subparsers.add_parser('stop', help='"stop" help')
    stop_parser.set_defaults(action=lambda: 'stop')

    command_parser.parse_args()

in this case command line syntax will be bit different:

python3 test.py start -d /mydownloadfolder/ -j /myconfig.json

python3 test.py stop
Vader
  • 3,675
  • 23
  • 40