Argument Parsing - nnlib.arguments
¶
The arguments
package provides a replacement for the built-in argument parser argparse
.
The Arguments
Class¶
Yet Another command line argument class, but with IDE support.
The Arguments
class is designed with the goals in mind (in decreasing order of importance):
- Index-able by IDEs and IPython.
- Non-verbose, intuitive syntax.
- Support custom verification.
- Valid replacement for
argparse
for simple usages.
Using Arguments
is simple. Simply subclass and add attributes:
class MyArguments(Arguments):
batch_size: int = 32
optimizer: Choice('sgd', 'adam', 'adagrad') = 'sgd'
cuda = Arguments.Switch()
languages: Optional[List[str]] = None
data_path: Path(exists=True) = None
These arguments can be specified through command line:
python main.py \
--batch-size 16 \
--optimizer adam \
--cuda \
--languages ['de','en','it'] # no spaces!
or programmatically:
args = MyArguments(languages=['de', 'en'])
Note
The following argument is provided by default:
ipdb
(Switch
): If on, spawn an IPython debugger on exceptions. Requires having the packageipdb
installed.
Type Resolution¶
Python annotations are used as both type specification and arguments options. When not specified, type will be deduced from the default value. In this case, the default value cannot be None
.
Note that it is difficult to perform complex type checks at run time (especially for custom and generic types). Instead of writing lengthy and hacky code for such pointless functionality, we use the following rule-based method:
If argument type is
typing.Optional
, allow value to be None. If not None, use parameter type and follow the rest of rules.If argument type is a simple Python built-in type (
bool
,int
,float
,str
), perform type coercion.- Specifically for bool, command-line syntax could be switch-style (–flag, only if default value is False), or value-style using anything you find reasonable (–flag true, –flag Y, etc).
If argument type is a container generic (
typing.List
,typing.Tuple
,Set
), or their corresponding types (list
,tuple
,set
), treat value as list and then perform coercion.- Lists could be specified using Python syntax (
[1,2,3]
, remember not to put spaces unless wrapped in quotes), or a comma-separated string (1,2,3
). Only the former syntax supports complex lists e.g. nested lists. - Each list element is recursively checked against the container parameter type (if specified using
typing
module).
- Lists could be specified using Python syntax (
If argument type is
typing.Dict
ordict
, treat value as dict.- Python syntax must be used (
{'a':1,'b':2}
). Key and value types are checked recursively (if specified).
- Python syntax must be used (
If argument type is
Choice
, value is compared to string forms of each choice and the matched choice is used. If no match is found, aValueError
is raised.Choice
is inherited fromtyping.Union
, so IDEs should be able to recognize.- However, it is not recommended to mix choices of different types. If False or None should be one of the choices, either include it as literal
none
or useOptional
.
If argument type is
Path
, value is stored asstr
but checked to be of correct format. If exists=True, thenos.path.exists()
is called for validation.If argument type is any other type, function, or callable object, it is called with the value (
str
form). This is not good practice and should be avoided.
Note
The above is the intended behavior, but is not quite what is implemented as of now.
Currently, if the argument type is not among built-in types, Choice
, and
Path
, we simply fallback to ast.literal_eval()
and do not perform type checking.