31

I'm writing a Python script to process a machine-readable file and output a human-readable report on the data contained within.
I would like to give the option of outputting the data to stdout (-s) (by default) or to a txt (-t) or csv (-c) file. I would like to have a switch for the default behaviour, as many commands do.

In terms of Usage:, I'd like to see something like script [-s | -c | -t] input file, and have -s be the default if no arguments are passed.

I currently have (for the relevant args, in brief):

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('-s', '--stdout', action='store_true')
group.add_argument('-c', '--csv', action='store_true')
group.add_argument('-t', '--txt', action='store_true')
args = parser.parse_args()

if not any((args.stdout, args.csv, args.txt)):
    args.stdout = True

So if none of -s, -t, or -c are set, stdout (-s) is forced to True, exactly as if -s had been passed.

Is there a better way to achieve this? Or would another approach entirely be generally considered 'better' for some reason?

Note: I'm using Python 3.5.1/2 and I'm not worried about compatibility with other versions, as there is no plan to share this script with others at this point. It's simply to make my life easier.

Alex B.
  • 412
  • 1
  • 4
  • 10
  • I know this doesn't work for _every_ case, but this sounds exactly like what `choices` option is for: `parser.add_argument('--output', default='stdout', choices=['stdout', 'csv', 'txt'])` – Mr. Llama Dec 01 '21 at 20:14

2 Answers2

54

You could have each of your actions update the same variable, supplying stdout as the default value for that variable.

Consider this program:

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument(
    '-s', '--stdout', action='store_const', dest='type', const='s', default='s')
group.add_argument(
    '-c', '--csv', action='store_const', dest='type', const='c')
group.add_argument(
    '-t', '--txt', action='store_const', dest='type', const='t')
args = parser.parse_args()
print args

Your code could look like:

if args.type == 's':
    ofile = sys.stdout
elif args.type == 'c':
    ofile = ...
...

First alternative:

Rather than arbitrarily choose one of the .add_argument()s to specify the default type, you can use parser.set_defaults() to specify the default type.

import argparse

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('-s', '--stdout', action='store_const', dest='type', const='s')
group.add_argument('-c', '--csv', action='store_const', dest='type', const='c')
group.add_argument('-t', '--txt', action='store_const', dest='type', const='t')
parser.set_defaults(type='s')
args = parser.parse_args()
print args

Second alternative:

Rather than specify the type as an enumerated value, you could store a callable into the type, and then invoke the callable:

import argparse

def do_stdout():
    # do everything that is required to support stdout
    print("stdout!")
    return
def do_csv():
    # do everything that is required to support CSV file
    print("csv!")
    return
def do_text():
    # do everything that is required to support TXT file
    print("text!")
    return

parser = argparse.ArgumentParser()
group = parser.add_mutually_exclusive_group()
group.add_argument('-s', '--stdout', action='store_const', dest='type', const=do_stdout)
group.add_argument('-c', '--csv', action='store_const', dest='type', const=do_csv)
group.add_argument('-t', '--txt', action='store_const', dest='type', const=do_text)
parser.set_defaults(type=do_stdout)
args = parser.parse_args()
print args
args.type()
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • The idea here is that each of the actions writes to the same variable. You could specify the default outside of the argument defintions too with: `if args.type is None or args.type =='s': ofile = sys.stdout` – theorifice Jul 21 '16 at 15:31
  • Thanks for the very detailed answer. – Alex B. Jul 21 '16 at 22:56
-2

You can "cheat" with sys.argv :

import sys


def main():
    if len(sys.argv) == 2 and sys.argv[1] not in ['-s', '-c', '-t', '-h']:
        filename = sys.argv[1]
        print "mode : stdout", filename
    else:
        parser = argparse.ArgumentParser()
        group = parser.add_mutually_exclusive_group()
        group.add_argument('-s', '--stdout')
        group.add_argument('-c', '--csv')
        group.add_argument('-t', '--txt')
        args = parser.parse_args()
        if args.stdout:
            print "mode stdout :",  args.stdout
        if args.csv:
            print "mode csv :",  args.csv
        if args.txt:
            print "mode txt :",  args.txt

if __name__ == "__main__":
    main()
pwnsauce
  • 416
  • 4
  • 14