Releases: swansonk14/typed-argument-parser
Added Support for Configuration Files
Configuration files may now be loaded along with arguments with the optional flag config_files: List[str]
. This addresses #25, thanks to @arbellea.
For example, if you have the config file my_config.txt
--arg1 1
--arg2 two
then you can write
from tap import Tap
class Args(Tap):
arg1: int
arg2: str
args = Args(config_files=['my_config']).parse_args()
Arguments passed in from the command line overwrite arguments from the configuration files. Arguments in configuration files that appear later in the list overwrite the arguments in previous configuration files.
Subparsers and configure
Subparsers
Tap now supports subparsers.
To add a subparser, override the configure
method and call self.add_subparser
. Optionally, to specify keyword arguments (e.g., help
) to the subparser collection, call self.add_subparsers
. For example,
class SubparserA(Tap):
bar: int # bar help
class SubparserB(Tap):
baz: Literal['X', 'Y', 'Z'] # baz help
class Args(Tap):
foo: bool = False # foo help
def configure(self):
self.add_subparsers(help='sub-command help')
self.add_subparser('a', SubparserA, help='a help')
self.add_subparser('b', SubparserB, help='b help')
Configure
Tap has a new method called configure
which is called at the end of initialization. Two uses of configure
are add_argument
(previously called in add_arguments
, which has now been deprecated) and add_subparser
. For example,
class SubparserA(Tap):
bar: int
class Args(Tap):
foo: bool = False
def configure(self):
self.add_argument('--foo', '-f')
self.add_subparser('a', SubparserA)
Improved Handling of Unpicklable Attributes
Changed saving and loading to better handle unpicklable attributes
Save now has the skip_unpicklable
flag that when set to True
skips unpicklable attributes and when loaded produces an UnpicklableObject
placeholder for that attribute, indicating that the attribute must be set specially. The signature of save is now:
save(self, path: str, with_reproducibility: bool = True, skip_unpicklable: bool = False) -> None
Support for Native Multiline Help Strings
Support for Native Multiline Help Strings
Previously, Tap
made it possible to specify the help string for an argument using a single line comment. Now, Tap
also supports specifying the help string with a multiline comment below the class variable for the argument. If both a single line comment and a multiline comment are provided, the two are concatenated with a space.
# main.py
from tap import Tap
class Args(Tap):
arg: bool = False # This is the one-line summary.
"""
This is the multiline explanation.
"""
args = Args().parse_args()
usage: main.py [--arg1] [-h]
optional arguments:
--arg (bool, default=False) This is the one-line summary. This
is the multiline explanation.
-h, --help show this help message and exit
as_dict bug fix
as_dict
bug fix
The Tap method as_dict
failed to include certain attributes, such as properties, when they exist in a super class Tap and not in the sub class Tap. For example, consider the following:
from tap import Tap
class SuperPropertyTap(Tap):
a: str
@property
def pi(self):
return 3.14
class SubPropertyTap(SuperPropertyTap):
b: int = 1
super_args = SuperPropertyTap().parse_args()
print(super_args.as_dict()) # includes pi=3.14
sub_args = SubPropertyTap().parse_args()
print(sub_args.as_dict()) # does NOT include pi=3.14 but should
In this example, the super class Tap does include the property pi
in its as_dict
but the sub class Tap does not. This has now been fixed so that both include the property pi
in their as_dict
.
Improved error handling and other improvements
Improved error handling
We have implemented a number of improvements to error handling and reporting as well as a few other minor changes.
-
Better error handling for older versions of git.
-
Previously, Tap correctly crashed when a
Literal
arg was passed an argument that was not a valid choice, but it threw aKeyError
that was difficult to interpret. Now Tap allows theargparse
error to surface when an incorrect choice is provided. -
Tap used to attempt to cast default values for
Set
andTuple
to sets and tuples, crashing if the cast was unsuccessful. Now Tap only attempts to cast lists and leaves other default values unchanged, in line with typicalargparse
functionality.
Other improvements
-
Added a
with_reproducibility
flag to thesave
method to allow users to decide whether to save reproducibility information. -
Previously, the Tap
from_dict
andload
methods returnedNone
but now returnself
. This enables the following streamlined workflow:
from tap import Tap
class Args(Tap):
arg: int
args = Args().from_dict({'arg': 10})
- Tap now has a
__version__
attribute, which can be checked as follows:
import tap
print(tap.__version__) # '1.5.1'
Support for actions, improved argument saving, and other fixes
Added support for action=
In argparse
, it is possible to specify actions that modify how the parsed arguments are used. For example, action='append'
appends the provided arguments to an existing list. Actions are specified as parameters in calls to the self.add_argument
function within the user's override of the add_arguments
method. A complete example is as follows:
from tap import Tap
from typing import List
class Args(Tap):
arg: List[int] = [1, 2]
def add_arguments(self):
self.add_argument('--arg', action='append')
args = Args().parse_args('--arg 3 --arg 4'.split())
print(args.arg) # [1, 2, 3, 4]
We added support for all actions defined by argparse
: count, const, append_const, append, extend, store_true, store_false, store_const, and version. However, we don't guarantee consistent behavior with argparse
for custom actions.
Improved Argument Saving
The as_dict
method has been modified to return not just parsed arguments but also properties and other attributes set by the user. Since save
uses as_dict
internally, the same modification applies to save
. Thus, the new as_dict
and save
will return a superset of the attributes it returned previously. For example:
from tap import Tap
class Args(Tap):
arg: int = 2
def __init__(self):
super(Args, self).__init__()
self._prop = 'hi'
@property
def prop(self):
return self._prop
args = Args().parse_args()
print(args.as_dict()) # previous version: {'arg': 2}, new version: {'arg': 2, 'prop': 'hi'}
Fixes
-
Fixed an issue to ensure that
dest=
works properly andas_dict
correctly dumps all parameters -
Fixed reliance on source code parsing to enable editing of Tap code without affecting currently running programs
-
Fixed an issue to prevent class methods from being included as parsed class variables
Fix to has_git
Previously, has_git
only checked whether git was installed on the system. Now, it also checks whether the current working directory is inside a git repo to prevent errors where the code is not inside a git repo.
Built saving, loading, and deepcopying into Tap objects
Several improvements and additions to saving, loading, and deepcopying Tap objects.
save
: Improved JSON encoding to enable saving of arbitrary Python objects with pickle strings inside the JSON file. Additionally, added native JSON support for tuples and sets.load
: Implemented aload
function which can load args from a JSON file (e.g. the JSON file created bysave
).from_dict
: Implemented afrom_dict
function which can load args from a dictionary.deepcopy
: Fixed several issues to enable deepcopying of Tap objects.
Support for Tuples, bool boxed types, and empty boxed types
New support for Tuples, for example:
Tuple
Tuple[int]
Tuple[int, str, float, bool, str]
Tuple[int, ...]
New support for bool boxed types, for example:
Optional[bool]
List[bool]
Set[bool]
Tuple[bool]
New support for empty boxed types, which are equivalent to Type[str], for example:
Optional
(same asOptional[str]
)List
(same asList[str]
)Set
(same asSet[str]
)Tuple
(same asTuple[str]
)