Skip to content

Instantly share code, notes, and snippets.

@caruccio
Last active December 26, 2022 00:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save caruccio/8cb1da25213a02d513ff to your computer and use it in GitHub Desktop.
Save caruccio/8cb1da25213a02d513ff to your computer and use it in GitHub Desktop.
Really small python command line arg parser, now with parameter validation®
# -*- coding: utf-8 -*-
from __future__ import print_function
class MiniArgs(object):
__getattr__ = lambda *a, **ka: None
def __init__(self, argv, valid=None):
def normalize_name(name):
return name.lstrip('-').replace('-','').replace('+','')
def resolve_arg(arg):
if '=' in arg:
return arg.split('=', 1)
else:
return (arg, arg[0] == '-') ## '-arg'=True
self._arg0 = argv[0] if argv else None
self._args = argv[1:] if argv else []
if not self._args:
return
for name, value in [ resolve_arg(arg) for arg in self._args ]:
setattr(self, normalize_name(name), value)
self._valid = valid
if self._valid:
diff = set([ resolve_arg(arg)[0] for arg in self._args ]) - set(self._valid)
if diff:
raise ValueError('Invalid parameters: {}'.format(', '.join(diff)))
def __str__(self):
return str([ '{}={}'.format(name, value) for (name, value) in iter(self)])
def __iter__(self):
return iter([ (attr, getattr(self, attr)) for attr in dir(self) if attr[0] != '_' ])
if __name__ == '__main__':
import sys
if len(sys.argv) == 1:
print('Run this for an example:')
print(' $ python {} -v --dump --write-length=10 -read-timeout=2s +reverse'.format(sys.argv[0]))
sys.exit(1)
args = MiniArgs(sys.argv)
print('Original args:', sys.argv)
print('Supplied args:', args)
print()
print('Iterate over arguments as tuple(name, value):')
for i, arg in enumerate(args):
print(' > args[{}]={}'.format(i, arg))
print()
print('Access individual arguments as properties:')
print(' > args.v:'.ljust(17), str(args.v).ljust(8), '# without value, an arg is set to True')
print(' > args.dump:'.ljust(17), str(args.dump).ljust(8), '# --long-opt format is fine too')
print(' > args.write-length:'.ljust(17), str(args.writelength).ljust(8), '# --opt=value can be used to set a value')
print(' > args.read-timeout:'.ljust(17), str(args.readtimeout).ljust(8), '# values are either None, bool or str')
print(' > args.reverse:'.ljust(17), str(args.reverse).ljust(8), '# args prefixed with + are negated')
print(' > args.undefined:'.ljust(17), str(args.undefined).ljust(8), '# abscent args are None')
#######
print()
print('-'*10)
print('You can also define acceptable arguments:\n')
# Set `valid` for a list of acceptable parameters.
# Raises ValueError if unexpected parameter is supplied.
# This is ok:
valid = ['--debug', '--count']
supplied_ok = [ sys.argv[0], '--debug', '--count=2']
supplied_er = [ sys.argv[0], '--debug', '--invalid-arg', '--another-invalid-arg']
print('Valid args: {}'.format(', '.join(valid)))
print('Supplied args (OK): {}'.format(', '.join(supplied_ok[1:])))
ok = MiniArgs(supplied_ok, valid=valid)
print('Result is:')
for t in ok:
print(' >', t)
print()
print('And if we supply unexpected arguments, a ValueError() exception is raised:')
# This must fail:
print('Supplied args (Error): {}'.format(', '.join(supplied_er[1:])))
try:
MiniArgs(supplied_er, valid=valid)
except ValueError as ex:
print(' > {}: {}'.format(ex.__class__.__name__, ex))
@caruccio
Copy link
Author

It is now readable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment