Package: parse-this
parse_thisMakes it easy to parse command line arguments for any function, method or classmethod.You just finished writing an awesome piece of code and now comes the boring part: adding the command line parsing to
actually use it ...So now you need to use the awesome, but very verbose,argparsemodule. For each argument of your entry point method
you need to add a name, a help message and/or a default value. But wait... Your parameters are correctly named, right!?
They also have type hinting, right!? And you have an awesome docstring for that method. There is probably a way of
creating theArgumentParsereasily right?Yes and it's calledparse_this!Usageparse_thiscontains a simple way to create a command line interface from an entire class. For that you will need to
use theparse_classclass decorator.# script.pyfromparse_thisimportcreate_parser,parse_class@parse_class()classParseMePlease(object):"""This will be the description of the parser."""@create_parser()def__init__(self,foo:int,ham:int=1):"""Get ready to be parsed!Args:foo: because naming stuff is hardham: ham is good and it defaults to 1"""self._foo=fooself._ham=ham@create_parser()defdo_stuff(self,bar:int,spam:int=1):"""Can do incredible stuff with bar and spam.Args:bar: as in foobar, will be multiplied with everything elsespam: goes well with eggs, spam, bacon, spam, sausage and spamReturns:Everything multiplied with each others"""returnself._foo*self._ham*bar*spamif__name__=="__main__":print(ParseMePlease.parser.call())pythonscript.py--help# Print a comprehensive help and usage messagepythonscript.py2do-stuff2>>>4pythonscript.py2--ham2do-stuff2--spam2>>>16How does it workTL;DR version?You need to decorate the methods you want to be usable from the command line  usingcreate_parser.The__init__method arguments and keyword arguments will be the arguments and options of the script command linei.e.the first arguments and optionsThe other methods will be transformed into sub-command, again mapping the command line arguments and options to the method's own argumentsAll you have to do for this to work is:Decorate your class withparse_classDecorate methods withcreate_parserDocument your class and method with properly formed docstring to get help and usage messageAnnotate all parameters with their typeCall<YourClass>.parser.call()and you are done!If you feel like you may need more customization and details, please read on!If the__init__method is decorated it will be considered the first, or top-level, parser this means that all
arguments in your__init__will be arguments pass right after invoking you script
i.e.python script.py init_arg_1 init_arg_2 etc...The description of the top-level parser is taken from the class's docstring or overwritten by the keyword
argumentdescriptionofparse_class.Each method decorated withcreate_parserwill become a subparser of its own.The command name of the subparser is the same as the method name with_replaced by-.'Private' methods, whose name start with an_, do not have a subparser by default, as this would expose them to the
outside. However if you want to expose them you can set the keyword argumentparse_private=Trueinparse_class. If
exposed their command name will not contain the leading-as this would be confusing for command parsing. Special
methods, such as__str__, can be decorated as well. Their command name will be stripped of all_s resulting in
command names such asstr.When used in aparse_classdecorated classcreate_parsercan take an extra parametersnamethat will be used as
the sub-command name. The same modifications are made to thenamereplacing_with-When callingpython script.py --helpthe help message foreveryparser will be displayed making easier to find
what you are looking forArguments and typesBothparse_thisandcreate_parserneed parameters to have type annotations. Any Python builtin type can be used.
There is no need to provide a type for keyword arguments since it is inferred from the default value of the argument. If
your method signature containsarg_with_default=12parse_thisexpect anintwherearg_with_defaultis on the
command line.If this is the content ofparse_me.py:fromparse_thisimportcreate_parserclassINeedParsing(object):"""A class that clearly needs argument parsing!"""def__init__(self,an_argument):self._an_arg=an_argument@create_parser(delimiter_chars="--")defparse_me_if_you_can(self,an_int:int,a_string:str,an_other_int:int=12):"""I dare you to parse me !!!Args:an_int -- int are pretty coola_string -- string aren't that nicean_other_int -- guess what? I got a default value"""returna_string*an_int,an_other_int*self._an_argif__name__=="__main__":need_parsing=INeedParsing(2)print(INeedParsing.parse_me_if_you_can.parser.call(need_parsing))The following would be the output of the command linepython parse_me.py --help:usage:parse_me.py[-h][--an_other_intAN_OTHER_INT]an_inta_string

Idareyoutoparseme!!!

positionalarguments:an_intintareprettycoola_stringstringaren'tthatnice

optionalarguments:-h,--helpshowthishelpmessageandexit--an_other_intAN_OTHER_INTguesswhat?IgotadefaultvalueThe methodparse_me_if_you_canexpect anintwith the namean_int, astrwith the namea_stringand
otherintwith the namean_other_intand a default value of 12. So does the parser as displayed by the--helpcommand.Note:create_parsercannot decorate the__init__method of a class unless the class is itself decorated
withparse_class. AParseThisExceptionwill be raised if you attempt to use thecallmethod of such a parser.The following would be the output of the command linepython parse_me.py 2 yes --default 4:('yesyes',8)Help messageIn order to get a help message generated automatically from the method docstring
it needs to be in the specific format described below:fromparse_thisimportcreate_parser@create_parser(delimiter_chars="--")defmethod(self,spam:int,ham:int):"""<description><blank_line><arg_name><delimiter_chars><arg_help><arg_name><delimiter_chars><arg_help>"""passdescription: is a multiline description of the method used for the command lineeach line of argument help have the following component:arg_name: thesamename as the argument of the method.delimiter_chars: one or more chars that separate the argument and its help message. Using whitespaces is not
recommended as it could have an expected behavior with multiline help message.arg_help: is everything behind the delimiter_chars until the next argument,a blank lineor the end of the
docstring.Thedelimiter_charscan be passed to bothparse_thisandcreate_parseras the keywords argumentdelimiter_chars.
It defaults to:since this is the convention I most often use.If no docstring is specified a generic - not so useful - help message will be generated for the command line and
arguments.Using None as a default value and bool as flagsUsingNoneas a default value is common practice in Python but forparse_thisandcreate_parserto work properly
the type of the argument which defaults toNoneneeds to be specified. Otherwise aParseThisExceptionwill be
raised.fromparse_thisimportcreate_parser@create_parser()defparrot(ham:str,spam=None):ifspamisnotNone:returnham*spamreturnham# Will raise ParseThisException: To use default value of 'None' you need to specify# the type of the argument 'spam' for the method 'parrot'Specifying the type ofspamwill allowcreate_parserto work properlyfromparse_thisimportcreate_parser@create_parser()defparrot(ham:str,spam:int=None):ifspamisnotNone:returnham*spamreturnham# Calling function.parser.call(args="yes".split()) -> 'yes'# Calling function.parser.call(args="yes --spam 3".split()) -> 'yesyesyes'An other common practice is to usebools as flags or switches. All arguments of typebool, either typed directly or
inferred from the default value, will become optional arguments of the command line. Aboolargument without default
value will default toTrueas in the following example:fromparse_thisimportcreate_parser@create_parser()defparrot(ham:str,spam:bool):ifspam:returnham,spamreturnham# Calling parrot.parser.call(args="yes".split()) -> 'yes', True# Calling parrot.parser.call(args="yes --spam".split()) -> 'yes'Adding--spamto the arguments will act as a flag/switch settingspamtoFalse. Note thatspamas become
optional and will be given the valueTrueif--spamis not among the arguments to parse.Arguments with a boolean default value will act as a flag to change the default value:fromparse_thisimportcreate_parser@create_parser()defparrot(ham:str,spam:bool=False):ifspam:returnham,spamreturnham# Calling parrot.parser.call(args="yes".split()) -> 'yes'# Calling parrot.parser.call(args="yes --spam".split()) -> ('yes', True)Here everything works as intended and the default value forspamisFalseand passing--spamas an argument to be parsed will assign itTrue.DecoratorAs a decoratorcreate_parserwill create an argument parser for the decorated function. Aparserattribute will be
added to the method and can be used to parse the command line argument.fromparse_thisimportcreate_parser@create_parser()defconcatenate_str(one:str,two:int=2):"""Concatenates a string with itself a given number of times.Args:one: string to be concatenated with itselftwo: number of times the string is concatenated, defaults to 2"""returnone*twoif__name__=="__main__":print(concatenate_str.parser.call())Calling this script from the command line,python script.py yes --two 3will return'yesyesyes'as expected and all
the parsing has been done for you.Note that the function can still be called as any other function from any python file. Also it isnotpossible to
stackcreate_parserwith any decorator that would modify the signature of the decorated function e.g.
usingfunctools.wraps.FunctionAs a functionparse_thiswill handle the command line arguments directly.fromparse_thisimportparse_thisdefconcatenate_str(one,two=2):"""Concatenates a string with itself a given number of times.Args:one: string to be concatenated with itselftwo: number of times the string is concatenated, defaults to 2"""returnone*twoif__name__=="__main__":print(parse_this(concatenate_str))Calling this script with the same command line argumentsyes --two 3will also return'yesyesyes'as expected.ClassmethodsIn a similar fashion you can parse command line arguments for classmethods:fromparse_thisimportcreate_parserclassMyClass(object):@classmethod@create_parser(delimiter_chars="--")defparse_me_if_you_can(cls,an_int:int,a_string:str,default:int=12):"""I dare you to parse me !!!Args:an_int -- int are pretty coola_string -- string aren't that nicedefault -- guess what I got a default value"""returna_string*an_int,default*defaultMyClass.parse_me_if_you_can.parser.call(MyClass)The output will be the same as usingcreate_parseron a regular method.Notes:Theclassmethoddecorator is placedon topof thecreate_parserdecorator in order for the method to still be
a considered a class method.Aclassmethoddecorated withcreate_parserin a class decorated withparse_classwill not be accessible through
the class command line.Installingparse_thisparse_thiscan be installed using the following command:pipinstallparse_thisRUNNING TESTSTo check that everything is running fine you can run the following command after cloning the repo:python-mpipinstall--upgradepip&&python-mpipinstall-rrequirements.txt&&pytestCAVEATSparse_thisandcreate_parserare not able to be used on methods with*argsand**kwargsA subsequent effect of the previous caveat is thatcreate_parsercannot be stacked with other decorator that would
alter the callable's signatureClassmethods cannot be access from the command line in a class decorated withparse_classWhen usingcreate_parseron a method that has an argument withNoneas a default value its typemust bepast in
the list of types. AParseThisExceptionwill be raised otherwise.TO DOHandle file argumentsHandle list/tuple arguments i.e. argparse's nargsLicenseparse_thisis released under the MIT Licence. See the bundled LICENSE file for details.Contributing and devpython3-mvenv--clear--upgrade-deps--prompt"parse-this-39"venv&&\sourcevenv/bin/activate&&\pipinstall-rrequirements.txt&&\pre-commitinstall&&\pytest