HiveBrain v1.2.0
Get Started
← Back to all entries
snippetpythonMinor

Using getopt parse and validate command line arguments

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
validateargumentslineparsegetoptusingandcommand

Problem

I am learning getopt in Python to parse and validate command line inputs:

```
#!/usr/bin/python
'''\nMailing Script
Usage: python script.py -y -c "
Arguments:
-y, --year any value from 2008 to 2013
-c, --campaign any value in:
'80c', '80d', 'allowances', 'late_filing',
'exempt_income', 'other_sources'
Flags:
-h help
'''
import sys, getopt
first_yr, last_yr = (2008, 2013)
campaign_type = (
'80c', '80d', 'allowances', 'late_filing',
'exempt_income', 'other_sources',
)

def usage():
print sys.exit(__doc__)

def parse_arguments(argv):
try:
opts, args = getopt.getopt(argv[1:], "y:c:", ["year=", "campaign="])
except getopt.GetoptError:
usage()
for opt, arg in opts:
if opt == '-h':
usage()
elif opt in ("-y", "--year"):
target_year = arg
# customized exception
try:
if not target_year in map(str, range(first_yr, last_yr+1)):
raise Exception()
except:
sys.exit("Argument Error: Invalid year passed. \n"
"One valid year is within (%s, %s)" % (first_yr, last_yr)
)
elif opt in ("-c", "--campaign"):
campaign = arg.lower()
try:
if not campaign in campaign_type:
raise Exception()
except Exception, e:
sys.exit("Argument Error: Invalid 'Campaign type' passed. \n"
"A valid value can be any of %s" % str(campaign_type)
)

# to avoid 'UnboundLocalError' Exception when one argument not passed
try:
target_year, campaign
except UnboundLocalError, u:
usage()

print 'Year : ', target_year
print 'campaign: ', campaign

# return argument values
return (target_year, campaign)

if __name__ == "__main__":

Solution

I think you have too many try/excepts. More than one of them raise and catch their own exceptions. I've made some changes that improves readability.

For clarity the new code created a new exception class and raises it when appropriate.

Also using getopt's own handler for bogus input. The else in the main loop is redundant but it can also serve as a fallback in case you make a mistake by allowing an arg but forget to process it.

Pay attention to the wording:
if target_year not in map( ...

reads better than:
if not target_year in map( ...

#!/usr/bin/python
'''\nMailing Script
Usage: python script.py -y  -c "
Arguments:
 -y, --year       any value from 2008 to 2013
 -c, --campaign   any value in:
                    '80c', '80d', 'allowances', 'late_filing', 
                    'exempt_income', 'other_sources'    
Flags:
 -h      help
'''     
import sys
import getopt
first_yr, last_yr = (2008, 2013)
campaign_type = (
    '80c', '80d', 'allowances', 'late_filing',
    'exempt_income', 'other_sources',
)

class ArgumentError(Exception):
    pass

def usage():
    print sys.exit(__doc__)

def parse_arguments(argv):
    target_year = None
    campaign = None

    try:
        opts, args = getopt.getopt(argv[1:], "y:c:", ["year=", "campaign="])
    except getopt.GetoptError as err:
        # print help information and exit:
        print str(err)  # will print something like "option -a not recognized"
        usage()

    for opt, arg in opts:
        if opt == '-h':
            usage()

        elif opt in ("-y", "--year"):
            target_year = arg
            if target_year not in map(str, range(first_yr, last_yr + 1)):
                raise ArgumentError("Argument Error: Invalid year passed. \n "
                                    "One valid year is within (%s, %s)" % (first_yr, last_yr))

        elif opt in ("-c", "--campaign"):
            campaign = arg.lower()
            if campaign not in campaign_type:
                raise ArgumentError("Argument Error: Invalid 'Campaign type' passed. "
                                    "A valid value can be any of %s" % str(campaign_type))
        else:
            raise ArgumentError("Bad argument: I don't know what %s is" % arg)

    if target_year is None or campaign is None:
        raise ArgumentError("You need to supply both -y and -c")

    print 'Year : ', target_year 
    print 'campaign: ', campaign 

    # return argument values
    return target_year, campaign

if __name__ == "__main__":
    target_year, campaign = parse_arguments(sys.argv)

Code Snippets

#!/usr/bin/python
'''\nMailing Script
Usage: python script.py -y <year> -c <type_user>"
Arguments:
 -y, --year       any value from 2008 to 2013
 -c, --campaign   any value in:
                    '80c', '80d', 'allowances', 'late_filing', 
                    'exempt_income', 'other_sources'    
Flags:
 -h      help
'''     
import sys
import getopt
first_yr, last_yr = (2008, 2013)
campaign_type = (
    '80c', '80d', 'allowances', 'late_filing',
    'exempt_income', 'other_sources',
)


class ArgumentError(Exception):
    pass


def usage():
    print sys.exit(__doc__)


def parse_arguments(argv):
    target_year = None
    campaign = None

    try:
        opts, args = getopt.getopt(argv[1:], "y:c:", ["year=", "campaign="])
    except getopt.GetoptError as err:
        # print help information and exit:
        print str(err)  # will print something like "option -a not recognized"
        usage()

    for opt, arg in opts:
        if opt == '-h':
            usage()

        elif opt in ("-y", "--year"):
            target_year = arg
            if target_year not in map(str, range(first_yr, last_yr + 1)):
                raise ArgumentError("Argument Error: Invalid year passed. \n "
                                    "One valid year is within (%s, %s)" % (first_yr, last_yr))

        elif opt in ("-c", "--campaign"):
            campaign = arg.lower()
            if campaign not in campaign_type:
                raise ArgumentError("Argument Error: Invalid 'Campaign type' passed. "
                                    "A valid value can be any of %s" % str(campaign_type))
        else:
            raise ArgumentError("Bad argument: I don't know what %s is" % arg)

    if target_year is None or campaign is None:
        raise ArgumentError("You need to supply both -y and -c")

    print 'Year : ', target_year 
    print 'campaign: ', campaign 

    # return argument values
    return target_year, campaign



if __name__ == "__main__":
    target_year, campaign = parse_arguments(sys.argv)

Context

StackExchange Code Review Q#37499, answer score: 6

Revisions (0)

No revisions yet.