220 lines
6.3 KiB
Plaintext
220 lines
6.3 KiB
Plaintext
|
|
Metadata-Version: 2.4
|
||
|
|
Name: click-plugins
|
||
|
|
Version: 1.1.1.2
|
||
|
|
Summary: An extension module for click to enable registering CLI commands via setuptools entry-points.
|
||
|
|
Home-page: https://github.com/click-contrib/click-plugins
|
||
|
|
Author: Kevin Wurster, Sean Gillies
|
||
|
|
Author-email: wursterk@gmail.com, sean.gillies@gmail.com
|
||
|
|
License: New BSD
|
||
|
|
Keywords: click plugin setuptools entry-point
|
||
|
|
Classifier: Topic :: Utilities
|
||
|
|
Classifier: Intended Audience :: Developers
|
||
|
|
Classifier: Development Status :: 7 - Inactive
|
||
|
|
Classifier: License :: OSI Approved :: BSD License
|
||
|
|
Classifier: Programming Language :: Python
|
||
|
|
Classifier: Programming Language :: Python :: 3
|
||
|
|
License-File: LICENSE.txt
|
||
|
|
License-File: AUTHORS.txt
|
||
|
|
Requires-Dist: click>=4.0
|
||
|
|
Provides-Extra: dev
|
||
|
|
Requires-Dist: pytest>=3.6; extra == "dev"
|
||
|
|
Requires-Dist: pytest-cov; extra == "dev"
|
||
|
|
Requires-Dist: wheel; extra == "dev"
|
||
|
|
Requires-Dist: coveralls; extra == "dev"
|
||
|
|
Dynamic: author
|
||
|
|
Dynamic: author-email
|
||
|
|
Dynamic: classifier
|
||
|
|
Dynamic: description
|
||
|
|
Dynamic: home-page
|
||
|
|
Dynamic: keywords
|
||
|
|
Dynamic: license
|
||
|
|
Dynamic: license-file
|
||
|
|
Dynamic: provides-extra
|
||
|
|
Dynamic: requires-dist
|
||
|
|
Dynamic: summary
|
||
|
|
|
||
|
|
=============
|
||
|
|
click-plugins
|
||
|
|
=============
|
||
|
|
|
||
|
|
This PyPI package is no longer actively maintained, but the underlying
|
||
|
|
library can be vendored. See `homepage <https://github.com/click-contrib/click-plugins>`_
|
||
|
|
for more information.
|
||
|
|
|
||
|
|
An extension module for `click <https://github.com/pallets/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 <https://pythonhosted.org/setuptools/setuptools.html#dynamic-discovery-of-services-and-plugins>`_
|
||
|
|
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 <https://github.com/click-contrib/click-plugins/tree/master/example>`_ 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``.
|
||
|
|
|
||
|
|
.. code-block:: python
|
||
|
|
|
||
|
|
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.
|
||
|
|
|
||
|
|
.. code-block:: python
|
||
|
|
|
||
|
|
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:
|
||
|
|
|
||
|
|
.. code-block:: console
|
||
|
|
|
||
|
|
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:
|
||
|
|
|
||
|
|
.. code-block:: python
|
||
|
|
|
||
|
|
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:
|
||
|
|
|
||
|
|
.. code-block:: python
|
||
|
|
|
||
|
|
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``:
|
||
|
|
|
||
|
|
.. code-block:: console
|
||
|
|
|
||
|
|
$ pip install click-plugins
|
||
|
|
|
||
|
|
From source:
|
||
|
|
|
||
|
|
.. code-block:: console
|
||
|
|
|
||
|
|
$ git clone https://github.com/click-contrib/click-plugins.git
|
||
|
|
$ cd click-plugins
|
||
|
|
$ python setup.py install
|
||
|
|
|
||
|
|
|
||
|
|
Developing
|
||
|
|
----------
|
||
|
|
|
||
|
|
.. code-block:: console
|
||
|
|
|
||
|
|
$ 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``
|