Package: argparse-tree
argparse_treeLet the commandline arguments be distributed along the
source directory, which can be collected into one with
a single collection as a parent before initialising the
command.InstallationUsingpippipinstallargparse_treeUsingpipbut with a cloned repositorygitclone"https://github.com/bvraghav/argparse_tree"cdargparse_tree
pipinstall.UsageA detailed usage can be found in the folderexample. The crux of the matter is
illustrated below:parser=ArgumentParser(parents=collect_parsers('*_style1.py','style2/*.py',parent_package=__package__),)parser.add_argument('-v','--verbose',action='store_true',help="Verbosity switch.")add_commands(parser,'*_command.py',parent_package=__package__,action=load_module_subparser_action('*_command.py',__package__),)Inside ofstyle2/generic.pyparser.add_argument('--style2',choices=collect_keys('style2/*.py'),action=load_module_action('*.py',__package__))Four functions namely,collect_parsers,collect_keys,add_commands, andload_module_subparser_actionare utilized to achieve the desired behaviour, that isTo have a set of argument groups collected from a
set of files following a convenvention in names;To allow, non-homogeneity in such groups; andTo extend this behaviour for sub-parsers.ConventionA set of files, each containing a functioncli_argsthat returns
parser information, are grouped together using a
certain convention in their file names, for example,
using a suffix say,*_data.pymay represent different
datasets. The arguments generic to all datasets may be
written togeneric_data.py. They are all collected
using the functioncollect_parsers.The convention may have simple been altered to follow a
prefixed format, saydata/*.py--- should work
equally well.collect_parserscollect_parsers(*patterns,root=None,parent_package=None)Glob theROOTfolder withPATTERNS, one at a time,
and collect their parsers. If not specified,ROOTis
computed, using theinspectAPI, to be the folder
where the caller script resides.PARENT_PACKAGEis the name of package corresponding
toROOTfolder. If not specified,PARENT_PACKAGEis
not used.collect_keyscollect_keys(pattern,root=None,mod_to_key=utils.mod_to_key)Glob theROOTfolder withPATTERNand create a key
corresponding to each module. Key is computed usingMOD_TO_KEYfunctional, which follows the same
signature asutils.mod_to_key.add_commandsadd_commands(parser,pattern,*,root=None,parent_package=None,dest='command',action=None,mod_to_key=utils.mod_to_key)Create subcommands to cli usingPARSER, one
corresponding to eachPATTERN. Command name is
computed usingMOD_TO_KEYfunctional, which follows
the same signature asutils.mod_to_key.The same convention ascollect_parsersis followed forPATTERN,ROOT, andPARENT_PACKAGE.DESTandACTIONare forwarded toargparse.ArgumentParser.add_subparsers.load_module_subparser_actionload_module_subparser_action(pattern,package=None,key_to_mod=utils.key_to_mod)Create anargparse.Actionto load a module
corresponding to a user-given key, based onPATTERN,
andPACKAGEusing a decoderKEY_TO_MODfunctional,
which follows the same signature asutils.key_to_mod.In case it is desirable to load a module corresponding
to value in user-specified argument, at the time of
parsing the args, use this as value ofactioninargparser.ArgumentParser.add_argument.UpdateVersion >= 0.1.3: Code issues separate actions
corresponding tostoreaction andsubparseraction,
asload_module_store_actionandload_module_subparser_actionrespectively.BREAKING:load_module_actionis an
alias forload_module_store_action.MotivationThe core idea behind this project is to exploitthis
argparse
functionality:local_parser=argparse.ArgumentParser()local_parser.add_argument('--foo')global_parser=argparse.ArgumentParser(parents=[local_parser,...])The proof of concept can be seen inthis
code.For example in machine learning training routines,
there can be many models, many solvers, many datasets,
each with different set of options. We can have
something like:## generic_dataset.pydefcli_parser():fromargparseimportArgumentParserparser=ArgumentParser(add_help=False)group=parser.add_argument_group('Generic Dataset Options')group.add_arguemnt('--foo')returnparserAnd similarly add cli parsers fora_dataset.py,b_dataset.pyand so forth. Here, we have followed the
norm of suffxingdatasetto each python module. We
can collect them into the global parser as follows:fromargparseimportimportargparse_treeasAtreedefcli_parser():parser=ArgumentParser(parents=Atree.collect(['*_dataset.py']))parser.add_argument('--verbose',help="Verbosity switch.")returnparserWe can also have an option of collection from any
pattern. For example,data/*.pyfor any module in folderdata.Any other pattern compatible withpathllib.Path.globScratchpadCan an instance initializer know the caller file pathHere is an experiment which has an affirmative result.## alpha.py #### ----------------------------------------------------classA:def__init__(self):frompathlibimportPathimportinspectf=inspect.stack().pop(1)# print(f.filename)self.p=Path(f.filename)## beta.py #### ----------------------------------------------------fromalphaimportAprint(A().p)The idea is the use this behaviour and search the
caller's parent path for the relevant module patterns.This may be interesting; but it is always better to
provide an unambiguous argument instead of inspect!