Last active
December 26, 2022 00:22
-
-
Save caruccio/8cb1da25213a02d513ff to your computer and use it in GitHub Desktop.
Really small python command line arg parser, now with parameter validation®
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- 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)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
It is now readable.