Skip to content

Commit 59a2ea3

Browse files
committed
Improving README for Union and complex types
1 parent 58b85f1 commit 59a2ea3

File tree

1 file changed

+75
-19
lines changed

1 file changed

+75
-19
lines changed

README.md

+75-19
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,38 @@ pip install -e .
4141
## Table of Contents
4242

4343
* [Installation](#installation)
44+
* [Table of Contents](#table-of-contents)
4445
* [Tap is Python-native](#tap-is-python-native)
4546
* [Tap features](#tap-features)
46-
+ [Arguments](#arguments)
47-
+ [Help string](#help-string)
48-
+ [Flexibility of `configure`](#flexibility-of-configure)
49-
+ [Types](#types)
50-
+ [Argument processing with `process_args`](#argument-processing-with-process_args)
51-
+ [Processing known args](#processing-known-args)
52-
+ [Subclassing](#subclassing)
53-
+ [Printing](#printing)
54-
+ [Reproducibility](#reproducibility)
55-
- [Reproducibility info](#reproducibility-info)
56-
+ [Saving and loading arguments](#saving-and-loading-arguments)
57-
- [Save](#save)
58-
- [Load](#load)
59-
- [Load from dict](#load-from-dict)
60-
+ [Loading from configuration files](#loading-from-configuration-files)
47+
+ [Arguments](#arguments)
48+
+ [Help string](#help-string)
49+
+ [Flexibility of `configure`](#flexibility-of--configure-)
50+
- [Adding special argument behavior](#adding-special-argument-behavior)
51+
- [Adding subparsers](#adding-subparsers)
52+
+ [Types](#types)
53+
- [`str`, `int`, and `float`](#-str----int---and--float-)
54+
- [`bool`](#-bool-)
55+
- [`Optional`](#-optional-)
56+
- [`List`](#-list-)
57+
- [`Set`](#-set-)
58+
- [`Tuple`](#-tuple-)
59+
- [`Literal`](#-literal-)
60+
- [`Union`](#-union-)
61+
- [Complex Types](#complex-types)
62+
+ [Argument processing with `process_args`](#argument-processing-with--process-args-)
63+
+ [Processing known args](#processing-known-args)
64+
+ [Subclassing](#subclassing)
65+
+ [Printing](#printing)
66+
+ [Reproducibility](#reproducibility)
67+
- [Reproducibility info](#reproducibility-info)
68+
+ [Saving and loading arguments](#saving-and-loading-arguments)
69+
- [Save](#save)
70+
- [Load](#load)
71+
- [Load from dict](#load-from-dict)
72+
+ [Loading from configuration files](#loading-from-configuration-files)
6173

6274
## Tap is Python-native
75+
6376
To see this, let's look at an example:
6477

6578
```python
@@ -202,7 +215,7 @@ class Args(Tap):
202215

203216
### Types
204217

205-
Tap automatically handles all of the following types:
218+
Tap automatically handles all the following types:
206219

207220
```python
208221
str, int, float, bool
@@ -215,8 +228,10 @@ Literal
215228

216229
If you're using Python 3.9+, then you can replace `List` with `list`, `Set` with `set`, and `Tuple` with `tuple`.
217230

231+
Tap also supports `Union`, but this requires additional specification (see [Union](#-union-) section below).
232+
218233
Additionally, any type that can be instantiated with a string argument can be used. For example, in
219-
```
234+
```python
220235
from pathlib import Path
221236
from tap import Tap
222237

@@ -257,9 +272,9 @@ Tuples can be used to specify a fixed number of arguments with specified types u
257272

258273
Literal is analagous to argparse's [choices](https://docs.python.org/3/library/argparse.html#choices), which specifies the values that an argument can take. For example, if arg can only be one of 'H', 1, False, or 1.0078 then you would specify that `arg: Literal['H', 1, False, 1.0078]`. For instance, `--arg False` assigns arg to False and `--arg True` throws error. The `Literal` type was introduced in Python 3.8 ([PEP 586](https://www.python.org/dev/peps/pep-0586/)) and can be imported with `from typing_extensions import Literal`.
259274

260-
#### Complex types
275+
#### `Union`
261276

262-
More complex types _must_ be specified with the `type` keyword argument in `add_argument`, as in the example below.
277+
Union types must include the `type` keyword argument in `add_argument` in order to specify which type to use, as in the example below.
263278

264279
```python
265280
def to_number(string: str) -> Union[float, int]:
@@ -272,6 +287,47 @@ class MyTap(Tap):
272287
self.add_argument('--number', type=to_number)
273288
```
274289

290+
In Python 3.10+, `Union[Type1, Type2, etc.]` can be replaced with `Type1 | Type2 | etc.`, but the `type` keyword argument must still be provided in `add_argument`.
291+
292+
#### Complex Types
293+
294+
Tap can also support more complex types than the ones specified above. If the desired type is constructed with a single string as input, then the type can be specified directly without additional modifications. For example,
295+
296+
```python
297+
class Person:
298+
def __init__(self, name: str) -> None:
299+
self.name = name
300+
301+
class Args(Tap):
302+
person: Person
303+
304+
args = Args().parse_args('--person Tapper'.split())
305+
print(args.person.name) # Tapper
306+
```
307+
308+
If the desired type has a more complex constructor, then the `type` keyword argument must be provided in `add_argument`. For example,
309+
310+
```python
311+
class AgedPerson:
312+
def __init__(self, name: str, age: int) -> None:
313+
self.name = name
314+
self.age = age
315+
316+
def to_aged_person(string: str) -> AgedPerson:
317+
name, age = string.split(',')
318+
return AgedPerson(name=name, age=int(age))
319+
320+
class Args(Tap):
321+
aged_person: AgedPerson
322+
323+
def configure(self) -> None:
324+
self.add_argument('--aged_person', type=to_aged_person)
325+
326+
args = Args().parse_args('--aged_person Tapper,27'.split())
327+
print(f'{args.aged_person.name} is {args.aged_person.age}') # Tapper is 27
328+
```
329+
330+
275331
### Argument processing with `process_args`
276332

277333
With complex argument parsing, arguments often end up having interdependencies. This means that it may be necessary to disallow certain combinations of arguments or to modify some arguments based on other arguments.

0 commit comments

Comments
 (0)