File: README.rst

package info (click to toggle)
python-click-plugins 1.1.1.2-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 204 kB
  • sloc: python: 308; makefile: 8
file content (184 lines) | stat: -rw-r--r-- 5,340 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
=============
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``