Introduction
Quickie is a simple task runner, inspired on tools like celery, cargo-make, Task, and invoke. It aims to be simple to use, easy to extend, and to provide a good experience for developers and teams.
Unlike other task runners that define tasks in YAML, TOML or specialized formats, Quickie uses the Python programming language directly, leveraging the power of the language and the ecosystem around it. This means for example, that syntax highlighting, errors, and auto completion in most code editors will work out of the box.
Quickie is not limited to Python projects, you can for example define a virtual environment with Quickie and other dependencies alongside a project that is not using Python at all, similar to how make can be used in non C/C++ projects.
Some features include:
Run python, shell scripts, and subprocesses as tasks.
Powerful arguments parsing, by wrapping argparse.
Autocompletion both for the CLI and tasks, thanks to argcomplete.
Custom autocompletion for task arguments.
Conditions to run tasks only if certain conditions are met.
Dependencies between tasks.
Namespaces to organize tasks.
Requirements
Quickie has been tested on macOs, but should work on Linux and Windows as well. If you find any issues, please open an issue on GitHub.
Per Project Installation
The recommended way to use Quickie is to install it per-project in a virtual environment. This allows each project to pin its own version of Quickie and avoids version conflicts across projects.
With automatic launcher (recommended):
Once you have Quickie installed in a project virtual environment, you can run tasks from anywhere without manual virtual environment activation. The global qk command will automatically discover your project and delegate to the project-specific version:
cd my-project
python -m venv .venv
.venv/bin/pip install quickie-runner
qk task-name # Automatically uses project-specific quickie!
The launcher will search for a _qk directory or _qk.py file starting from your current directory and traveling up the directory tree. Once found, it delegates to the quickie installation in that project’s virtual environment.
Traditional manual activation (still supported):
If preferred, you can still manually activate the virtual environment:
cd my-project
python -m venv .venv
source .venv/bin/activate
pip install quickie-runner
qk --help
Global installation
While Quickie allows you to run tasks defined within a project, sometimes it is useful to have global tasks accessible from anywhere. Install quickie-runner globally (e.g. with pipx) and use the --global (or -g) flag:
qk --global task-name
Global tasks are stored in ~/_qkg and are only used when explicitly requested with --global (or -g).
Installation with pipx (recommended for global tools):
pipx install quickie-runner
qk --global task-name
Tip
If installing via pipx and you need to add extra dependencies, you can inject them:
pipx inject quickie-runner my-extra-dependency
Upgrading
You can also upgrade via pip:
pip install --upgrade quickie-runner
Or pipx:
pipx upgrade quickie-runner
Auto completion
Quickie provides auto completion for tasks and arguments via the argcomplete package.
To enable it, you need to install argcomplete globally and add the following line to your shell configuration file:
eval "$(register-python-argcomplete qk)"
This is sufficient for both project tasks and global tasks. When tab completing inside a project directory, the smart launcher delegates to the project-local quickie installation, so completions reflect that project’s tasks and version.
You can also call qk --autocomplete bash or qk --autocomplete zsh for instructions on how to enable auto completion for your shell.
Quick(ie)start
Defining tasks
Tasks can be defined in a _qk Python module, be it a single file or a package, usually at the
root of the project. For global tasks they can be defined in the same way at ~/_qkg. They can also
be defined at an arbitrary Python module, and passed to the runner using the --module or -m argument.
For example:
# MyProject/_qk.py
from quickie import Arg, task, script, command
@task
def hello():
print("Hello, World!")
@script(
args=[
Arg("--name", help="Your name"),
],
)
def hello_script(name):
return f"echo 'Hello, {name}!'"
@command(extra_args=True)
def some_command(*args):
return ["my_command", *args]
Now you can run the tasks from anywhere in the project, even from a subdirectory.
$ qk hello
Hello, World!
$ qk hello-script --name Alice
Hello, Alice!
$ qk some-command arg1 arg2
my_command arg1 arg2
Defining tasks in a package
For more complex projects, or teams, it is recommended to define tasks in a package. This allows to better organize the tasks and to have private tasks that are not committed to the repository.
For example:
MyProject/
├── _qk
│ ├── __init__.py
│ ├── public.py
│ ├── private.py # might not exist
│ └── ... # more files
└── ...
Then in the __init__.py file you can import the tasks from the other files.
# MyProject/_qk/__init__.py
from quickie import namespace
from . import public
@namespace
def _():
tasks = [public]
try:
from . import private
tasks.append(private)
return {"": tasks, "private": [private]}
except ImportError:
return tasks
Because @namespace is lazy, the optional private module is only imported when Quickie
actually needs one of the tasks in this namespace.
For most of of the documentation, we will assume tasks are defined in a package.