{"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-343675165", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 343675165, "node_id": "MDEyOklzc3VlQ29tbWVudDM0MzY3NTE2NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2017-11-11T16:07:10Z", "updated_at": "2017-11-11T16:07:10Z", "author_association": "OWNER", "body": "The plugin system can also allow alternative providers for the `publish` command - e.g. maybe hook up hyper.sh as an option for publishing containers.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-344438724", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 344438724, "node_id": "MDEyOklzc3VlQ29tbWVudDM0NDQzODcyNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2017-11-14T23:47:54Z", "updated_at": "2017-11-14T23:47:54Z", "author_association": "OWNER", "body": "Plugins should be able to interact with the build step. This would give plugins an opportunity to modify the SQL databases and help prepare them for serving - for example, a full-text search plugin might create additional FTS tables, or a mapping plugin might pre-calculate a bunch of geohashes for tables that have latitude/longitude values. Plugins could really take advantage of the immutable nature of the dataset here.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-345067498", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 345067498, "node_id": "MDEyOklzc3VlQ29tbWVudDM0NTA2NzQ5OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2017-11-16T21:25:32Z", "updated_at": "2017-11-16T21:26:22Z", "author_association": "OWNER", "body": "For visualizations, Google Maps should be made available as a plugin. The default visualizations can use Leaflet and Open Street Map, but there's no reason to not make Google Maps available as a plugin, especially if the plugin can provide a mechanism for configuring the necessary API key.\r\n\r\nI'm particularly excited in the Google Maps heatmap visualization https://developers.google.com/maps/documentation/javascript/heatmaplayer as seen on http://mochimachine.org/wasteland/", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-345893877", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 345893877, "node_id": "MDEyOklzc3VlQ29tbWVudDM0NTg5Mzg3Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2017-11-21T02:11:27Z", "updated_at": "2017-11-21T02:11:27Z", "author_association": "OWNER", "body": "http://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins Is pretty good ", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-346244871", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 346244871, "node_id": "MDEyOklzc3VlQ29tbWVudDM0NjI0NDg3MQ==", "user": {"value": 21148, "label": "jacobian"}, "created_at": "2017-11-22T05:06:30Z", "updated_at": "2017-11-22T05:06:30Z", "author_association": "CONTRIBUTOR", "body": "I'd also suggest taking a look at [stevedore](https://docs.openstack.org/stevedore/latest/), which has a ton of tools for doing plugin stuff. I've had good luck with it in the past.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-346406009", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 346406009, "node_id": "MDEyOklzc3VlQ29tbWVudDM0NjQwNjAwOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2017-11-22T16:39:08Z", "updated_at": "2017-11-22T16:39:08Z", "author_association": "OWNER", "body": "Oh thanks, that definitely looks like an interesting option.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381442233", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381442233, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTQ0MjIzMw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-15T22:13:06Z", "updated_at": "2018-04-15T22:13:06Z", "author_association": "OWNER", "body": "I started a thread on Twitter asking people for good examples of Python projects with a strong plugin ecosystem: https://twitter.com/simonw/status/985377670388105216\r\n\r\nThe most impressive example that came back was pytest - which now has nearly 400 plugins: https://plugincompat.herokuapp.com/\r\n\r\nThe pytest plugin infrastructure is available as an independent package called pluggy - which appears to offer everything I need for Datasette. I'm going to give that a go and see how well it works: https://pluggy.readthedocs.io/en/latest/", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381442494", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381442494, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTQ0MjQ5NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-15T22:17:59Z", "updated_at": "2018-04-15T22:17:59Z", "author_association": "OWNER", "body": "Datasette 1.0 will be the release of Datasette that attempts to provide a stable plugin API: https://github.com/simonw/datasette/milestone/7\r\n\r\nThere's a lot of work to be done before then, but as a starting point I'm going to support two very simple extension mechanisms:\r\n\r\n* Template system plugins - where the hook gets passed the Jinja environment and can freely register new template tags and filters\r\n* SQLite connection plugins - where the hook gets passed a new SQLite connection and can register custom SQLite functions\r\n\r\nThe template system hook will go near here:\r\n\r\nhttps://github.com/simonw/datasette/blob/efbb4e83374a2c795e436c72fa79f70da72309b8/datasette/app.py#L1225-L1228\r\n\r\nThe SQLite connection hook will go near here:\r\n\r\nhttps://github.com/simonw/datasette/blob/efbb4e83374a2c795e436c72fa79f70da72309b8/datasette/app.py#L1094-L1098\r\n\r\nThese two feel simple enough that I'm not worried that I might design an API that I later regret.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381443728", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381443728, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTQ0MzcyOA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-15T22:39:00Z", "updated_at": "2018-04-15T22:39:00Z", "author_association": "OWNER", "body": "Tox is a good example of a project that uses pluggy in the way I want to use it (function hooks rather than classes): https://github.com/tox-dev/tox/blob/master/tox/hookspecs.py", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381446392", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381446392, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTQ0NjM5Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-15T23:22:40Z", "updated_at": "2018-04-16T05:25:57Z", "author_association": "OWNER", "body": "OK, from that prototype in f2720b0c6b7172ebe8820 it looks like pluggy provides a solid path forward.\r\n\r\nNext steps:\r\n\r\n- [x] Build a demo plugin that uses setuptools entrypoints to register with the `datasette` plugin manager via pluggy\r\n- [x] Figure out a mechanism for registering plugins without first needing to publish them to PyPI. Can I load plugins from a special `plugins/` directory similar to the `--template-dir=templates/` option already supported by Datasette? #211", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381446511", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381446511, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTQ0NjUxMQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-15T23:25:04Z", "updated_at": "2018-04-15T23:25:04Z", "author_association": "OWNER", "body": "Here's a demo of the `convert_units()` SQL function I prototyped in f2720b0c6b7172ebe88\r\n\r\n![2018-04-15 at 4 23 pm](https://user-images.githubusercontent.com/9599/38784633-8c43821e-40c9-11e8-97dd-697755a0f858.png)\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381446906", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381446906, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTQ0NjkwNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-15T23:31:58Z", "updated_at": "2018-04-15T23:34:10Z", "author_association": "OWNER", "body": "Once I've got the plugins mechanism stable and people start releasing plugins it would be useful to have a dedicated Trove classifier on PyPI for Datasette plugins - `Framework :: Datasette` for example.\r\n\r\nThis would help me build a Datasette equivalent of the http://plugincompat.herokuapp.com/ site, which works by scanning PyPI for items with the ``Framework :: Pytest`` classifier:\r\n\r\nhttps://github.com/pytest-dev/plugincompat/blob/8bdf1a6fb82807091ece0c68c196103ee8270194/update_index.py#L52-L53\r\n\r\nIt looks like the mechanism for requesting new PyPI classifiers is to file a ticket against warehouse, like these ones: https://github.com/pypa/warehouse/issues/3570 and https://github.com/pypa/warehouse/issues/2881", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381450394", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381450394, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTQ1MDM5NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-16T00:27:23Z", "updated_at": "2018-04-16T00:27:23Z", "author_association": "OWNER", "body": "I created https://github.com/simonw/datasette-plugin-demos which is now published to PyPI and can be installed with `pip install datasette-plugin-demos` - I've confirmed that if you DO install it my Datasette `plugins` branch picks up the plugins, and `select random_integer(1, 4)` works as it should.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381450591", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381450591, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTQ1MDU5MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-16T00:30:22Z", "updated_at": "2018-04-16T00:34:42Z", "author_association": "OWNER", "body": "Slight code design problem... when I tried installing my branch in a fresh virtual environment I got this error, because `setup.py` now depends on `pluggy` (from importing `__version__`):\r\n\r\n```\r\n File \"/private/var/folders/jj/fngnv0810tn2lt_kd3911pdc0000gp/T/pip-req-build-dftqdezt/setup.py\", line 2, in \r\n from datasette import __version__\r\n File \"/private/var/folders/jj/fngnv0810tn2lt_kd3911pdc0000gp/T/pip-req-build-dftqdezt/datasette/__init__.py\", line 2, in \r\n from .hookspecs import hookimpl # noqa\r\n File \"/private/var/folders/jj/fngnv0810tn2lt_kd3911pdc0000gp/T/pip-req-build-dftqdezt/datasette/hookspecs.py\", line 1, in \r\n from pluggy import HookimplMarker\r\n ModuleNotFoundError: No module named 'pluggy'\r\n```\r\n\r\nLooks like I've run into point 6 on https://packaging.python.org/guides/single-sourcing-package-version/ :\r\n\r\n![2018-04-15 at 5 34 pm](https://user-images.githubusercontent.com/9599/38785314-403ce86a-40d3-11e8-8542-ba426eddf4ac.png)\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381611738", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381611738, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTYxMTczOA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-16T14:07:30Z", "updated_at": "2018-04-16T14:07:30Z", "author_association": "OWNER", "body": "I should check if it's possible to have two template registration function plugins in a single plugin module. If it isn't maybe I should use class plugins instead of module plugins.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381621338", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381621338, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTYyMTMzOA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-16T14:36:27Z", "updated_at": "2018-04-16T14:36:27Z", "author_association": "OWNER", "body": "Annoyingly, the following only results in the last of the two `prepare_connection` hooks being registered:\r\n\r\n```\r\nfrom datasette import hookimpl\r\nimport pint\r\nimport random\r\n\r\nureg = pint.UnitRegistry()\r\n\r\n\r\n@hookimpl\r\ndef prepare_connection(conn):\r\n def convert_units(amount, from_, to_):\r\n \"select convert_units(100, 'm', 'ft');\"\r\n return (amount * ureg(from_)).to(to_).to_tuple()[0]\r\n conn.create_function('convert_units', 3, convert_units)\r\n\r\n\r\n@hookimpl\r\ndef prepare_connection(conn):\r\n conn.create_function('random_integer', 2, random.randint)\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381622793", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381622793, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTYyMjc5Mw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-16T14:40:39Z", "updated_at": "2018-04-17T01:47:15Z", "author_association": "OWNER", "body": "I think that's OK. The two plugins I've implemented so far (`prepare_connection` and `prepare_jinja2_environment`) both make sense if they can only be defined once-per-plugin. For the moment I'll assume I can define future hooks to work well with the same limitation.\r\n\r\nThe syntactic sugar idea in #220 can help here too.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-381809998", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 381809998, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MTgwOTk5OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-17T02:23:39Z", "updated_at": "2018-04-17T02:23:39Z", "author_association": "OWNER", "body": "I just shipped Datasette 0.19 with where I'm at so far: https://github.com/simonw/datasette/releases/tag/0.19", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-382256729", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 382256729, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MjI1NjcyOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-18T04:29:29Z", "updated_at": "2018-04-18T04:30:14Z", "author_association": "OWNER", "body": "I added a mechanism for plugins to serve static files and define custom CSS and JS URLs in #214 - see new documentation on http://datasette.readthedocs.io/en/latest/plugins.html#static-assets and http://datasette.readthedocs.io/en/latest/plugins.html#extra-css-urls", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-383139889", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 383139889, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MzEzOTg4OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-20T15:51:47Z", "updated_at": "2018-04-20T15:51:47Z", "author_association": "OWNER", "body": "I released everything we have so far in [Datasette 0.20](https://github.com/simonw/datasette/releases/tag/0.20) and built and released an example plugin, [datasette-cluster-map](https://pypi.org/project/datasette-cluster-map/). Here's my blog entry about it: https://simonwillison.net/2018/Apr/20/datasette-plugins/", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-383140111", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 383140111, "node_id": "MDEyOklzc3VlQ29tbWVudDM4MzE0MDExMQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2018-04-20T15:52:33Z", "updated_at": "2018-04-20T15:52:33Z", "author_association": "OWNER", "body": "Here's a link demonstrating my new plugin: https://datasette-cluster-map-demo.now.sh/polar-bears-455fe3a/USGS_WC_eartags_output_files_2009-2011-Status", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/14#issuecomment-491944613", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/14", "id": 491944613, "node_id": "MDEyOklzc3VlQ29tbWVudDQ5MTk0NDYxMw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2019-05-13T18:58:19Z", "updated_at": "2019-05-13T18:58:19Z", "author_association": "OWNER", "body": "We've grown a bunch of plugin hooks over the past two years: https://datasette.readthedocs.io/en/latest/plugins.html#plugin-hooks\r\n\r\nSince the plugin system will never be 100% \"finished\", I'm closing this in favor of the label: https://github.com/simonw/datasette/labels/plugins", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 267707940, "label": "Datasette Plugins"}, "performed_via_github_app": null}