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

#!/usr/bin/python
'''\nMailing Script
Usage: python script.py -y &lt;year&gt; -c &lt;type_user&gt;"
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__":
    target_year, campaign = parse_arguments(sys.argv)


 Presently I didn't learn 16.4. argparse. I will learn it in the next step.

I want to improve my script as follows:


All arguments are mandatory. Presently if the user misses some argument then the script raises an UnboundLocalError exception. I then catch it and call usage(). Is it a correct way? Should I use len(argv) in some ways to handle this?  
I check for correct values of arguments. If any argument value is wrong then I explicitly raise an exception and print an error message in the except clause. I am again not sure whether it's a preferable way to write this kind of code. I feel it adds one more level of nested blocks.


Some runs of this script:




$ python opt.py -y 2012 -c 80c
Year :  2012
campaign:  80c



Correct as I want:


$ python opt.py -year=2012 
Argument Error: Invalid year passed. 
One valid year is within (2008, 2013)



I think I should print message something like "insufficient arguments passed" or "use --year=2012 or -y 2012".
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 &lt;year&gt; -c &lt;type_user&gt;"
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)