Skip to main content

Python ArgParse

Maybe it's my use of the getopt() function in C/C++ but in my Python scripts, I've always just made do with the native getopt package and it's served me well. Over time, people have encouraged me to look at using ArgParse but I've always been kind of resistant because it was a little mysterious to me. I finally sat down and figured out how to do basic things I do a lot in scripts using getopt and worked up a simple script using ArgParse to do the same thing. There are definitely some advantages to ArgParse!

Here's the gist with this sample:

#! /usr/bin/env python
"""
I've always been a fan of using the getopt module but am sometimes encouraged by folks to use argparse. It does seem to have
some advantages but it's always been a little cryptic to me too. So I came up with a sample for some common options/arguments
I often what to deal with and put in my own tools. It's still a little unnatural to me but it works.
With regard to mutually exclusive options, argparse gives you a way to do it but it does not allow the options to appear on the
same command line - an error is thrown when this happens. When I implement mutually exclusive options with getopt, I typically
only accept the last option and behave as though the other options were not specified at all. I would kind of prefer that behavior
but this isn't a bad compromise.
"""
import sys
import argparse
def see(expr):
value = eval(expr)
sys.stderr.write('{expr}: {value!r}\n'.format(**locals()))
parser = argparse.ArgumentParser(description='A sample use of ArgumentParser')
parser.add_argument('-v', '--verbose', dest='verbose', action='count', help='Toggle verbose debugging')
parser.add_argument('--cfg', dest='cfg', help='Specify configuration filename', required=True)
group = parser.add_mutually_exclusive_group()
group.add_argument('--opt1', dest='opt1', action='store_true', help='Option 1')
group.add_argument('--opt2', dest='opt2', action='store_true', help='Option 2')
parser.add_argument('pattern', help='Regular expression pattern')
parser.add_argument('files', metavar='file', nargs='*', help='Files to process')
args = parser.parse_args()
see('vars(args)')
# args.verbose is None if not specified - to test toggling, try `(args.verbose or 0) % 2`
see('args.verbose')
see('args.cfg')
see('args.pattern')
see('args.files')
see('args.opt1')
see('args.opt2')
parser.print_help(sys.stderr) # could be used when validating options/arguments
print 'Done'
I've made a few updates to the gist as I discover new features of which I want to take advantage:
  • Mutually-exclusive options
  • Script description
  • etc.

I will admit that since I've created this gist, I've been using ArgParse for new scripts. I can't whip out a script using ArgParse from memory yet (which I can do when I use getopt!) but it's becoming easier. I haven't gone back and retrofitted any existing scripts but who knows.

Building on ArgParse

After I wrote this post, I wrote a couple of other new ones which use ArgParse so the example is proven very useful to me:

Comments

Popular posts from this blog

Dynamic Python script loading

I have a bunch of toys and tools in a Git repository - I affectionately call this my toys repo . Most are just scripts that I use from a Unix (Cygwin or Git bash on Windoze) command line but there are some Python classes that I sometimes use in another script. Today at work, I was coming up with a new Python script that made use of a couple of my public classes. The script is good enough to share with my colleagues but I'm faced with the problem of my public classes: I imagine that most of my colleagues haven't even heard of my public classes and I don't expect them to download the entire repo just to get a couple of classes If I'm going to distribute the classes as separate files I introduce new problems: It could be confusing to have the files tag along. What is the user supposed to do with them? The answer is nothing - they should leave them alone and make sure they are in the same directory as the main script in case they decide to move th...

Git-based version information from Python script

I had this idea of generating version information for a Python script that uses ArgParse . The code is a little more than I was expecting but I think it works well. Here is the code: Usage Here is an example of its usage if the script is part of a git repository: $ ./version-example --version b92798, master, 2019-01-18 10:35:02, ['origin:https://github.com/pfuntner/gists.git'] $ It contains: The SHA1 of last git commit that changed the script The current branch of the repository The date of the commit - I think the timezone element is present in this but I didn't want to deal with timezones so I'm ignoring it A list of the remote repositories This is printed on two lines but that's something that ArgParse is doing, not me. Here is an example of its usage if the script is not part of a git repository - we don't have much information to work from but we can at least get the timestamp of the script: $ ~/tmp/version-example --version 2019-01-...

Extra Cygwin packages

Extra Cygwin packages I'm a big fan of Cygwin on Windoze to get the Unix/Linux look and feel. I can't stand the Windoze command prompt and their Power Shell is ridiculous. I don't want to get on another rant but I thought Power Shell was supposed to be their attempt at making Windoze more like Unix but it isn't. Yes, I have looked into WSL and it's ok but I'll stick with Cygwin. One of the things I don't like about Cygwin is that their default list of packages just isn't enough for me. After I install it on a new system, I often find myself adding additional packages one-at-a-time when I discover I need them. I finally decided to be a little proactive and keep track of packages I had to install. It looks like my beautiful formatting of this page has been trashed somehow and I apologize for that. I'll work on it. My extra packages Package Comments git Gotta have this! pip2 Yes, I'm still a proud Python2 guy! An...