Skip to main content

Turns your Python script or module into an application with decent CLI.

Project description

Turns your Python script or module into an application with decent CLI.

Motivation

Kickoff is inspired by utilities like invoke, fire, runfile. It has similar function with this difference that it unterstands Python3 syntax, therefore doesn’t need from you to use decorators or any dedicated API. This way kickoff can be applied to pretty much any script with zero overhead.

Kickoff is built on top of stunning click module. This lets you build beautiful CLI in seconds without making any effort.

Installation

As typically:

pip install kickoff

Quick Start

Let’s say you have this simple hello.py script:

"""Very simple application"""

def greet(name="World", *, greeting="Hello"):
    """Say hello"""

    print(f"{greeting} {name}!")

All you need to do is invoking you script with kickoff command. Kickoff will execute the script and examine top level namespace to collect your functions and then turn them into commands:

$ kickoff hello.py
Usage: hello.py [OPTIONS] COMMAND [ARGS]...

  Very simple application

Options:
  --help  Show this message and exit.

Commands:
  greet  Say hello
$ kickoff hello.py greet --help
Usage: hello.py greet [OPTIONS] [NAME]

  Say hello

Options:
  --greeting TEXT  [default: Hello]
  --help           Show this message and exit.
$ kickoff hello.py greet
Hello World!
$ kickoff hello.py greet John --greeting Hi
Hi John!

The same can be achieved by using module name and location. For intstance this is how you can test findall function from re module:

$ kickoff :re findall "b\w*d" "beer bear bird bore beard"
['bird', 'beard']

Invoke kickoff without any parameters to get more explanations.

Usage

Basics

There is very simmilar logic behind how Python3 handles arguments of the callables and how typical CLI does. This fact makes kickoff able to translate signatures of your functions into corresponding CLI commands. Docstrings in the code can be used for providing descriptions. As an example, see ex01_simple. Table below summarizes relationship explained above.

Function Argument

Command Line

None

None

foobar()

foobar

Argument without default value

Required parameter

foobar(qux)

foobar <QUX>

Argument with default value

Optional parameter

foobar(qux=123)

foobar [<QUX>]

Argument with default value of boolean type

Flag

foobar(*, qux=False)

foobar [--qux]

Keyword-only argument without default value

Required option

foobar(*, qux)

foobar --qux <QUX>

Keyword-only argument with default value

Optional option

foobar(*, qux=123)

foobar [--qux <QUX>]

Non-keyworded variable-length argument list

Multi-parameter

foobar(*args)

foobar <ARGS> ...

Keyworded variable-length argument list

Ignored

foobar(**kwargs)

foobar

In addition, annotations can be used to specify details which cannot be distinguised from Python syntax. See ex02_args_and_opts. Table below shows couple of practical examples.

Function Argument

Command Line

“required” speifier & variable-length argument list

Optional multi-parameter

foobar(*qux: dict(required=False))

foobar [<ARGS> ...]

“multiple” specifier

Multi-option

foobar(*, qux: dict(multiple=True)

foobar --qux <QUX> ...

“multiple” & “required” specifiers

Optional multi-option

foobar(*, qux: dict(multiple=True, required=False))

foobar [--qux <QUX> ...]

“count” specifier

Counting flag

foobar(*, qux: dict(count=True) )

foobar --qux ...

“count” specifier & default value

Optional counting flag

foobar(*, qux: dict(count=True) =0 )

foobar [--qux ...]

Hierarhical Desigh

Kickoff recursively traverses across the module of your choice to find functions and classes. Functions become commands. Classes are interpreted as command groups. This way you can arrange your commands in a hierarhical structure as presented in ex03_command_groups.

By default only those functions which have their body defined in in given module can become commands. This prevents form exposing of utility functions that the script may import from other modules for internal use. Also functions and classes names of which start with underscore are ignored.

Despite of this fact, it is possible to create a desing which is divided into multiple files. To accept external code in the top level module, relevanot option must be explicitly set. This topic will be covered further. For now, here is an example of what we have discussed: ex04_distributed_design.

Following table explains the details on how kickoff translates elements of Python AST into components of click module.

Python AST

Click

Function or static method

Command

Function argument

Parameter or option

Class

Command group

Return value annotation

Command settings

Argument annotation

Parameter/option settings

Function docstring

Command description

Class docstring

Command group description

Module docstring

Application description

Annotations are expected to be dictionaries. Values of argument annotations, depending of the context, are used as arguments to either click.Option or click.Argument class. Additionally alias can be used to specify short name of an option.

Values of return annotation are used as arguments to click.Command class.

Customization

Kickoff provides a way of fine tuning specific settings through kickoff.config data structure. It is recommended to do this in a code which is conditionally unavailable. This is how we can keep the module reusable in environments where kickoff is not installed. For example:

if __name__ == "__kickoff__":
    import kickoff
    kickoff.config.prog_name = "demo"
    kickoff.config.version_option = dict(version='1.2.3')
    kickoff.config.help_option_names = ['-h', '--help']

Available options can be found in the table:

Option

Default Value

Description

accept_imported

False

Inspect entire content of given module, not only functions and classes defined locally.

scan_recursively

True

Inspect classes (allows for grouping commands).

result_file

sys.stderr

Where to print stringified values returned by commands. Use /dev/null if you want to suppress this.

black_list

[]

Functions and classes to be explicitely skipped in the inspection process.

error_handler

kickoff.default_error_handler

Function to be applied to the exceptions of expected_error_cls type when raised.

expected_error_cls

kickoff.ExpectedError

Type of the errors to be presented without traceback.

usage_error_cls

click.UsageError

Type of the errors to be presented with context help.

prog_name

None

Name of the application to be used in context help.

version_option

None

Dictionary to be unpacked to the arguments of click.version_option.

help_option_names

None

Dictionary to be unpacked to the arguments of click.help_option.

Corresponding example: ex05_customization.

Error Handling

By default, exceptions raised from the commands are not handled by kickoff. Typically this results in a traceback printed to stderr. In order to hide this from the eyes of the users, we may raise exceptions that inherit from kickoff.ExpectedError, or any other class registered in config.expected_error_cls. This kind of exceptions will be stringified and handled by kickoff.default_error_handler. By default this handler writes the error message to stderr. Traceback is not shown, unless KICKOFF_DEV_MODE environment variable is set to non-zero.

Even though click provides ways of validating users’ input, one may want to indicate improper input only in the code of his command. This can be achieved by raising an exceptions which inherits from click.UsageError or any other class registered in config.usage_error_cls. As a result, we get our exception stringified next to the context help produced by click.

Following example demonstrates functionality described above: ex06_error_handling.

References

Tips

  • ptrepl or similar tools can be used to provide your CLI in a form of REPL. For example: ptrepl --prompt demo "kickoff demo.py"

  • kickoff used in shebang will let the users run your script as any other executable binary. Remember to add executable attribute: chmod +x somescript.py.

  • Using .py extension of your script is not required.

  • Set KICKOFF_DEV_MODE=1 environment variable to force all the traceback appear on stderr.

  • kickoffcustomize.py file is loaded at the very beginning. Crease this file in your CWD if you need to perform any early configuration. Example can be found here.

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

kickoff-0.1.0.tar.gz (15.5 kB view details)

Uploaded Source

Built Distributions

kickoff-0.1.0-py3.7.egg (24.9 kB view details)

Uploaded Egg

kickoff-0.1.0-py3-none-any.whl (13.3 kB view details)

Uploaded Python 3

File details

Details for the file kickoff-0.1.0.tar.gz.

File metadata

  • Download URL: kickoff-0.1.0.tar.gz
  • Upload date:
  • Size: 15.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.1

File hashes

Hashes for kickoff-0.1.0.tar.gz
Algorithm Hash digest
SHA256 4f6ff6e13d96c057044aba80a876f04e1735d0769733736065a547deaeee7d28
MD5 c0cbf0c986f194b5b98155d82f733e09
BLAKE2b-256 9e4d720b94fad0daa71a729fa2bf94239393c25977c71477c44f0fb635916218

See more details on using hashes here.

File details

Details for the file kickoff-0.1.0-py3.7.egg.

File metadata

  • Download URL: kickoff-0.1.0-py3.7.egg
  • Upload date:
  • Size: 24.9 kB
  • Tags: Egg
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.1

File hashes

Hashes for kickoff-0.1.0-py3.7.egg
Algorithm Hash digest
SHA256 b5dcd6d0da84c7496dfd79f0c72d654df97b819490ccaf6b34972f88743704f5
MD5 b7afc13b9c3c064462547b03737c7f43
BLAKE2b-256 07ae931d18dbdb6249c229dcf8085ba1afa34ad3363e072253ea73c1d2ba32a6

See more details on using hashes here.

File details

Details for the file kickoff-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: kickoff-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.6.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.7.1

File hashes

Hashes for kickoff-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ce7cf337885c7159284efcff9b08bb1938a6a768c68bc227ece0f659e2d3cb71
MD5 906a0967c1e6404a03ed51a3f630817f
BLAKE2b-256 4f00a15767800a5c5be35bbaaf1728b6ff05b81d8b2a84d60ea42b88c38b9390

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page