1

As described in the accepted answer to Most pythonic way of accepting arguments using optparse, I have a program with a function that performs operations on a string. The program uses argparse to check whether the string is provided as-is or in a file and massages the input as needed to pass it to the function.

Now I want to extend the program with a more advanced version of my function, but still leave the basic version in for comparison, somewhat as in Use argparse to run 1 of 2 functions in my script. Where I believe my situation differs is that regardless of the function that gets called, I want the option of also passing my existing input flags.

Just adding a new argument to the parser and nesting my previous code inside a if/else that checks for that flag doesn't work: it complains about the wrong number of arguments. I am aware of sub-commands, but I am still pretty new with argparse and it just seems like that would be overkill for what I want - but maybe not.

tl;dr: I need to choose one of two functions and one of two input types; both input types apply to both functions. Thanks for any help!

Edited to add code:

p = argparse.ArgumentParser(description="program.py")
p.add_argument("-e", dest='extended')   #The new flag causing the trouble
p.add_argument("-s", dest="string")
p.add_argument("-f", dest="infile")

args = p.parse_args()

if args.extended:
    if args.infile:
        with open(args.infile,'r') as f:
            for line in enumerate(f.readlines()):
                print 'Input: ', line[1],    
                output = funcExtended(line[1])  #new and improved function
                print 'Extended output: ', output
    elif args.string:
        output = funcExtended(args.string)
        print output
    else:  #my future default option to grab strings from a database
        print 'This will soon work: extended'
else:   #I fully realize that I shouldn't have to essentially copy and paste here
    if args.infile:
        with open(args.infile,'r') as f:
            for line in enumerate(f.readlines()):
                print 'Input: ', line[1],    
                output = funcBasic(line[1])  #old and tired function
                print 'Basic output: ', output
    elif args.string:
        output = funcBasic(args.string)
        print output
    else:   #my future default option to grab strings from a database
        print 'This will soon work: basic'

This is a command line utility. Issuing

$ python program.py -s 'string'

returns a properly formatted string, as before. But issuing

$ python program.py -s 'string' -e

returns

program.py: error: argument -e: expected one argument

Whew. Thanks again to anybody who can help!

Community
  • 1
  • 1
verbsintransit
  • 888
  • 3
  • 8
  • 18

1 Answers1

4

If you change your extended argument to a boolean flag with

p.add_argument("-e", dest='extended', action="store_true")

it will no longer expect an argument. You can then invoke your script with

$ python program.py -e -s 'string'

Finally as a bonus here are some ideas to make your code less redundant:

import argparse

def funcExtended(line):
   return " ".join(line)

def funcBasic(line):
    return line.upper()

p = argparse.ArgumentParser(description="program.py")
p.add_argument("-e", "--extended", dest="func", action="store_const", const=funcExtended, default=funcBasic)
p.add_argument("-s", "--string")
p.add_argument("-f", "--infile")

args = p.parse_args()

def readlines(args):
    if args.infile:
        with open(args.infile,'r') as f:
            for line in f:
                yield line.rstrip("\n")
    elif args.string:
        yield args.string
    else:  #my future default option to grab strings from a database
        print 'This will soon work: extended'

for line in readlines(args):
    print 'Input: ', line
    output = args.func(line)
    print "Output: ", output
  • Thank you! I had been messing around with actions, but I guess I have gotten too tired and didn't do the one you proposed. Your optimization code is appreciated too. I hadn't even considered just letting the string input get echoed to the "Input:"/"Output:" paradigm in addition to the batch input. – verbsintransit Sep 14 '12 at 07:51