4
\$\begingroup\$

I wrote a Python program that can take as argument either a string or a filename, with these options being mutually exclusive:

#!/usr/bin/env python

import argparse
import sys  

def parse_command_line():
    parser = argparse.ArgumentParser()
    parser.add_argument('-t', '--text', dest = 'input_text', help = 'Text')
    parser.add_argument('-f', '--file', dest = 'input_file', help = 'File containing the text')
    return parser


if __name__ == '__main__':
    parser = parse_command_line()
    args = parser.parse_args()

    if args.input_text is not None and args.input_file is None:
        text = args.input_text
    elif args.input_file is not None and args.input_text is None:
        text = open(args.input_file, 'r').read()
    else:
        parser.print_help()
        sys.exit()

    # Program continues ...

The mutually exclusive check is not optimal and will become very ugly in case of three, four, ... options. How can I improve this code?

\$\endgroup\$
5
  • \$\begingroup\$ Hi, we're a little different from SO by not answering specific questions. Could you also make your code one code block. Thanks. \$\endgroup\$
    – Peilonrayz
    Commented Oct 11, 2018 at 10:26
  • \$\begingroup\$ You might want to count the number of options used from the mutually-exclusive set; if the count is greater than one, then tell the user that only one of those arguments is permitted. I don't know argparse well enough to demonstrate that and write a proper answer, though. \$\endgroup\$ Commented Oct 11, 2018 at 10:38
  • 3
    \$\begingroup\$ Actually, looking at the docs, can't you simply use ArgumentParser.add_mutually_exclusive_group()? \$\endgroup\$ Commented Oct 11, 2018 at 10:43
  • 2
    \$\begingroup\$ @Peilonrayz I don't see anything wrong with this specific question. \$\endgroup\$ Commented Oct 11, 2018 at 11:04
  • 1
    \$\begingroup\$ @SimonForsberg I don't see how I said there was anything wrong with it. I guess reading back I should have said "we're a little different from SO by not being required to answer specific questions", or something along those lines. \$\endgroup\$
    – Peilonrayz
    Commented Oct 11, 2018 at 11:06

1 Answer 1

11
\$\begingroup\$

What you are looking for is add_mutually_exclusive_group. It allows you to define options that are mutually exclusive as well as specifying if selecting one of these options is a required action or not. And looking at your else: block, yours is; meaning if we call the program without arguments, it should fail with an error along the following:

usage: [-h] (-t TEXT | -f FILE)
program_name.py: error: one of the arguments -t/--text -f/--file is required

You are also handling files in your input and should avoid doing so manually. argparse provides a FileType object meant to be used as the type keyword of add_argument to handle files for you. It will fail with a proper error message if the file cannot be opened with the proper mode and will return the opened file object in the resulting parsed arguments.

Proposed improvements:

#!/usr/bin/env python

import argparse


def parse_command_line():
    parser = argparse.ArgumentParser()
    group = parser.add_mutually_exclusive_group(required=True)
    group.add_argument('-t', '--text', help='Text')
    group.add_argument('-f', '--file', type=argparse.FileType('r'), help='File containing the text')
    return parser


if __name__ == '__main__':
    args = parse_command_line().parse_args()

    if args.text is not None:
        text = args.text
    elif args.file is not None:
        with args.file as f:
            text = f.read()

    # Program continues ...

Also note the use of with to properly close the provided file.

\$\endgroup\$
0

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.