diff --git a/README.md b/README.md index 0be1ee2a..408bbd58 100644 --- a/README.md +++ b/README.md @@ -117,6 +117,7 @@ http://localhost:8001/History/downloads.json?_shape=objects will return that dat -m, --metadata FILENAME Path to JSON file containing license/source metadata --template-dir DIRECTORY Path to directory containing custom templates + --plugins-dir DIRECTORY Path to directory containing custom plugins --static STATIC MOUNT mountpoint:path-to-directory for serving static files --help Show this message and exit. @@ -167,6 +168,7 @@ This will create a docker image containing both the datasette application and th --force Pass --force option to now --branch TEXT Install datasette from a GitHub branch e.g. master --template-dir DIRECTORY Path to directory containing custom templates + --plugins-dir DIRECTORY Path to directory containing custom plugins --static STATIC MOUNT mountpoint:path-to-directory for serving static files --title TEXT Title for metadata @@ -192,6 +194,7 @@ If you have docker installed you can use `datasette package` to create a new Doc --extra-options TEXT Extra options to pass to datasette serve --branch TEXT Install datasette from a GitHub branch e.g. master --template-dir DIRECTORY Path to directory containing custom templates + --plugins-dir DIRECTORY Path to directory containing custom plugins --static STATIC MOUNT mountpoint:path-to-directory for serving static files --title TEXT Title for metadata diff --git a/docs/getting_started.rst b/docs/getting_started.rst index ff32c67f..0a5178d7 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -110,6 +110,7 @@ datasette serve options -m, --metadata FILENAME Path to JSON file containing license/source metadata --template-dir DIRECTORY Path to directory containing custom templates + --plugins-dir DIRECTORY Path to directory containing custom plugins --static STATIC MOUNT mountpoint:path-to-directory for serving static files --help Show this message and exit. diff --git a/docs/index.rst b/docs/index.rst index 7f2b9269..20df03cd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,6 +21,7 @@ Contents sql_queries metadata custom_templates + plugins changelog .. _Zeit Now: https://zeit.co/now diff --git a/docs/plugins.rst b/docs/plugins.rst new file mode 100644 index 00000000..b0056793 --- /dev/null +++ b/docs/plugins.rst @@ -0,0 +1,156 @@ +Plugins +======= + +Datasette's plugin system is currently under active development. It allows +additional features to be implemented as Python code (or, soon, JavaScript) +which can be wrapped up in a separate Python package. + +You can follow the development of plugins in `issue #14 `_. + +Using plugins +------------- + +If a plugin has been packaged for distribution using setuptools you can use +the plugin by installing it alongside Datasette in the same virtual +environment or Docker container. + +You can also define one-off per-project plugins by saving them as +``plugin_name.py`` functions in a ``plugins/`` folder and then passing that +folder to ``datasette serve``. + +Writing plugins +--------------- + +The easiest way to write a plugin is to create a ``my_plugin.py`` file and +drop it into your ``plugins/`` directory. Here is an example plugin, which +adds a new custom SQL function called ``hello_world()`` which takes no +arguments and returns the string ``Hello world!``. + +.. code-block:: python + + from datasette import hookimpl + + @hookimpl + def prepare_connection(conn): + conn.create_function('hello_world', 0, lambda: 'Hello world!') + +If you save this in ``plugins/my_plugin.py`` you can then start Datasette like +this:: + + datasette serve mydb.db --plugins-dir=plugins/ + +Now you can navigate to http://localhost:8001/mydb and run this SQL:: + + select hello_world(); + +To see the output of your plugin. + +Packaging a plugin +------------------ + +Plugins can be packaged using Python setuptools. You can see an example of a +packaged plugin at https://github.com/simonw/datasette-plugin-demos + +The example consists of two files: a ``setup.py`` file that defines the plugin: + +.. code-block:: python + + from setuptools import setup + + VERSION = '0.1' + + setup( + name='datasette-plugin-demos', + description='Examples of plugins for Datasette', + author='Simon Willison', + url='https://github.com/simonw/datasette-plugin-demos', + license='Apache License, Version 2.0', + version=VERSION, + py_modules=['datasette_plugin_demos'], + entry_points={ + 'datasette': [ + 'plugin_demos = datasette_plugin_demos' + ] + }, + install_requires=['datasette'] + ) + +And a Python module file, ``datasette_plugin_demos.py``, that implements the +plugin: + +.. code-block:: python + + from datasette import hookimpl + import random + + + @hookimpl + def prepare_jinja2_environment(env): + env.filters['uppercase'] = lambda u: u.upper() + + + @hookimpl + def prepare_connection(conn): + conn.create_function('random_integer', 2, random.randint) + + +Having built a plugin in this way you can turn it into an installable package +using the following command:: + + python3 setup.py sdist + +This will create a ``.tar.gz`` file in the ``dist/`` directory. + +You can then install your new plugin into a Datasette virtual environment or +Docker container using ``pip``:: + + pip install datasette-plugin-demos-0.1.tar.gz + +To learn how to upload your plugin to `PyPI `_ for use by +other people, read the PyPA guide to `Packaging and distributing projects +`_. + + +Plugin hooks +------------ + +Datasette will eventually have many more plugin hooks. You can track and +contribute to their development in `issue #14 +`_. + +prepare_connection(conn) +~~~~~~~~~~~~~~~~~~~~~~~~ + +This hook is called when a new SQLite database connection is created. You can +use it to `register custom SQL functions `_, +aggregates and collations. For example: + +.. code-block:: python + + from datasette import hookimpl + import random + + @hookimpl + def prepare_connection(conn): + conn.create_function('random_integer', 2, random.randint) + +This registers a SQL function called ``random_integer`` which takes two +arguments and can be called like this:: + + select random_integer(1, 10); + +prepare_jinja2_environment(env) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This hook is called with the Jinja2 environment that is used to evaluate +Datasette HTML templates. You can use it to do things like `register custom +template filters `_, for +example: + +.. code-block:: python + + from datasette import hookimpl + + @hookimpl + def prepare_jinja2_environment(env): + env.filters['uppercase'] = lambda u: u.upper()