A simple task runner.
Project description
Duty
A simple task runner.
Inspired by Invoke.
Requirements
Duty requires Python 3.6 or above.
To install Python 3.6, I recommend using pyenv
.
# install pyenv
git clone https://github.com/pyenv/pyenv ~/.pyenv
# setup pyenv (you should also put these three lines in .bashrc or similar)
export PATH="${HOME}/.pyenv/bin:${PATH}"
export PYENV_ROOT="${HOME}/.pyenv"
eval "$(pyenv init -)"
# install Python 3.6
pyenv install 3.6.12
# make it available globally
pyenv global system 3.6.12
Installation
With pip
:
python3.6 -m pip install duty
With pipx
:
python3.6 -m pip install --user pipx
pipx install --python python3.6 duty
Quick start
Proper documentation pages will soon be available.
Configuration
Create a duties.py
file at the root of your repository.
Each task is declared as a "duty", using the duty.duty
decorator.
from duty import duty
@duty
def docs(ctx):
ctx.run("mkdocs build", title="Building documentation")
The ctx
argument is the "context" of the duty.
It is automatically created and passed to your function.
It has only one purpose: running command with its run
method.
The run
method accepts strings, list of strings, or even Python callables.
The above duty can be rewritten as:
from duty import duty
@duty
def docs(ctx):
ctx.run(["mkdocs", "build"], title="Building documentation")
# avoid the overhead of an extra shell process
Or:
from duty import duty
from mkdocs import build, config
@duty
def docs(ctx):
ctx.run(build.build, args=[config.load_config()], title="Building documentation")
# avoid the overhead of an extra Python process
The run
methods accepts various options,
mostly coming from its underlying dependency:
failprint
.
Arguments of the run
method:
Name | Type | Description | Default |
---|---|---|---|
cmd | str , list of str , or Python callable |
The command to run. | required |
args | list |
Arguments to pass to the callable. | [] |
kwargs | dict |
Keyword arguments to pass to the callable. | {} |
number | int |
The command number (useful for the tap format). |
None |
output_type | str |
The type of output: stdout , stderr , combine or nocapture |
combine |
title | str |
The command title. | cmd as a shell command or Python statement |
fmt | str |
The output format as a Jinja template: pretty , tap or custom=... |
pretty |
pty | bool |
Whether to run in a PTY. | False |
progress | bool |
Whether to show progress. | True |
nofail | bool |
Whether to always succeed. | False |
quiet | bool |
Don't print the command output, even if it failed. | False |
silent | bool |
Don't print anything. | False |
Example usage of the silent
option:
@duty
def clean(ctx):
ctx.run("find . -type d -name __pycache__ | xargs rm -rf", silent=True)
Now let's say you have more than one command, and you want to silence all of them:
@duty(silent=True)
def clean(ctx):
ctx.run("rm -rf .coverage*")
ctx.run("rm -rf .mypy_cache")
ctx.run("rm -rf .pytest_cache")
ctx.run("rm -rf build")
ctx.run("rm -rf dist")
ctx.run("rm -rf pip-wheel-metadata")
ctx.run("rm -rf site")
ctx.run("find . -type d -name __pycache__ | xargs rm -rf")
ctx.run("find . -name '*.rej' -delete")
Run
To run a duty, simply use:
duty clean
If you are using Poetry:
poetry run duty clean
You can pass multiple duties in one command:
duty clean docs
If one of your duties accept arguments, you can pass them on the command line as well:
@duty
def docs(ctx, serve=False):
command = "serve" if serve else "build"
ctx.run(f"mkdocs {command}")
duty docs serve=1
!!! note Note that arguments are not type-casted: they are always passed as strings to the duties.
Todo
- CLI
--list
option, to list the available duties and their description. - Aliases. I think I will replace
_
by-
when registering a duty, and add the original name as an alias. - Better handling of missing duties arguments.
Maybe simply print the error without a traceback:
release() missing 1 required positional argument: 'version'
- Arguments type casting, ideally based on type annotations!
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.