Skip to main content

An extension module for click to enable registering CLI commands via setuptools entry-points.

Project description

https://travis-ci.org/click-contrib/click-plugins.svg?branch=master https://coveralls.io/repos/click-contrib/click-plugins/badge.svg?branch=master&service=github

An extension module for click to register external CLI commands via setuptools entry-points.

Why?

Lets say you develop a commandline interface and someone requests a new feature that is absolutely related to your project but would have negative consequences like additional dependencies, major refactoring, or maybe its just too domain specific to be supported directly. Rather than developing a separate standalone utility you could offer up a setuptools entry point that allows others to use your commandline utility as a home for their related sub-commands. You get to choose where these sub-commands or sub-groups CAN be registered but the plugin developer gets to choose they ARE registered. You could have all plugins register alongside the core commands, in a special sub-group, across multiple sub-groups, or some combination.

Enabling Plugins

For a more detailed example see the examples section.

The only requirement is decorating click.group() with click_plugins.with_plugins() which handles attaching external commands and groups. In this case the core CLI developer registers CLI plugins from core_package.cli_plugins.

from pkg_resources import iter_entry_points

import click
from click_plugins import with_plugins


@with_plugins(iter_entry_points('core_package.cli_plugins'))
@click.group()
def cli():
    """Commandline interface for yourpackage."""

@cli.command()
def subcommand():
    """Subcommand that does something."""

Developing Plugins

Plugin developers need to register their sub-commands or sub-groups to an entry-point in their setup.py that is loaded by the core package.

from setuptools import setup

setup(
    name='yourscript',
    version='0.1',
    py_modules=['yourscript'],
    install_requires=[
        'click',
    ],
    entry_points='''
        [core_package.cli_plugins]
        cool_subcommand=yourscript.cli:cool_subcommand
        another_subcommand=yourscript.cli:another_subcommand
    ''',
)

Broken and Incompatible Plugins

Any sub-command or sub-group that cannot be loaded is caught and converted to a click_plugins.core.BrokenCommand() rather than just crashing the entire CLI. The short-help is converted to a warning message like:

Warning: could not load plugin. See ``<CLI> <command/group> --help``.

and if the sub-command or group is executed the entire traceback is printed.

Best Practices and Extra Credit

Opening a CLI to plugins encourages other developers to independently extend functionality independently but there is no guarantee these new features will be “on brand”. Plugin developers are almost certainly already using features in the core package the CLI belongs to so defining commonly used arguments and options in one place lets plugin developers reuse these flags to produce a more cohesive CLI. If the CLI is simple maybe just define them at the top of yourpackage/cli.py or for more complex packages something like yourpackage/cli/options.py. These common options need to be easy to find and be well documented so that plugin developers know what variable to give to their sub-command’s function and what object they can expect to receive. Don’t forget to document non-obvious callbacks.

Keep in mind that plugin developers also have access to the parent group’s ctx.obj, which is very useful for passing things like verbosity levels or config values around to sub-commands.

Here’s some code that sub-commands could re-use:

from multiprocessing import cpu_count

import click

jobs_opt = click.option(
    '-j', '--jobs', metavar='CORES', type=click.IntRange(min=1, max=cpu_count()), default=1,
    show_default=True, help="Process data across N cores."
)

Plugin developers can access this with:

import click
import parent_cli_package.cli.options


@click.command()
@parent_cli_package.cli.options.jobs_opt
def subcommand(jobs):
    """I do something domain specific."""

Installation

With pip:

$ pip install click-plugins

From source:

$ git clone https://github.com/click-contrib/click-plugins.git
$ cd click-plugins
$ python setup.py install

Developing

$ git clone https://github.com/click-contrib/click-plugins.git
$ cd click-plugins
$ pip install -e .\[dev\]
$ pytest tests --cov click_plugins --cov-report term-missing

Changelog

See CHANGES.txt

Authors

See AUTHORS.txt

License

See LICENSE.txt

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

click-plugins-1.0.4.tar.gz (8.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

click_plugins-1.0.4-py2.py3-none-any.whl (6.2 kB view details)

Uploaded Python 2Python 3

File details

Details for the file click-plugins-1.0.4.tar.gz.

File metadata

  • Download URL: click-plugins-1.0.4.tar.gz
  • Upload date:
  • Size: 8.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.11.0 pkginfo/1.4.2 requests/2.19.1 setuptools/40.2.0 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/3.7.0

File hashes

Hashes for click-plugins-1.0.4.tar.gz
Algorithm Hash digest
SHA256 dfed74b5063546a137de99baaaf742b4de4337ad2b3e1df5ec7c8a256adc0847
MD5 3db73ba58271e3d9644be9b9c03a9d8d
BLAKE2b-256 09302e963988d037b0f400753e958da88c1eecf3739aba6e532d863f4e94d137

See more details on using hashes here.

File details

Details for the file click_plugins-1.0.4-py2.py3-none-any.whl.

File metadata

  • Download URL: click_plugins-1.0.4-py2.py3-none-any.whl
  • Upload date:
  • Size: 6.2 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.11.0 pkginfo/1.4.2 requests/2.19.1 setuptools/40.2.0 requests-toolbelt/0.8.0 tqdm/4.26.0 CPython/3.7.0

File hashes

Hashes for click_plugins-1.0.4-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 b1ee1ccc9421c73007fe290680d97984eb6eaf5f4512b7620c6aa46031d6cb6b
MD5 df99f9d23dc509b3f45b4eb3ce2399d7
BLAKE2b-256 95ddfef84cf1678418f241ef542c0288bdf215bdd3e35f1fe03dc5223a2e80ba

See more details on using hashes here.

Supported by

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