diff --git a/datasette/__init__.py b/datasette/__init__.py index 668a8c82..1ec88d90 100644 --- a/datasette/__init__.py +++ b/datasette/__init__.py @@ -1 +1,3 @@ from datasette.version import __version_info__, __version__ # noqa +from .hookspecs import hookimpl # noqa +from .hookspecs import hookspec # noqa diff --git a/datasette/app.py b/datasette/app.py index 3cd39b2f..c20b8275 100644 --- a/datasette/app.py +++ b/datasette/app.py @@ -17,6 +17,7 @@ import jinja2 import hashlib import time import pint +import pluggy import traceback from .utils import ( Filters, @@ -38,6 +39,7 @@ from .utils import ( urlsafe_components, validate_sql_select, ) +from . import hookspecs from .version import __version__ app_root = Path(__file__).parent.parent @@ -49,6 +51,11 @@ connections = threading.local() ureg = pint.UnitRegistry() +pm = pluggy.PluginManager('datasette') +pm.add_hookspecs(hookspecs) +pm.load_setuptools_entrypoints('datasette') + + class DatasetteError(Exception): def __init__(self, message, title=None, error_dict=None, status=500, template=None): self.message = message @@ -1100,6 +1107,7 @@ class Datasette: conn.enable_load_extension(True) for extension in self.sqlite_extensions: conn.execute("SELECT load_extension('{}')".format(extension)) + pm.hook.prepare_connection(conn=conn) def inspect(self): if not self._inspect: @@ -1226,6 +1234,7 @@ class Datasette: self.jinja_env.filters['quote_plus'] = lambda u: urllib.parse.quote_plus(u) self.jinja_env.filters['escape_sqlite'] = escape_sqlite self.jinja_env.filters['to_css_class'] = to_css_class + pm.hook.prepare_jinja2_environment(env=self.jinja_env) app.add_route(IndexView.as_view(self), '/') # TODO: /favicon.ico and /-/static/ deserve far-future cache expires app.add_route(favicon, '/favicon.ico') diff --git a/datasette/hookspecs.py b/datasette/hookspecs.py new file mode 100644 index 00000000..0e65a198 --- /dev/null +++ b/datasette/hookspecs.py @@ -0,0 +1,15 @@ +from pluggy import HookimplMarker +from pluggy import HookspecMarker + +hookspec = HookspecMarker('datasette') +hookimpl = HookimplMarker('datasette') + + +@hookspec +def prepare_connection(conn): + "Modify SQLite connection in some way e.g. register custom SQL functions" + + +@hookspec +def prepare_jinja2_environment(env): + "Modify Jinja2 template environment e.g. register custom template tags" diff --git a/setup.py b/setup.py index 362484c4..2071ac27 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ from setuptools import setup, find_packages -from datasette import __version__ +from datasette.version import __version__ import os @@ -28,7 +28,8 @@ setup( 'Sanic==0.7.0', 'Jinja2==2.10', 'hupper==1.0', - 'pint==0.8.1' + 'pint==0.8.1', + 'pluggy>=0.1.0,<1.0', ], entry_points=''' [console_scripts]