neuralib.argp
Annotation-based argparse
- author:
Ta-Shun Su
This module provide a way to integrate python argparse module into class attribute
and annotation type, that allow options have type information, and allow parser combination
easily.
class Argument and function argument
A simple option class that contains several options carried by their attributes.
>>> class ExampleOptions:
... ANIMAL: str = argument('--ANIMAL')
... EXP_DATE: str = argument('--EXP_DATE')
... OUTPUT_DIR: str = argument()
In ExampleOptions, ANIMAL is an attribute with type annotation str. it has
a class variable Argument (argument() return) which contains the
arguments of ArgumentParser.add_argument. For now, we have an optional
argument --ANIMAL which accept one argument. EXP_DATE as is another optional argument.
And OUTPUT_DIR are a positonal argument because it doesn’t have dashed options.
>>> opt = parse_args(ExampleOptions())
... print(opt.ANIMAL)
After class declared, you can use parse_args() to parse cli arguments. This
function will create an ArgumentParser and find out all argument attributes.
Then set the attributes from parsed result.
Commandline usage
In bash, you can call this option class
python -m module.path --ANIMAL name --EXP_DATE date output
print_help() does the similar things but print the help document to the stdout.
>>> print_help(opt)
Or use -h options
python -m module.path -h
Annotation type infering
In general, you can think argument() just a delegate funcion that passes the arguments
to the ArgumentParser.add_argument. However, this function will try to
infer missing arguments based on the annotation type when creating the ArgumentParser.
For now, this module is not powerful to handle all possible case. there are support type
(Please see complete_kwargs() for detailed):
bool: infer parameteractiontostore_true,defaulttoFalse.Literal[...]: infer parameterchoices.Optional[T]: infer parametertypetoTif it is callable. If it isLiteral, apply 2.callable(...)with signatureCallable[[str], T]: infer parametertypetoTparameter
destalways use the attribute name.
Additionally, Argument also provide a parameter group to reduce the complexity of
create subgrouping parser.
Option class compose
Option class can be composed by inherition. Child option class can also change the value from parent’s argument. As well as disable it (by replacing a value)
>>> class MoreOptions(ExampleOptions):
... # additional optional option
... verbose: bool = argument('-v', '--verbose')
... # change default value
... ANIMAL: str = as_argument(ExampleOptions.animal).with_options(default='YW00')
... # and disable an option
... OUTPUT_DIR: str = 'output' # just replace with a value
Change options name is more complicate, because you might want to add more name, remove some name,
or rename some name. with_options() allow you to do that:
>>> class ChangeExample(ExampleOptions):
... # replace option name: --animal
... ANIMAL: str = as_argument(ExampleOptions.animal).with_options('--animal')
... # add more option name: -A, --ANIMAL
... ANIMAL: str = as_argument(ExampleOptions.animal).with_options(..., '-A')
... # rename option: --animal
... ANIMAL: str = as_argument(ExampleOptions.animal).with_options({
... '--ANIMAL': '--animal'
... })
Utility function for option class
ExampleOptions doesn’t declated neither __str__ nor __repr__, so it is not convenient to debug.
funciont as_dict() provide a way to take argument attribute’s value into a dictionary.
>>> as_dict(opt)
{'ANIMAL': ..., 'EXP_DATE': ..., 'OUTPUT_DIR': ...}
Option class is not restricted into only one use case. It works like a normal class.
class AbstractParser
This class provide a main like class that has more control on argparse.ArgumentParser
creation.
>>> class ExampleParser(AbstractParser, ExampleOptions):
... DESCRIPTION = 'Example parser'
... def run(self):
... ...
>>> if __name__ == '__main__':
... ExampleParser().main()
Subcommands
This module isn’t fully support sub-command feature, but only provide a simple way for specific case:
>>> parse_command_args(
... description='top level parser',
... parsers=dict(ep=ExampleParser)
... )
In bash:
python -m module.path ep --ANIMAL name --EXP_DATE date output
Validator usage
The validator system provides a fluent, chainable API for building type-specific validation rules. It works by defining specialized “builder” classes (e.g., for strings, integers, floats, lists, and tuples) that let you specify constraints like numeric ranges, string length ranges, regex checks, or container length/item rules
See detailed in validator
Notebook Demo