https://github.com/harrybrwn/dispatch
A simple, low redundancy cli framework for python.
https://github.com/harrybrwn/dispatch
Last synced: 2 months ago
JSON representation
A simple, low redundancy cli framework for python.
- Host: GitHub
- URL: https://github.com/harrybrwn/dispatch
- Owner: harrybrwn
- License: apache-2.0
- Created: 2019-12-05T05:20:41.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2020-03-28T06:49:49.000Z (about 5 years ago)
- Last Synced: 2025-01-04T11:09:01.513Z (4 months ago)
- Language: Python
- Homepage:
- Size: 151 KB
- Stars: 0
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# dispatch
[](https://pypi.python.org/pypi/py-dispatch)A low information-redundancy CLI framework for a quick and dirty way of converting python a script to a command line tool.
The idea behind this framework is that functions and classes have one-to-one correspondence to command line interfaces with functions as commands and arguments as flags. This framework allows users to define a function or a class in plain python and turn it into a CLI with one decorator.
This was inspired by [fire](https://github.com/google/python-fire) and [click](https://click.palletsprojects.com/).
## Install
```
pip install py-dispatch
```## Demo
[docs.python]: # (grep -Pzo "[[?s]]# example\.py.*[[?=#end example\.py]]" example.py | tr -d '\0')
```python
# example.py
import sys
from dispatch import command@command
def hello(name: str, verbose: bool, debug: bool, file: str = 'stdout'):
'''Run the 'hello' command line interface.:v verbose: Run the command verbosly
:name: Name of the person you are saying hello to.
:file: Either stdout or stderr
'''
if debug:
print(f'debugging with {name}', file=getattr(sys, file))
else:
print(f'hello, {name}', file=getattr(sys, file))if __name__ == '__main__':
hello()
``````
$ python example.py --help
```[docs]: # (python example.py --help)
```
Run the 'hello' command line interface.Usage:
hello [options]Options:
--name Name of the person you are saying hello to.
-v, --verbose Run the command verbosly
--debug
--file Either stdout or stderr (default: 'stdout')
-h, --help Get help.
```Arguments
---------
Arguments can be retrieved in two ways, either from Command.args or with positional only arguments. When a cli function is run, it is replaced with a Command object so the cli function can use the command in it's own body.
```python
@dispatch.command
def cli(verbose: bool):
print(cli.args)
```
Running this cli with `python cli.py hello --verbose these are some args` will result in `['hello', 'these', 'are', 'some', 'args']`.The Other way to get arguments is to give the cli function a positional only argument at the beginning of the parameters list.
```python
@dispatch.command
def cli(*args, verbose: bool):
print(args)
```
Running this cli as before will have the same result. However, it only works when the args tuple is the first function parameter.Properties of Flags
===================
Because flags are specified by function arguments, the properties of flags are a little bit weird.Boolean Flags
-------------
All boolean flags have a default of `False`.A positional argument with no default and no type annotation is assumed to be a boolean flag and will default to a value of `False`.
```python
@disptch.command
def cli(verbose):
if verbose:
print('the verbose flag has been given')
else:
print('using default of False for verbose')
```Flag Types
----------
Dispatch uses type annotations to infer flag types and will use those annotations to convert the arguments given.
```python
@dispatch.command
def cli(num: complex, decimal: float):
pass
```
When the program `cli.py` is executed it will convert each argument to its type.
```bash
python cli.py --num=5+3j --decimal=5.9
```
For this command, the parser internals will eventually call `complex('5+3j')` and `float('5.9')` before giving the values as function arguments.
What this means is that you can use any type as long it has an `__init__` function that takes one argument. If a flag is given a default value and no type annotation, the flag will inherit whatever type is given as default.Default Values
--------------
```python
@dispatch.command(hidden_defaults={'one', 'two'})
def cli(one=1, two=2, other_stuff=None): pass
```
To specify a default value, simply set the function argument as you would with regular python code. The default values will be displayed in the help view of the command unless a `set` of type `str` is passed to the decorator as the 'hidden_defaults' argument. This will hide any default values from the help message. Falsy defaults that are non-boolean like an empty string will also be hidden.Multiple Commands
=================
For more complicated command line interfaces, you need more than just flags. To do this you simply use the same `command` decorator on a class instead of a function.[docs.python]: # (cat multicommand.py)
```python
# multicommand.py
from dispatch import command@command
class multicommand:
''':v verbose: print stuff verbosly'''verbose: bool
filename = 'README.md'def cat(self, file: str):
'''Print a file:f file: print this file'''
if self.verbose:
print('doing the thing verbosly')
print(open(file, 'r').read())def do(self, thing):
'''Do a thing:thing: the thing you will be doing'''
print('doing', thing)def _helper(self):
'''functions that start with an underscore are not
interpreted as commands (not even hidden commands).
'''
passif __name__ == "__main__":
multicommand()
```This small program is used just like any other cli.
```bash
python multicommand.py --help
```[docs]: # (python multicommand.py --help)
```
Usage:
multicommand [options] [command]Commands:
cat Print a file
do Do a thingOptions:
--filename default: 'README.md'
-v, --verbose print stuff verbosly
-h, --help Get help.
```Group Flags
------------
You can define flags that are used at a class level and can be accessed by all commands in the cli. To do this you can set a class variable or you can use a type annotation if you do not want the flag to have a default value. When retrieving the value of these flags you can simply use them like any class attribute with `self.attr_name`.
```python
@dispatch.command
class cli:
verbose: bool
filename = 'notes.txt'
```Command Aliases
---------------
Aliasing commands is really simple. All you need to do is assign the function you want to alias to another variable inside the class.
```python
@dispatch.command
class cli:
def command(self):
'''This is a command.'''
passcmd = command
``````
Usage:
cli [options] [command]Options:
-h, --help Get help.Commands:
command, cmd This is a command.
```