Package: quickparse
QuickParseSimple command line argument parser for PythonExamplelist_things.py:fromquickparseimportQuickParsedeflist_things(a_list,quickparse):ifquickparse.numeric:ifisinstance(quickparse.numeric,tuple):print(', '.join(map(str,a_list[:quickparse.numeric[-1]])))else:print(', '.join(map(str,a_list[:quickparse.numeric])))else:print("How many items? Give a numeric value like '-3'")commands_config={'ls':list_things,'':lambda:print("Command is missing, use 'ls'"),}things=['apple','banana','blueberry','orange','pear','pineapple']QuickParse(commands_config).execute(things)Run it:$pythonlist_things.pyls-5
apple,banana,blueberry,orange,pearThe way it works:commands_configtells QuickParse to look forlsas a command and calllist_thingson it - when no commands show helpQuickParse parses arguments as normal whilelsis privileged as a commandQuickParse finds-5so it adds asquickparse.numeric = 5(quickparsebeing theQuickParseinstance that otherwise would come asquickparse = QuickParse(commands_config))QuickParse seeslist_thingsbeing associated tols, soquickparse.execute(things)calls it, passing on the arguments ofexecute(..)- one positional argument in this casesincelist_thingsexpects a named argumentquickparse, QuickParse makes sure it passes on the reference to its own instance ofquickparseif there are multiple numeric flags are given all are passed down withquickparse.numericin a tupleGNU Argument Syntax implementation with extensionsGNU Argument Syntax:https://www.gnu.org/software/libc/manual/html_node/Argument-Syntax.htmlExtensionsNumeric '-' values$my_cmd-12Numeric '+' values$my_cmd+12Long '-' options - only with explicit config$my_cmd-listBy default it becomes-l -i -s -t, but addingQuickParse(options_config = [ ('-list', ) ])will stop unpacking.Long '+' options by default$my_cmd+listEquivalent options - using options_config$my_cmd-lis equivalent to$my_cmd--listif addingQuickParse(options_config = [ ('-l', '--list') ])Command-subcommand hierarchy and function bindings - using commands_configDefining a random sample fromgitlooks like this:commands_config={'':do_show_help,'commit':do_commit,'log':do_log,'stash':{'':do_stash,'list':do_stash_list,}}options_config=[('-a','--all'),]QuickParse(commands_config,options_config).execute()Commands are called according to commands_config.That is$ git log -3callsdo_logdo_logmay look like this:defdo_log(quickparse):print(get_log_entries()[:quickparse.numeric])If there is a named argument indo_log's signature calledquickparse, the instance coming fromQuickParse(commands_config, options_config)is passed down holding all the results of parsing.Parsing happens by using the defaults and applying whatoptions_configadds to it.Argument FormatsArgument FormatExampleRemarks-<number>$ my_cmd -12(default)+<number>$ my_cmd +12(default)-<single_letter>$ my_cmd -x(default)+<single_letter>$ my_cmd +x(default)-<single_letter><value>$ my_cmd -nFoounpacking is the default: -n -F -ooptions_configneeds a type entry saying it expects a value (other than bool)+<single_letter><value>$ my_cmd +nFoounpacking is the default: +n +F +ooptions_configneeds a type entry saying it expects a value (other than bool)-<single_letter>=<value>$ my_cmd -n=Foo(default)+<single_letter>=<value>$ my_cmd +n=Foo(default)-<single_letter> <value>$ my_cmd -n Foooptions_configneeds a type entry saying it expects a value (other than bool)+<single_letter> <value>$ my_cmd +n Foooptions_configneeds a type entry saying it expects a value (other than bool)-<letters>$ my_cmd -abcunpacking is the default: -a -b -cif inoptions_configit's taken as-abc+<letters>$ my_cmd +abcunpacking is the default: +a +b +cif inoptions_configit's taken as+abc-<letters>=<value>$ my_cmd -name=Foo(default)+<letters>=<value>$ my_cmd +name=Foo(default)--<letters>$ my_cmd --list(default)--<letters>=<value>$ my_cmd --message=Bar(default)--<letters> <value>$ my_cmd --message Baroptions_configneeds a type entry saying it expects a value (other than bool)--$ my_cmd -- --param-anywayparameters delimiter(default)<letters>means [a-zA-Z] and '-'s not in the first placeAn argument like '-a*' gets unpacked if...'-a' is not defined to expect a valuethe '*' part has only letters, not '-' or '='How to change the interpretation of-swingIt can mean (default):-s -w -i -n -gor-s wing/-s=wingTo acheve the latter make the parser aware that '-s' expects astrvalue:options_config=[('-s',str),]Make the parser aware that an option expects a value after a spaceAdd type explicitly inoptions_config.For just getting as it is addstr.How to define option typesUse build-in types likeintorfloat, or create a callable that raises exceptions.Usingboolis a special case: parser will not expect a value but explicitly adds an error if one provided.How to add empty value to an option--option=Some commands support '-' as empty value likecurl -C - -O http://domanin.com/To avoid ambiguities this syntax is not supported.Use--option=instead.How to define optionsoptions_test.py:fromquickparseimportQuickParseoptions_config=[('-u','--utc','--universal'),('-l','--long'),('-n','--name',str),]quickparse=QuickParse(options_config=options_config)print(f'quickparse.options:{quickparse.options}')print(f'quickparse.errors:{quickparse.errors}')Run it:$pythonoptions_test.py
quickparse.options:{}quickparse.errors:{}$pythonoptions_test.py-u
quickparse.options:{'-u':True,'--utc':True,'--universal':True}quickparse.errors:{}$pythonoptions_test.py-ul
quickparse.options:{'-u':True,'--utc':True,'--universal':True,'-l':True,'--long':True}quickparse.errors:{}$pythonoptions_test.py-uln
quickparse.options:{'-u':True,'--utc':True,'--universal':True,'-l':True,'--long':True,'-n':True,'--name':True}quickparse.errors:{'-n':{'type':1,'message':"No value got for '-n/--name' - validator: str"},'--name':{'type':1,'message':"No value got for '-n/--name' - validator: str"}}$pythonoptions_test.py-ul-nthe_name
quickparse.options:{'-u':True,'--utc':True,'--universal':True,'-l':True,'--long':True,'-n':'the_name','--name':'the_name'}quickparse.errors:{}$pythonoptions_test.py-ul-nthe_name
quickparse.options:{'-u':True,'--utc':True,'--universal':True,'-l':True,'--long':True,'-n':'the_name','--name':'the_name'}quickparse.errors:{}$pythonoptions_test.py-ul-n=the_name
quickparse.options:{'-u':True,'--utc':True,'--universal':True,'-l':True,'--long':True,'-n':'the_name','--name':'the_name'}quickparse.errors:{}$pythonoptions_test.py-ul--namethe_name
quickparse.options:{'-u':True,'--utc':True,'--universal':True,'-l':True,'--long':True,'--name':'the_name','-n':'the_name'}quickparse.errors:{}$pythonoptions_test.py-ul--name=the_name
quickparse.options:{'-u':True,'--utc':True,'--universal':True,'-l':True,'--long':True,'--name':'the_name','-n':'the_name'}quickparse.errors:{}Test your command line argumentsquickparse_test_args.py(committed in the repo):frompprintimportpformatfromquickparseimportQuickParsedefdo_show_help():print("Executing 'do_show_help'...")defdo_commit():print("Executing 'do_commit'...")defdo_log(quickparse):print("Executing 'do_log'...")defdo_stash():print("Executing 'do_stash'...")defdo_stash_list():print("Executing 'do_stash_list'...")commands_config={'':do_show_help,'commit':do_commit,'log':do_log,'stash':{'':do_stash,'list':do_stash_list,}}options_config=[('-m','--message',str),('-p','--patch'),]quickparse=QuickParse(commands_config,options_config)print(f'Commands:\n{pformat(quickparse.commands)}')print(f'Parameters:\n{pformat(quickparse.parameters)}')print(f'Options:\n{pformat(quickparse.options)}')print(f'\'-\'numeric argument:\n{pformat(quickparse.numeric)}')print(f'\'+\'numeric argument:\n{pformat(quickparse.plusnumeric)}')print(f'Functions to call:\n{pformat(quickparse.to_execute)}')quickparse.execute()Error handlingIf the parser parameters 'commands_config' or 'options_config' are not valid, ValueError is rased from the underlying AssertionError.If the arguments are not compliant with the config (e.g. no value provided for an option that requires one) then no exceptions are raised but anerrorslist is populated on theQuickParseobject.See the error object again fromoptions_test.py$pythonoptions_test.py-uln
quickparse.options:{'-u':True,'--utc':True,'--universal':True,'-l':True,'--long':True,'-n':True,'--name':True}quickparse.errors:{'-n':{'type':1,'message':"No value got for '-n/--name' - validator: str"},'--name':{'type':1,'message':"No value got for '-n/--name' - validator: str"}}quickparse.errorsdict is about validation of options. These are the types:ERROR_TYPE_VALIDATION=0ERROR_VALUE_NOT_FOUND=1ERROR_INCOMPLETE_COMMAND=2quickparse.has_errors is also available to check if any errors occurred.ValidationWell, I still need to elaborate the docs on this but here is a quick example snippet.quickparse.validate({'parameters':{'mincount':1,},'options':{'mandatory':'--branch','optional':'--stage',},'numeric':{'maxcount':0},'plusnumeric':{'maxcount':0},})assert'parameters.mincount'notinquickparse.errors,f'Add a target'assertnotquickparse.has_errors,'\n'.join(quickparse.error_messages)