Skip to content

Instantly share code, notes, and snippets.

@ekimekim
Created May 29, 2013 06:02
Show Gist options
  • Save ekimekim/5668250 to your computer and use it in GitHub Desktop.
Save ekimekim/5668250 to your computer and use it in GitHub Desktop.
A quick and dirty way of interpreting command line args and options for scripts. As an example, calling a script with: $ python script.py hello -x=123 --foobar world causes the script's main function to be called with the args: main('hello', 'world', x='123', foobar=True) with the function's docstring printed in case of TypeError.
from functools import wraps
import sys
def with_argv(fn):
"""Decorator that wraps your 'main' function, giving it some automatic behaviour.
The resulting function should not be passed any args.
Command line arguments are interpreted as follows:
The first argument is the program name, it is dropped (you can still access it with sys.argv[0]).
If an argument has form '--key=value', main's kwargs are updated
with {key: value}
If an argument has form '--flag', main's kwargs are updated
with {flag: True}
Short options (with a single '-') are treated similarly.
Note that '-xyz=blah' produces the kwargs:
{'x': True, 'y': True, 'z': 'blah'}
Remaining arguments are passed to main as *args.
If main raises a TypeError, it is suppressed and main's docstring is printed
along with str() of the TypeError.
Any other exception is not suppressed and will produce a normal traceback.
If main returns None, exit(0) is called.
Otherwise, exit(return value) is called.
"""
@wraps(fn)
def _with_argv():
args = []
kwargs = {}
argv = sys.argv[1:][::-1]
while argv:
arg = argv.pop()
if arg.startswith('--'):
arg = arg[2:]
elif arg.startswith('-'):
if len(arg) != 2 and arg[2] != '=': # rule out cases '-x' and '-x=value'
argv.append('-' + arg[2:]) # thus '-xyz' becomes '-yz'
arg = arg[1]
else:
arg = arg[1:]
else:
args.append(arg)
continue
# We have a flag or key=value
if '=' in arg:
k, v = arg.split('=', 1)
else:
k, v = arg, True
kwargs[k] = v
try:
ret = fn(*args, **kwargs)
except TypeError, e:
print str(e)
if fn.__doc__: print fn.__doc__
ret = 255
sys.exit(0 if ret is None else ret)
return _with_argv
if __name__=='__main__':
# for examples
@with_argv
def main(*args, **kwargs):
print 'args:', args
print 'kwargs:', kwargs
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment