Added plugin_config() method

This commit is contained in:
Simon Willison 2018-08-28 01:35:21 -07:00
commit 0a14a4846b
No known key found for this signature in database
GPG key ID: 17E2DEA2588B7F52
6 changed files with 114 additions and 6 deletions

View file

@ -170,7 +170,7 @@ class Datasette:
def metadata(self, key=None, database=None, table=None, fallback=True):
"""
Looks up metadata, cascading backwards from specified level.
Returns None if metadata value is not foundself.
Returns None if metadata value is not found.
"""
assert not (database is None and table is not None), \
"Cannot call metadata() with table= specified but not database="
@ -199,6 +199,17 @@ class Datasette:
m.update(item)
return m
def plugin_config(
self, plugin_name, database=None, table=None, fallback=True
):
"Return config for plugin, falling back from specified database/table"
plugins = self.metadata(
"plugins", database=database, table=table, fallback=fallback
)
if plugins is None:
return None
return plugins.get(plugin_name)
def app_css_hash(self):
if not hasattr(self, "_app_css_hash"):
self._app_css_hash = hashlib.sha1(

View file

@ -1,8 +1,8 @@
from pluggy import HookimplMarker
from pluggy import HookspecMarker
hookspec = HookspecMarker('datasette')
hookimpl = HookimplMarker('datasette')
hookspec = HookspecMarker("datasette")
hookimpl = HookimplMarker("datasette")
@hookspec

View file

@ -149,6 +149,71 @@ The priority order for template loading is:
See :ref:`customization` for more details on how to write custom templates,
including which filenames to use to customize which parts of the Datasette UI.
Plugin configuration
--------------------
Plugins can have their own configuration, embedded in a :ref:`metadata` file. Configuration options for plugins live within a ``"plugins"`` key in that file, which can be included at the root, database or table level.
Here is an example of some plugin configuration for a specific table::
{
"databases: {
"sf-trees": {
"tables": {
"Street_Tree_List": {
"plugins": {
"datasette-cluster-map": {
"latitude_column": "lat",
"longitude_column": "lng"
}
}
}
}
}
}
}
This tells the ``datasette-cluster-map`` column which latitude and longitude columns should be used for a table called ``Street_Tree_List`` inside a database file called ``sf-trees.db``.
When you are writing plugins, you can access plugin configuration like this using the ``datasette.plugin_config()`` method. If you know you need plugin configuration for a specific table, you can access it like this::
plugin_config = datasette.plugin_config(
"datasette-cluster-map", database="sf-trees", table="Street_Tree_List"
)
This will return the ``{"latitude_column": "lat", "longitude_column": "lng"}`` in the above example.
If it cannot find the requested configuration at the table layer, it will fall back to the database layer and then the root layer. For example, a user may have set the plugin configuration option like so::
{
"databases: {
"sf-trees": {
"plugins": {
"datasette-cluster-map": {
"latitude_column": "xlat",
"longitude_column": "xlng"
}
}
}
}
}
In this case, the above code would return that configuration for ANY table within the ``sf-trees`` database.
The plugin configuration could also be set at the top level of ``metadata.json``::
{
"title": "This is the top-level title in metadata.json",
"plugins": {
"datasette-cluster-map": {
"latitude_column": "xlat",
"longitude_column": "xlng"
}
}
}
Now that ``datasette-cluster-map`` plugin configuration will apply to every table in every database.
Plugin hooks
------------

View file

@ -129,9 +129,19 @@ METADATA = {
'license_url': 'https://github.com/simonw/datasette/blob/master/LICENSE',
'source': 'tests/fixtures.py',
'source_url': 'https://github.com/simonw/datasette/blob/master/tests/fixtures.py',
"plugins": {
"name-of-plugin": {
"depth": "root"
}
},
'databases': {
'fixtures': {
'description': 'Test tables description',
"plugins": {
"name-of-plugin": {
"depth": "database"
}
},
'tables': {
'simple_primary_key': {
'description_html': 'Simple <em>primary</em> key',
@ -143,7 +153,12 @@ METADATA = {
'sortable_with_nulls',
'sortable_with_nulls_2',
'text',
]
],
"plugins": {
"name-of-plugin": {
"depth": "table"
}
}
},
'no_primary_key': {
'sortable_columns': [],

View file

@ -14,8 +14,8 @@ label_re = re.compile(r'\.\. _([^\s:]+):')
def get_headings(filename, underline="-"):
content = (docs_path / filename).open().read()
heading_re = re.compile(r'(\S+)\n\{}+\n'.format(underline))
return set(heading_re.findall(content))
heading_re = re.compile(r'(\w+)(\([^)]*\))?\n\{}+\n'.format(underline))
return set(h[0] for h in heading_re.findall(content))
def get_labels(filename):

View file

@ -85,3 +85,20 @@ def test_plugins_render_cell(app_client):
assert a is not None, str(a)
assert a.attrs["href"] == "http://example.com/"
assert a.text == "Example"
def test_plugin_config(app_client):
assert {"depth": "table"} == app_client.ds.plugin_config(
"name-of-plugin", database="fixtures", table="sortable"
)
assert {"depth": "database"} == app_client.ds.plugin_config(
"name-of-plugin", database="fixtures", table="unknown_table"
)
assert {"depth": "database"} == app_client.ds.plugin_config(
"name-of-plugin", database="fixtures"
)
assert {"depth": "root"} == app_client.ds.plugin_config(
"name-of-plugin", database="unknown_database"
)
assert {"depth": "root"} == app_client.ds.plugin_config("name-of-plugin")
assert None is app_client.ds.plugin_config("unknown-plugin")