Package: apegears
An improvedArgumentParser, fully compatible with the standardargparse.ArgumentParser.What is ApeGears?ApeGears’ goal is making it easier to use theArgumentParser.
It provides simple and intuitive tools for achieving the most common use cases.ApeGears defines anArgumentParserwhich is a subclass ofargparse.ArgumentParser, and
is fully compatible with it.What is wrong withargparse?Nothing.  It works great.However, it seems to be putting too much emphasis on being powerful, and too little on being intuitive.
For some actions, its interface is overly complicated.
The most common operations (e.g. defining flags and list arguments) are sometimes not perfectly intuitive.Furthermore, it seems to be missing some useful options, such as support fordictarguments.
Also, using arguments of custom types (using thetypeparameter) doesn’t work as smoothly as you’d hope.Another annoyance is that when usingFileTypein write-mode, the output file is created very early (while
parsing cli args), not giving the option in your script to decide not to write to it after all.FeauresFollowing is an overview of the main features.  See below for more details on each.Intuitive “adder” methods for defining arguments:add_positional,add_optional,add_flag,add_list.These cover the most common use cases.  You’d hardly ever need to use the basicadd_argumentmethod.Dict arguments, usingadd_dictmethod.Defining custom argument-types is simpler and more powerful, using “specs”.Builtin support for some standard python types.E.g.range,date,datetime,Path, literals, IP address, regular expression.An alternativeFileTypeargument type, better thanargparse.FileType.Smooth integration withfileinput.Builtin support for enum arguments.Builtin support for overriding log levels from cli, usinglo99ing.Can extractdescriptionfrom docstring of caller module, to avoid doc duplication
(enable by passing:description=CALLER_DOC)Integration with otherArgumentParser-related tools.argcompletefunc_argparseAvoidingargparsebugs:Issue16399append-with-nonempty-default:
apegears includes an easy-to-use workaround.Issue13041terminal width is not detected properly:
this is fixed in python 3.8, but using apegears avoids the issue for all version.Adder methodsThe standard all-in-oneadd_argumentmethod is powerful, but not intuitive for some uses.
It has many kwargs, and not all combinations make sense.Instead, in most cases, you can use the more precise and convenient adder methods:add_positional– for defining positional arguments.add_optional– for defining optional (i.e. non-positional) arguments.add_flag– for defining (optional) flags (“switches”).add_list– for defining (optional) list arguments.Multiple values can be passed in a single arg, or multiple.  The following are equivalent,
and result with{'chars': ['a', 'b', 'c','d']}:% prog.py --chars a b c d
% prog.py --chars a b --chars c d
% prog.py --chars a --chars b --chars c --chars dYou can still use theadd_argumentmethod for “advanced” argument definitions, but you’d rarely need to.Dict argumentsUseadd_dictfor defining dict optional arguments.  E.g.:parser.add_dict('--overrides')
parser.parse_args('--overrides log_level=debug logfile=out.log'.split()).overrides
=> OrderedDict([('log_level', 'debug'), ('logfile', 'out.log')])Similar to list arguments, multiple key-value pairs can be passed in a single arg, or multiple.Custom argument typesargparsesupports adding argument types by passingtype=callable, wherecallableconverts the CLI string value to whatever you want (e.g.intconverts the string to an integer).This is not powerful enough, because often, when defining how a new argument type behaves, you’d want to include more
than just how to convert a CLI string.ApeGears makes use ofArgument Type Specs, which supports defining defaults for several fields:namesdefaultchoiceshelpmetavarEach of these can be explicitly overridden when calling the adder function.Suppose you have a typeTwhich you want to use with the parser, so you define
a spec for it,Tspec.For supporting usage likeparser.add_xxx(...,type=T,...), you either:register the spec:register_spec(T, Tspec)define a class attribute named__argparse__. E.g.:T.__argparse__ = TspecAlternatively, this also works:parser.add_xxx(...,type=Tspec,...)Argument types for standard python typesArgument type specs are predefined for some standard python types.
E.g., range, date, datetime, path, literals, IP address, regular expression.Can be used like this:parser.add_optional(..., type='date', ...)  # also works: type=datetime.date
parser.parse_args('--date 2020-03-04'.split()).date
=> datetime.date(2020, 3, 4)Another example:parser.add_optional('indexes', ..., type='range', ...)  # also works: type=range
parser.parse_args('--indexes 0:100:10'.split()).indexes
=> range(0, 100, 10)Another example, for using literals (inspired bypython-fire):parser.add_optional(‘val’, …, type=’literal’, …)
parser.parse_args(’–val {“four”:4,”six”:6}’.split()).val
{‘four’: 4, ‘six’: 6}  # this is a dictImprovedFileTypeThe problem withargparse.FileType, is that in write-mode, the file is opened (created)
during cli-parsing, even in cases where you wouldn’t want to write to the file.For example, if your script is usingargparseand takes a positional output file (mode='w'),
The following invocations will create an empty file namedfoo(deleting it if already exists):% myscript.py foo -h  # will create the file, and print help message
% myscript.py foo --no-such-option  # will create the file, and print argparse error messageThere are other cases where you would decide not to write to output file (e.g. you fail generating
the content), but usingargparse.FileTypewould still create an empty file (deleting existing
one).The solution is usingapegears.FileTypeinstead, which lazily opens the file, when it is first
accessed.fileinputargumentsWhen you want to usefileinputin
your script,apegearscan save you a few lines of code:from apegears import ArgumentParser, fileinput
parser = ArgumentParser()
parser.add_positional(type=fileinput(decompress=True), nargs='*')
args = parser.parse_args()
for line in args.infiles:
    ...Also, passingdecompress=Truehandles compressed files better than usingfileinputdirectly withhook_compressed(seeissue5758).Enum argumentsEnum types are also supported as argument types:class Direction(Enum):
    UP = 1
    DOWN = 2
    LEFT = 3
    RIGHT = 4

parser.add_optional(type=Direction)
parser.parse_args('--direction LEFT'.split()).direction
=> <Direction.LEFT: 3>Overriding log levels from cliIf you’re usinglo99ing, the parser automatically
includes a -L/–log-level option for overriding log levels from cli.E.g., to override from cli:myscript.py -L mylogger.traffic=debug yourlogger=errorThe append-with-nonempty-default issueYou might have encountered abugwhen using list arguments
in the standardArgumentParser:from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('list', action='append', default=['D'])
parser.parse_args('X'.split()).list
=> ['D', 'X']  # expected: ['X']Basically,default, instead of being used as adefaultvalue, is used as aninitialvalue.There is no easy-to-use workaround in the argparser level, but ApeGears provides one.Theadd_listandadd_dictmethods include a workaround this issue.  It is enabled by default.If you use theadd_argumentmethod directly, the workaround is disabled (for being compatible withargparse),
but you can enable it by passingstrict_default=True.Integration with otherArgumentParser-related toolsargcompleteargcompleteallows
“command line tab completion of arguments for your Python script”.For argcomplete users, there are a few (minor) advantages to using ApeGearsArgumentParser, overargparse’s:No need to callargcomplete.autocomplete(parser), it is called automatically for youBetter completion of enum typesAvoids the awkward way of setting a custom completeruse like:parser.add_argument(...,completer=MyCompleter)instead of:parser.add_argument(...).completer= MyCompleterIf you define custom argument types, you can also define a completer as part of their specfunc_argparsefunc_argparseis used for
“Generating a nice command line interface for a list of functions or a module”.ApeGears lets you use func_argparse for generating an ApeGearsArgumentParser, instead ofargparse’s.The main advantages of usingapegears + func_argparseover usingfunc_argparsealone:Dict optionsCustom argument types, and argument types for standard python typesTo use it, simply replace your import lines:import func_argparse            -->    import apegears.func_argparse
from func_argparse import ...   -->    from apegears.func_argparse import ...Getting StartedInstallationUsing pip:pip install apegearsStart using theArgumentParserapegears.ArgumentParseris fully compatible withargparse’s, so you can start
by replacing your import lines:import argparse            -->  import apegears
from argparse import ...   -->  from apegears import ...… to unleash the apes.What does the Name Mean?Nothing.argparse = list('argparse')
apegears = list('apegears')
while argparse != apegears:
    random.shuffle(argparse)
print('Got it?')
print('Probably not...')