Add options to click commands based on inspecting functions
Project description
click-inspect
Add options to click commands based on inspecting functions
Installation
Usage
Suppose an application containing an API function for which we would like to expose a command line interface. That function expects one or two arguments with internal data types and a bunch of configuration options. For example:
def display_data(data: List[Tuple[int, int]],
*,
size: int, symbol: str = 'x', empty: str = ' ') -> str:
"""Display the given data points in a 2D ASCII grid.
Args:
data (list of (int, int)): The data points as x- and y-tuples.
size (int): Size of the grid in both dimensions.
symbol (str): Symbol for displaying data points.
empty (str): Symbol for displaying empty space.
Returns:
str: The string containing the grid.
"""
grid = [[empty]*size for _ in range(size)]
for x, y in data:
grid[y][x] = symbol
top = bottom = ('+', *'-'*size, '+')
grid = (top, *(('|', *row, '|') for row in grid), bottom)
return '\n'.join(map(''.join, grid))
Here the type of the first argument, data
, is an internal aspect of the application, but the remaining arguments are generic options.
Now we want to create a click interface for using this function from the command line. Specifically we want it to work on JSON files of the following format:
{"data": [[1, 1], [2, 4], [3, 3]]}
So the only thing our command needs to do is to read the JSON file and convert the content in a way that it is compatible with what display_data
expects:
import json
import click
@click.command()
@click.argument('file')
def display(file):
with open(file) as fh:
data = json.load(fh)['data']
data = [[int(x) for x in row] for row in data]
print(display_data(data))
if __name__ == '__main__':
display()
Then we can run the program in the following way:
$ python example.py test.json
+-----+
| |
| x |
| |
| x |
| x |
+-----+
Now this only uses the default configuration of the display_data
function and we also want to expose these optional parameters to the command line interface. We can do so by adding a few options:
@click.command()
@click.argument('file')
@click.option('--size', default=5, help='Size of the grid in both dimensions.')
@click.option('--symbol', default='x', help='Symbol for displaying data points.')
@click.option('--empty', default=' ', help='Symbol for displaying empty space.')
def display(file, size, symbol, empty):
with open(file) as fh:
data = json.load(fh)['data']
data = [[int(x) for x in row] for row in data]
print(display_data(data, size=size, symbol=symbol, empty=empty))
But that's a lot of code duplication. We duplicated the parameter names, the default values and the help text from the docstring.
Also if we decide to add a new parameter to display_data
we need to update the command as well.
This is where click-inspect
comes in handy. Using the add_options_from
decorator we can simply add all optional parameters from display_data
to the display
command without code duplication:
from click_inspect import add_options_from
@click.command()
@click.argument('file')
@add_options_from(display_data)
def display(file, **kwargs):
with open(file) as fh:
data = json.load(fh)['data']
data = [[int(x) for x in row] for row in data]
print(display_data(data, **kwargs))
Customization
The add_options_from
decorator supports various keyword parameters which can be used for
customizing the way options are created from parameters. Please refer to the docstring
of add_options_from
for more information. In the following some possibilities are shown:
# This adds only the `size` and `empty` parameters as options:
@add_options_from(display_data, include={'size', 'empty'})
# This has a similar effect by excluding the `symbol` parameter:
@add_options_from(display_data, exclude={'symbol'})
# This specifies custom option-names for some of the parameters:
@add_options_from(display_data, names={'size': ['-s', '--size'], 'empty': ['-e', '--empty']})
# This overrides the default value for the `symbol` parameter:
@add_options_from(display_data, custom={'symbol': {'default': '#'}})
Boolean flags
Boolean flags are supported via the bool
type hint. The default behavior is to create an on-/off-option
as described in the click docs.
If this is undesired, it can be customized by using the names
keyword parameter of add_options_from
:
foo: bool = True
# translates to
click.option('--foo/--no-foo', default=True)
# Use the following to create an on-/off-option:
add_options_from(my_func, names={'foo': ['--foo']})
# translates to
click.option('--foo', is_flag=True, default=True)
Lists and tuples
click-inspect
also supports sequences as type hints (e.g. list[int]
or tuple[int, str]
).
Specifically those type hints are translated as follows:
foo: Sequence[int]
foo: List[int]
# translates to
click.option('--foo', multiple=True, type=int)
# -------------------------------------------------- #
foo: Tuple[int, str]
# translates to
click.option('--foo', type=(int, str))
These type hints are also supported as part of the docstring:
"""
Args:
foo (list of int): Equivalent to List[int].
foo ((int, str)): Equivalent to Tuple[int, str].
"""
Unions
click-inspect
also supports typing.Union
by simply selecting the first option as the type.
So Union[int, str]
is equivalent to int
.(1)
Unions are also supported as part of the docstring via int or str
.
Docstring styles
click-inspect
supports inspecting reST-style docstrings, as well as Google- and Numpy-style docstrings via sphinx.ext.napoleon
.
(1) If the Union is part of a generic type, it is not guaranteed that the first option is the same one that is displayed in the Union literal. This is because generic types cache their __getitem__
methods. For that reason List[Union[int, str]] is List[Union[str, int]]
and the selected type would be int
in both cases since that's the one that got cached.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file click-inspect-0.3.2.tar.gz
.
File metadata
- Download URL: click-inspect-0.3.2.tar.gz
- Upload date:
- Size: 8.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.0.8 CPython/3.8.6 Linux/5.4.0-1031-azure
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | b8045196b1227c5c730e877f336155b6fa2e35e40183e87fd5f2310e31104e78 |
|
MD5 | de986f18fb23b2ecfa1644d32e42d06e |
|
BLAKE2b-256 | 15dc9717d4c3030f8fd10948efe6ddbddd22b65ba093612ec80a29c697500b8e |
File details
Details for the file click_inspect-0.3.2-py3-none-any.whl
.
File metadata
- Download URL: click_inspect-0.3.2-py3-none-any.whl
- Upload date:
- Size: 8.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: poetry/1.0.8 CPython/3.8.6 Linux/5.4.0-1031-azure
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 621580d980da24cbb3f83fac0da6475f8eaca11632054b7c2b277ea452dcae47 |
|
MD5 | 542bbe5c7e3399c1434a0be7ae6a6cf1 |
|
BLAKE2b-256 | 12db8a99c671cfcb54857bbc20dd928db13634b0ec50c5024640a9514b77017e |