{"html_url": "https://github.com/simonw/datasette/issues/1077#issuecomment-720003026", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1077", "id": 720003026, "node_id": "MDEyOklzc3VlQ29tbWVudDcyMDAwMzAyNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T23:48:21Z", "updated_at": "2020-10-31T23:50:07Z", "author_association": "OWNER", "body": "Needed by https://github.com/simonw/datasette-backup/issues/6", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733829385, "label": "database_actions plugin hook"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1047#issuecomment-719994676", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1047", "id": 719994676, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk5NDY3Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T22:11:25Z", "updated_at": "2020-10-31T22:11:25Z", "author_association": "OWNER", "body": "https://docs.datasette.io/en/latest/binary_data.html", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 728895233, "label": "A new section in the docs about how Datasette handles BLOB columns"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1027#issuecomment-719988113", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1027", "id": 719988113, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4ODExMw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T21:03:37Z", "updated_at": "2020-10-31T21:03:37Z", "author_association": "OWNER", "body": "On my Mac, I run:\r\n\r\n datasette . -p 8009 --config base_url:/datasette-prefix/\r\n\r\nThen I edited `/usr/local/etc/httpd/httpd.conf` and add this section:\r\n```\r\nLoadModule proxy_module lib/httpd/modules/mod_proxy.so\r\nLoadModule proxy_http_module lib/httpd/modules/mod_proxy_http.so\r\nProxyPass /datasette-prefix/ http://localhost:8009/datasette-prefix/\r\n```\r\nI ran Apache in the foreground like so:\r\n\r\n apachectl -X\r\n\r\nNow hitting http://localhost:8081/datasette-prefix/fixtures/compound_three_primary_keys worked!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 722758132, "label": "Add documentation on serving Datasette behind a proxy using base_url"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1023#issuecomment-719986922", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1023", "id": 719986922, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4NjkyMg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:51:01Z", "updated_at": "2020-10-31T20:51:01Z", "author_association": "OWNER", "body": "This should all be working correctly now.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 722673818, "label": "Fix issues relating to base_url"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/838#issuecomment-719986904", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/838", "id": 719986904, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4NjkwNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:50:41Z", "updated_at": "2020-10-31T20:50:41Z", "author_association": "OWNER", "body": "OK, this should be working now. You can use the `datasette.urls.static_plugins()` method to generate the correct URLs in the `extra_css_urls` plugin hook: https://docs.datasette.io/en/latest/internals.html#datasette-urls", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 637395097, "label": "Incorrect URLs when served behind a proxy with base_url set"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1041#issuecomment-719986800", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1041", "id": 719986800, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4NjgwMA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:49:28Z", "updated_at": "2020-10-31T20:49:28Z", "author_association": "OWNER", "body": "Implemented in a4ca26a2659d21779adf625183061d8879954c15", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 727627923, "label": "extra_js_urls and extra_css_urls should respect base_url setting"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1072#issuecomment-719986698", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1072", "id": 719986698, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4NjY5OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:48:17Z", "updated_at": "2020-10-31T20:48:17Z", "author_association": "OWNER", "body": "Here's the `datasette-edit-templates` plugin WIP I had before removing the hook: https://github.com/simonw/datasette-edit-templates/tree/82855c2612b84bc09c48fca885f831633a0d1552", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733499930, "label": "load_template hook doesn't work for include/extends"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719983750", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719983750, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4Mzc1MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:22:29Z", "updated_at": "2020-10-31T20:22:29Z", "author_association": "OWNER", "body": "I bet this is because I'm mucking around with one of those `__` methods. I'll try just doing the non-underscore methods instead.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719983565", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719983565, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4MzU2NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:21:03Z", "updated_at": "2020-10-31T20:21:03Z", "author_association": "OWNER", "body": "Here's the output of `dir(str)`:\r\n\r\n`['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719983484", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719983484, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4MzQ4NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:20:28Z", "updated_at": "2020-10-31T20:20:28Z", "author_association": "OWNER", "body": "It looks like this is specific to the way `PrefixedUrlString` is built.\r\n```\r\n(Pdb) class Weird(str): pass\r\n(Pdb) isinstance(Weird('bob'), collections.abc.Awaitable)\r\nFalse\r\n```\r\nSo subclassing strings doesn't trigger this bug, but something about `PrefixedUrlString` causes the problem.\r\n\r\nHere's the current `PrefixedUrlString` implementation:\r\n\r\nhttps://github.com/simonw/datasette/blob/bf18b9ba175a7b25fb8b765847397dd6efb8bb7b/datasette/utils/__init__.py#L1015-L1035", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719983240", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719983240, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4MzI0MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:18:49Z", "updated_at": "2020-10-31T20:18:49Z", "author_association": "OWNER", "body": "Here's the core problem:\r\n```\r\n(Pdb) isinstance('bob', collections.abc.Awaitable)\r\nFalse\r\n(Pdb) isinstance(PrefixedUrlString('bob'), collections.abc.Awaitable)\r\n*** TypeError: issubclass() arg 1 must be a class\r\n```\r\nFor some reason `isinstance()` does not like being handed an instance of PrefixedUrlString.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719981173", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719981173, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4MTE3Mw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T20:02:30Z", "updated_at": "2020-10-31T20:03:45Z", "author_association": "OWNER", "body": "I wonder how Jinja's `Markup()` class works? It uses https://pypi.org/project/MarkupSafe/\r\n\r\nIt's a subclass of `str`, defined here: https://github.com/pallets/markupsafe/blob/master/src/markupsafe/__init__.py", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1075#issuecomment-719980742", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1075", "id": 719980742, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk4MDc0Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T19:58:57Z", "updated_at": "2020-10-31T19:58:57Z", "author_association": "OWNER", "body": "Sample traceback:\r\n```\r\n \r\n/opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/site-packages/jinja2/asyncsupport.py:173: in auto_await\r\n if inspect.isawaitable(value):\r\n/opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/inspect.py:226: in isawaitable\r\n isinstance(object, collections.abc.Awaitable))\r\n/opt/hostedtoolcache/Python/3.7.9/x64/lib/python3.7/abc.py:139: in __instancecheck__\r\n return _abc_instancecheck(cls, instance)\r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\n\r\ncls = \r\nsubclass = .method of '/-/static/app.css'>\r\n\r\n def __subclasscheck__(cls, subclass):\r\n \"\"\"Override for issubclass(subclass, cls).\"\"\"\r\n> return _abc_subclasscheck(cls, subclass)\r\nE TypeError: issubclass() arg 1 must be a class\r\n```\r\nThis is within Jinja. It looks like Jinja really doesn't like methods that return non-string objects like `PrefixedUrlString`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733796942, "label": "PrefixedUrlString mechanism broke everything"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1074#issuecomment-719977864", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1074", "id": 719977864, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk3Nzg2NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T19:35:01Z", "updated_at": "2020-10-31T19:35:01Z", "author_association": "OWNER", "body": "These plugins were not designed to be actually hosted online, so they do some nasty things like linking to the made-up `plugin-example.com` domain. I should fix that.\r\n\r\n\"Datasette_Fixtures__fixtures\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733768037, "label": "latest.datasette.io should include plugins from fixtures"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1067#issuecomment-719966176", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1067", "id": 719966176, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk2NjE3Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T17:51:31Z", "updated_at": "2020-10-31T17:51:31Z", "author_association": "OWNER", "body": "Demo:\r\n\r\n- https://latest.datasette.io/fixtures/facetable?_bot=1\r\n- https://latest.datasette.io/fixtures/simple_view?_bot=1", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732905360, "label": "Table actions menu on view pages, not on query pages"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1074#issuecomment-719965426", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1074", "id": 719965426, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk2NTQyNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T17:45:00Z", "updated_at": "2020-10-31T17:45:00Z", "author_association": "OWNER", "body": "This is working. Go to https://latest.datasette.io/login-as-root and click the button, then visit this page to see extra content added by plugins: https://latest.datasette.io/fixtures/compound_three_primary_keys\r\n\r\n![latest-plugins](https://user-images.githubusercontent.com/9599/97786052-19e53d00-1b66-11eb-8268-b452e08965e3.gif)\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733768037, "label": "latest.datasette.io should include plugins from fixtures"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1074#issuecomment-719963074", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1074", "id": 719963074, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk2MzA3NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T17:23:48Z", "updated_at": "2020-10-31T17:23:48Z", "author_association": "OWNER", "body": "Needs a way to login as root, seeing as several plugins only show extra content if the user is logged in as root.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733768037, "label": "latest.datasette.io should include plugins from fixtures"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1067#issuecomment-719961701", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1067", "id": 719961701, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk2MTcwMQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T17:11:59Z", "updated_at": "2020-10-31T17:11:59Z", "author_association": "OWNER", "body": "It bothers me that these aren't visible in any public demos. Maybe `latest.datasette.io` should include the `my_plugins.py` and `my_plugins2.py` plugins?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732905360, "label": "Table actions menu on view pages, not on query pages"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1026#issuecomment-719959754", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1026", "id": 719959754, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk1OTc1NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T16:56:35Z", "updated_at": "2020-10-31T16:56:35Z", "author_association": "OWNER", "body": "#1041 can also benefit from the string subclass that shows that `base_url` has been added.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 722738988, "label": "How should datasette.client interact with base_url"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1067#issuecomment-719959419", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1067", "id": 719959419, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk1OTQxOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T16:53:42Z", "updated_at": "2020-10-31T16:53:42Z", "author_association": "OWNER", "body": "For the 0.51 release I'm going to add tests that show this works on view pages. I won't implement it for query pages.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732905360, "label": "Table actions menu on view pages, not on query pages"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1067#issuecomment-719956184", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1067", "id": 719956184, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk1NjE4NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T16:26:09Z", "updated_at": "2020-10-31T16:26:09Z", "author_association": "OWNER", "body": "Should the hook provide an indication that it's running on a different type of page? I think yes for queries. Not sure about views - they behave very much like tables, and the plugin can always introspect to see if something is a view if it needs to.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 732905360, "label": "Table actions menu on view pages, not on query pages"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1070#issuecomment-719955724", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1070", "id": 719955724, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk1NTcyNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T16:22:45Z", "updated_at": "2020-10-31T16:22:45Z", "author_association": "OWNER", "body": "I've removed this plugin hook in #1073.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 733390884, "label": "load_template() example in documentation showing loading from a database"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1072#issuecomment-719955491", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1072", "id": 719955491, "node_id": "MDEyOklzc3VlQ29tbWVudDcxOTk1NTQ5MQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-10-31T16:20:58Z", "updated_at": "2020-10-31T16:20:58Z", "author_association": "OWNER", "body": "Here's the proof of concept `FunctionLoader` that showed me that this wasn't going to work:\r\n```diff\r\ndiff --git a/datasette/app.py b/datasette/app.py\r\nindex 4b28e71..b076be7 100644\r\n--- a/datasette/app.py\r\n+++ b/datasette/app.py\r\n@@ -21,7 +21,7 @@ from pathlib import Path\r\n from markupsafe import Markup\r\n from itsdangerous import URLSafeSerializer\r\n import jinja2\r\n-from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PrefixLoader\r\n+from jinja2 import ChoiceLoader, Environment, FileSystemLoader, FunctionLoader, PrefixLoader\r\n from jinja2.environment import Template\r\n from jinja2.exceptions import TemplateNotFound\r\n import uvicorn\r\n@@ -300,6 +300,7 @@ class Datasette:\r\n template_paths.append(default_templates)\r\n template_loader = ChoiceLoader(\r\n [\r\n+ FunctionLoader(self._load_template_from_plugins),\r\n FileSystemLoader(template_paths),\r\n # Support {% extends \"default:table.html\" %}:\r\n PrefixLoader(\r\n@@ -322,6 +323,17 @@ class Datasette:\r\n self._root_token = secrets.token_hex(32)\r\n self.client = DatasetteClient(self)\r\n \r\n+ def _load_template_from_plugins(self, template):\r\n+ # \"If auto reloading is enabled it\u2019s called to check if the template changed\"\r\n+ uptodatefunc = lambda: True\r\n+ source = pm.hook.load_template(\r\n+ template=template,\r\n+ datasette=self,\r\n+ )\r\n+ if source is None:\r\n+ return None\r\n+ return source, template, uptodatefunc\r\n+\r\n @property\r\n def urls(self):\r\n return Urls(self)\r\n@@ -719,35 +731,7 @@ class Datasette:\r\n else:\r\n if isinstance(templates, str):\r\n templates = [templates]\r\n-\r\n- # Give plugins first chance at loading the template\r\n- break_outer = False\r\n- plugin_template_source = None\r\n- plugin_template_name = None\r\n- template_name = None\r\n- for template_name in templates:\r\n- if break_outer:\r\n- break\r\n- plugin_template_source = pm.hook.load_template(\r\n- template=template_name,\r\n- request=request,\r\n- datasette=self,\r\n- )\r\n- plugin_template_source = await await_me_maybe(plugin_template_source)\r\n- if plugin_template_source:\r\n- break_outer = True\r\n- plugin_template_name = template_name\r\n- break\r\n- if plugin_template_source is not None:\r\n- template = self.jinja_env.from_string(plugin_template_source)\r\n- else:\r\n- template = self.jinja_env.select_template(templates)\r\n- for template_name in templates:\r\n- from_plugin = template_name == plugin_template_name\r\n- used = from_plugin or template_name == template.name\r\n- templates_considered.append(\r\n- {\"name\": template_name, \"used\": used, \"from_plugin\": from_plugin}\r\n- )\r\n+ template = self.jinja_env.select_template(templates)\r\n body_scripts = []\r\n # pylint: disable=no-member\r\n for extra_script in pm.hook.extra_body_script(\r\ndiff --git a/datasette/hookspecs.py b/datasette/hookspecs.py\r\nindex ca84b35..7804def 100644\r\n--- a/datasette/hookspecs.py\r\n+++ b/datasette/hookspecs.py\r\n@@ -50,7 +50,7 @@ def extra_template_vars(\r\n \r\n \r\n @hookspec(firstresult=True)\r\n-def load_template(template, request, datasette):\r\n+def load_template(template, datasette):\r\n \"Load the specified template, returning the template code as a string\"\r\n \r\n \r\ndiff --git a/docs/plugin_hooks.rst b/docs/plugin_hooks.rst\r\nindex 3c57b6a..8f2704e 100644\r\n--- a/docs/plugin_hooks.rst\r\n+++ b/docs/plugin_hooks.rst\r\n@@ -273,15 +273,12 @@ Example: `datasette-cluster-map