pythonparsingcommand-line-argumentsargparsesubcommand

Command-line argument overwritten by default value when using subparser


I have a program with sub commands, but they all have common arguments (e.g. they all require input and output directories) which I included in the parent parser to avoid redundancies. However, I want each subcommand to have a different default value, but this causes the value provided in the command-line to be ignored.

MWE:

import argparse

top_parser = argparse.ArgumentParser()
top_parser.add_argument("--input-dir", type=str)

subparsers = top_parser.add_subparsers()

generate_parser = subparsers.add_parser("generate")
generate_parser.set_defaults(input_dir=".")

process_parser = subparsers.add_parser("process")
process_parser.set_defaults(input_dir="SOME_OTHER_DIR")

generate_args = top_parser.parse_args("--input-dir USE_THIS_DIR generate".split())
print("generate_args = ", generate_args)

process_args = top_parser.parse_args("--input-dir USE_THIS_DIR process".split())
print("process_args = ", process_args)

This gives:

generate_args =  Namespace(input_dir='.')
process_args =  Namespace(input_dir='SOME_OTHER_DIR')

but I want:

generate_args =  Namespace(input_dir='USE_THIS_DIR')
process_args =  Namespace(input_dir='USE_THIS_DIR')

I can circumvent this by separately adding the argument to each subparser, but I would like to avoid this redundancy if possible.


Solution

  • One workaround would be to check the value of input_dir after parsing, and substitute a subparser-specific default at that time.

    import argparse
    
    top_parser = argparse.ArgumentParser()
    top_parser.add_argument("--input-dir", type=str)
    
    subparsers = top_parser.add_subparsers()
    
    generate_parser = subparsers.add_parser("generate")
    generate_parser.set_defaults(alt_input_dir=".")
    
    process_parser = subparsers.add_parser("process")
    process_parser.set_defaults(alt_input_dir="SOME_OTHER_DIR")
    
    args = top_parser.parse_args()
    if args.input_dir is None:
        args.input_dir = args.alt_input_dir
    del args.alt_input_dir