-
Notifications
You must be signed in to change notification settings - Fork 354
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #127 from yuvipanda/pluggy
Add plugin support to the installer
- Loading branch information
Showing
11 changed files
with
369 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
.. _contributing/plugins: | ||
|
||
============ | ||
TLJH Plugins | ||
============ | ||
|
||
TLJH plugins are the official way to make customized 'spins' or 'stacks' | ||
with TLJH as the base. For example, the earth sciences community can make | ||
a plugin that installs commonly used packages, set up authentication | ||
and pre-download useful datasets. The mybinder.org community can | ||
make a plugin that gives you a single-node, single-repository mybinder.org. | ||
Plugins are very powerful, so the possibilities are endless. | ||
|
||
Design | ||
====== | ||
|
||
`pluggy <https://github.com/pytest-dev/pluggy>`_ is used to implement | ||
plugin functionality. TLJH exposes specific **hooks** that your plugin | ||
can provide implementations for. This allows us to have specific hook | ||
points in the application that can be explicitly extended by plugins, | ||
balancing the need to change TLJH internals in the future with the | ||
stability required for a good plugin ecosystem. | ||
|
||
Writing a simple plugins | ||
======================== | ||
|
||
We shall try to write a simple plugin that installs a few libraries, | ||
and use it to explain how the plugin mechanism works. We shall call | ||
this plugin ``tljh-simple``. | ||
|
||
Plugin directory layout | ||
----------------------- | ||
|
||
We recommend creating a new git repo for your plugin. Plugins are | ||
normal python packages - however, since they are usually simpler, | ||
we recommend they live in one file. | ||
|
||
For ``tljh-simple``, the repository's structure should look like: | ||
|
||
.. code-block:: none | ||
tljh_simple: | ||
- tljh_simple.py | ||
- setup.py | ||
- README.md | ||
- LICENSE | ||
The ``README.md`` (or ``README.rst`` file) contains human readable | ||
information about what your plugin does for your users. ``LICENSE`` | ||
specifies the license used by your plugin - we recommend the | ||
3-Clause BSD License, since that is what is used by TLJH itself. | ||
|
||
``setup.py`` - metadata & registration | ||
-------------------------------------- | ||
|
||
``setup.py`` marks this as a python package, and contains metadata | ||
about the package itself. It should look something like: | ||
|
||
.. code-block:: python | ||
from setuptools import setup | ||
setup( | ||
name="tljh-simple", | ||
author="YuviPanda", | ||
version="0.1", | ||
license="3-clause BSD", | ||
url='https://github.com/yuvipanda/tljh-simple', | ||
entry_points={"tljh": ["simple = tljh_simple"]}, | ||
py_modules=["tljh_simple"], | ||
) | ||
This is a mostly standard ``setup.py`` file. ``entry_points={"tljh": ["simple = tljh_simple]}`` | ||
'registers' the module ``tljh_simple`` (in file ``tljh_simple.py``) with TLJH as a plugin. | ||
|
||
``tljh_simple.py`` - implementation | ||
----------------------------------- | ||
|
||
In ``tljh_simple.py``, you provide implementations for whichever hooks | ||
you want to extend. | ||
|
||
A hook implementation is a function that has the following characteristics: | ||
|
||
#. Has same name as the hook | ||
#. Accepts some or all of the parameters defined for the hook | ||
#. Is decorated with the ``hookimpl`` decorator function, imported from | ||
``tljh.hooks``. | ||
|
||
The current list of available hooks and when they are called can be | ||
seen in ```tljh/hooks.py`` <https://github.com/jupyterhub/the-littlest-jupyterhub/blob/master/tljh/hooks.py>`_ | ||
in the source repository. | ||
|
||
|
||
This example provides an implementation for the ``tljh_extra_user_conda_packages`` | ||
hook, which can return a list of conda packages that'll be installed in users' | ||
environment from conda-forge. | ||
|
||
.. code-block:: python | ||
from tljh.hooks import hookimpl | ||
@hookimpl | ||
def tljh_extra_user_conda_packages(): | ||
return [ | ||
'xarray', | ||
'iris', | ||
'dask', | ||
] | ||
Publishing plugins | ||
================== | ||
|
||
Plugins are python packages and should be published on PyPI. Users | ||
can also install them directly from GitHub - although this is | ||
not good long term practice. | ||
|
||
The python package should be named ``tljh-<pluginname>``. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,3 +56,32 @@ will fail. | |
|
||
When pointing to a file on GitHub, make sure to use the 'Raw' version. It should point to | ||
``raw.githubusercontent.com``, not ``github.com``. | ||
|
||
Installing TLJH plugins | ||
======================= | ||
|
||
The Littlest JupyterHub can install additional *plugins* that provide additional | ||
features. They are most commonly used to install a particular *stack* - such as | ||
the `PANGEO Stack <https://github.com/yuvipanda/tljh-pangeo>`_ for earth sciences | ||
research, a stack for a praticular class, etc. | ||
|
||
``--plugin <plugin-to-install>`` installs and activates a plugin. You can pass it | ||
however many times you want. Since plugins are distributed as python packages, | ||
``<plugin-to-install>`` can be anything that can be passed to ``pip install`` - | ||
``plugin-name-on-pypy==<version>`` and ``git+https://github.com/user/repo@tag`` | ||
are the most popular ones. Specifying a version or tag is highly recommended. | ||
|
||
For example, to install the PANGEO Plugin version 0.1 in your new TLJH install, | ||
you would use: | ||
|
||
.. code-block:: bash | ||
curl https://raw.githubusercontent.com/jupyterhub/the-littlest-jupyterhub/master/bootstrap/bootstrap.py \ | ||
| sudo python3 - \ | ||
--plugin git+https://github.com/yuvipanda/[email protected] | ||
.. note:: | ||
|
||
Plugins are extremely powerful and can do a large number of arbitrary things. | ||
Only install plugins you trust. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
from setuptools import setup | ||
|
||
setup( | ||
name="tljh-simplest", | ||
entry_points={"tljh": ["simplest = tljh_simplest"]}, | ||
py_modules=["tljh_simplest"], | ||
) | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
""" | ||
Simplest plugin that excercises all the hooks | ||
""" | ||
from tljh.hooks import hookimpl | ||
|
||
|
||
@hookimpl | ||
def tljh_extra_user_conda_packages(): | ||
return [ | ||
'hypothesis', | ||
] | ||
|
||
|
||
@hookimpl | ||
def tljh_extra_user_pip_packages(): | ||
return [ | ||
'django', | ||
] | ||
|
||
|
||
@hookimpl | ||
def tljh_extra_apt_packages(): | ||
return [ | ||
'sl', | ||
] | ||
|
||
|
||
@hookimpl | ||
def tljh_config_post_install(config): | ||
# Put an arbitrary marker we can test for | ||
config['simplest_plugin'] = { | ||
'present': True | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
""" | ||
Test simplest plugin | ||
""" | ||
from ruamel.yaml import YAML | ||
import os | ||
import subprocess | ||
|
||
yaml = YAML(typ='rt') | ||
|
||
|
||
def test_apt_packages(): | ||
""" | ||
Test extra apt packages are installed | ||
""" | ||
assert os.path.exists('/usr/games/sl') | ||
|
||
|
||
def test_pip_packages(): | ||
""" | ||
Test extra user pip packages are installed | ||
""" | ||
subprocess.check_call([ | ||
'/opt/tljh/user/bin/python3', | ||
'-c', | ||
'import django' | ||
]) | ||
|
||
|
||
def test_conda_packages(): | ||
""" | ||
Test extra user conda packages are installed | ||
""" | ||
subprocess.check_call([ | ||
'/opt/tljh/user/bin/python3', | ||
'-c', | ||
'import hypothesis' | ||
]) | ||
|
||
|
||
def test_config_hook(): | ||
""" | ||
Check config changes are present | ||
""" | ||
with open('/opt/tljh/config.yaml') as f: | ||
data = yaml.load(f) | ||
|
||
assert data['simplest_plugin']['present'] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
""" | ||
Hook specifications that pluggy plugins can override | ||
""" | ||
import pluggy | ||
|
||
hookspec = pluggy.HookspecMarker('tljh') | ||
hookimpl = pluggy.HookimplMarker('tljh') | ||
|
||
|
||
@hookspec | ||
def tljh_extra_user_conda_packages(): | ||
""" | ||
Return list of extra conda packages to install in user environment. | ||
""" | ||
pass | ||
|
||
|
||
@hookspec | ||
def tljh_extra_user_pip_packages(): | ||
""" | ||
Return list of extra pip packages to install in user environment. | ||
""" | ||
pass | ||
|
||
|
||
@hookspec | ||
def tljh_extra_apt_packages(): | ||
""" | ||
Return list of extra apt packages to install in the user environment. | ||
These will be installed before additional pip or conda packages. | ||
""" | ||
pass | ||
|
||
|
||
@hookspec | ||
def tljh_config_post_install(config): | ||
""" | ||
Modify on-disk tljh-config after installation. | ||
config is a dict-like object that should be modified | ||
in-place. The contents of the on-disk config.yaml will | ||
be the serialized contents of config, so try to not | ||
overwrite anything the user might have explicitly set. | ||
""" | ||
pass |
Oops, something went wrong.