home / github

Menu
  • GraphQL API

github

Custom SQL query returning 49 rows (hide)

Query parameters

This data as json, CSV

html_urlissue_urlidnode_idusercreated_atupdated_atauthor_associationbodyreactionsissueperformed_via_github_app
https://github.com/simonw/datasette/issues/1034#issuecomment-719094027 https://api.github.com/repos/simonw/datasette/issues/1034 719094027 MDEyOklzc3VlQ29tbWVudDcxOTA5NDAyNw== 9599 2020-10-30T00:11:17Z 2020-10-30T00:11:17Z OWNER Demos: https://latest.datasette.io/fixtures/binary_data.csv?_size=max ```csv rowid,data 1,http://latest.datasette.io/fixtures/binary_data/1.blob?_blob_column=data 2,http://latest.datasette.io/fixtures/binary_data/2.blob?_blob_column=data 3, ``` https://latest.datasette.io/fixtures.csv?sql=select+rowid%2C+data+from+binary_data+order+by+rowid+limit+1001&_size=max ```csv rowid,data 1,http://latest.datasette.io/fixtures.blob?sql=select+rowid%2C+data+from+binary_data+order+by+rowid+limit+1001&_size=max&_blob_column=data&_blob_hash=f3088978da8f9aea479ffc7f631370b968d2e855eeb172bea7f6c7a04262bb6d 2,http://latest.datasette.io/fixtures.blob?sql=select+rowid%2C+data+from+binary_data+order+by+rowid+limit+1001&_size=max&_blob_column=data&_blob_hash=b835b0483cedb86130b9a2c280880bf5fadc5318ddf8c18d0df5204d40df1724 3, ``` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 725184645  
https://github.com/simonw/datasette/issues/1064#issuecomment-719104883 https://api.github.com/repos/simonw/datasette/issues/1064 719104883 MDEyOklzc3VlQ29tbWVudDcxOTEwNDg4Mw== 9599 2020-10-30T00:50:01Z 2020-10-30T00:52:29Z OWNER Here's what the prototype looks like so far: ![menu](https://user-images.githubusercontent.com/9599/97647443-7eda4f00-1a0f-11eb-8d78-b703b7a13616.gif) {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732798913  
https://github.com/simonw/datasette/issues/1064#issuecomment-719105197 https://api.github.com/repos/simonw/datasette/issues/1064 719105197 MDEyOklzc3VlQ29tbWVudDcxOTEwNTE5Nw== 9599 2020-10-30T00:51:16Z 2020-10-30T00:51:16Z OWNER I used a `<details><summary>` for this: https://github.com/simonw/datasette/blob/0d7ac764861d84be24d661cf4104ce61ea11a82a/datasette/templates/base.html#L16-L36 I added a bit of JavaScript so that clicking outside the menu would close it: https://github.com/simonw/datasette/blob/0d7ac764861d84be24d661cf4104ce61ea11a82a/datasette/templates/base.html#L59-L74 {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732798913  
https://github.com/simonw/datasette/issues/1064#issuecomment-719105641 https://api.github.com/repos/simonw/datasette/issues/1064 719105641 MDEyOklzc3VlQ29tbWVudDcxOTEwNTY0MQ== 9599 2020-10-30T00:53:00Z 2020-10-30T00:53:00Z OWNER Tips for making this accessible: https://css-tricks.com/accessible-svgs/ {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732798913  
https://github.com/simonw/datasette/issues/1064#issuecomment-719106174 https://api.github.com/repos/simonw/datasette/issues/1064 719106174 MDEyOklzc3VlQ29tbWVudDcxOTEwNjE3NA== 9599 2020-10-30T00:55:12Z 2020-10-30T00:55:12Z OWNER So what should go in this menu? If the user is logged in as root, I'll link to the various debug pages. If they're not logged in at all I don't think the menu should appear. If they are logged in as anyone, it should display to give them access to the "log out" button. Plugins can add links to it. If those plugins add links, the menu will display. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732798913  
https://github.com/simonw/datasette/issues/1064#issuecomment-719109770 https://api.github.com/repos/simonw/datasette/issues/1064 719109770 MDEyOklzc3VlQ29tbWVudDcxOTEwOTc3MA== 9599 2020-10-30T01:08:14Z 2020-10-30T01:08:14Z OWNER How should the plugin hook work? Here's the first version of the HTML: ```html <div class="nav-menu-inner"> <ul> <li><a href="{{ urls.instance() }}">Home</a></li> <li><a href="{{ urls.path('/-/plugins') }}">Installed plugins</a></li> <li><a href="{{ urls.path('/-/versions') }}">Software versions</a></li> <li><a href="{{ urls.path('/-/metadata') }}">Metadata</a></li> {% if show_logout %} <form action="{{ urls.logout() }}" method="post"> <input type="hidden" name="csrftoken" value="{{ csrftoken() }}"> <button class="button-as-link">Log out</button> </form>{% endif %} </ul> </div> ``` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732798913  
https://github.com/simonw/datasette/issues/1064#issuecomment-719110582 https://api.github.com/repos/simonw/datasette/issues/1064 719110582 MDEyOklzc3VlQ29tbWVudDcxOTExMDU4Mg== 9599 2020-10-30T01:11:13Z 2020-10-30T01:11:13Z OWNER Should plugins be able to add forms like the logout form here, or should they be restricted to adding navigation links? I can't think of a reason a plugin would need to add a form. The logout form is a special case to protect against logout-csrf attacks. So I think plugins get to return a list of dictionaries, each with a `label` and an `href`: ```python return [{ "label": "Upload CSVs", "href": datasette.urls.path("/-/upload-csvs") }] ``` But... is there an argument for returning headings, to divide up the menu? I think so. I also like the idea that a default plugin checks for the `root` user and outputs links to the different debugging tools - maybe those should be wrapped in a section heading. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732798913  
https://github.com/simonw/datasette/issues/1064#issuecomment-719110808 https://api.github.com/repos/simonw/datasette/issues/1064 719110808 MDEyOklzc3VlQ29tbWVudDcxOTExMDgwOA== 9599 2020-10-30T01:12:09Z 2020-10-30T01:12:19Z OWNER Or... plugins could return HTML - maybe optionally using helper functions to generate common HTML such that plugins which use the helpers can have their HTML modified in the future. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732798913  
https://github.com/simonw/datasette/issues/1064#issuecomment-719111373 https://api.github.com/repos/simonw/datasette/issues/1064 719111373 MDEyOklzc3VlQ29tbWVudDcxOTExMTM3Mw== 9599 2020-10-30T01:14:13Z 2020-10-30T01:14:13Z OWNER Plugins returning HTML makes more sense for some of the other areas that plugins will be able to inject content - e.g. injecting content on the table or row page above the table. If I'm going to have that as a pattern though it may make sense to use HTML here, since that will be consistent with other places that plugins can inject additional content. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732798913  
https://github.com/simonw/datasette/issues/1064#issuecomment-719111597 https://api.github.com/repos/simonw/datasette/issues/1064 719111597 MDEyOklzc3VlQ29tbWVudDcxOTExMTU5Nw== 9599 2020-10-30T01:15:05Z 2020-10-30T01:15:05Z OWNER I'm torn on this one. I think I have a very slight preference for plugins returning structured objects as opposed to HTML. Less likely to regret that choice in the future? {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732798913  
https://github.com/simonw/datasette/issues/1064#issuecomment-719117185 https://api.github.com/repos/simonw/datasette/issues/1064 719117185 MDEyOklzc3VlQ29tbWVudDcxOTExNzE4NQ== 9599 2020-10-30T01:35:17Z 2020-10-30T01:35:17Z OWNER I'm going to go with a list of `{"label": ..., "href": ...}` as the first iteration of this. The logout link will not be returned as part of the plugin output. A default plugin will provide the debug tools if the user is logged in as root. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732798913  
https://github.com/simonw/datasette/pull/1065#issuecomment-719153773 https://api.github.com/repos/simonw/datasette/issues/1065 719153773 MDEyOklzc3VlQ29tbWVudDcxOTE1Mzc3Mw== 22429695 2020-10-30T03:44:57Z 2020-10-30T03:44:57Z NONE # [Codecov](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=h1) Report > Merging [#1065](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=desc) into [main](https://codecov.io/gh/simonw/datasette/commit/1a861be19e326e0c88230a711a1b6536366697d7?el=desc) will **increase** coverage by `0.03%`. > The diff coverage is `100.00%`. [![Impacted file tree graph](https://codecov.io/gh/simonw/datasette/pull/1065/graphs/tree.svg?width=650&height=150&src=pr&token=eSahVY7kw1)](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=tree) ```diff @@ Coverage Diff @@ ## main #1065 +/- ## ========================================== + Coverage 91.23% 91.27% +0.03% ========================================== Files 28 29 +1 Lines 3710 3724 +14 ========================================== + Hits 3385 3399 +14 Misses 325 325 ``` | [Impacted Files](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=tree) | Coverage Δ | | |---|---|---| | [datasette/plugins.py](https://codecov.io/gh/simonw/datasette/pull/1065/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL3BsdWdpbnMucHk=) | `82.35% <ø> (ø)` | | | [datasette/app.py](https://codecov.io/gh/simonw/datasette/pull/1065/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2FwcC5weQ==) | `96.42% <100.00%> (+0.03%)` | :arrow_up: | | [datasette/default\_menu\_links.py](https://codecov.io/gh/simonw/datasette/pull/1065/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2RlZmF1bHRfbWVudV9saW5rcy5weQ==) | `100.00% <100.00%> (ø)` | | | [datasette/hookspecs.py](https://codecov.io/gh/simonw/datasette/pull/1065/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2hvb2tzcGVjcy5weQ==) | `100.00% <100.00%> (ø)` | | ------ [Continue to review full report at Codecov](https://codecov.io/gh/simonw/datasette/pull/1065?src=pr&el=continue). > **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta) > `Δ = absolute <relative> (impact)`, `ø = not affected… {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732856937  
https://github.com/simonw/datasette/issues/1066#issuecomment-719154646 https://api.github.com/repos/simonw/datasette/issues/1066 719154646 MDEyOklzc3VlQ29tbWVudDcxOTE1NDY0Ng== 9599 2020-10-30T03:48:15Z 2020-10-30T03:48:15Z OWNER This will use a very similar implementation to the navigation menu in #1064 - similar plugin hook and I'll use a `<details><summary>` to implement it. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732859030  
https://github.com/simonw/datasette/issues/1066#issuecomment-719194619 https://api.github.com/repos/simonw/datasette/issues/1066 719194619 MDEyOklzc3VlQ29tbWVudDcxOTE5NDYxOQ== 9599 2020-10-30T05:18:04Z 2020-10-30T05:18:04Z OWNER The cog only appears if at least one table action has been registered by a plugin. It looks like this: ![table-actions](https://user-images.githubusercontent.com/9599/97662535-9bd54900-1a34-11eb-8e0f-56d159c8834e.gif) {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732859030  
https://github.com/simonw/datasette/issues/1066#issuecomment-719194756 https://api.github.com/repos/simonw/datasette/issues/1066 719194756 MDEyOklzc3VlQ29tbWVudDcxOTE5NDc1Ng== 9599 2020-10-30T05:18:35Z 2020-10-30T05:18:35Z OWNER Documentation: https://docs.datasette.io/en/latest/plugin_hooks.html#table-actions-datasette-actor-database-table {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732859030  
https://github.com/simonw/datasette/issues/690#issuecomment-719195346 https://api.github.com/repos/simonw/datasette/issues/690 719195346 MDEyOklzc3VlQ29tbWVudDcxOTE5NTM0Ng== 9599 2020-10-30T05:20:42Z 2020-10-30T05:20:42Z OWNER I've now added two new plugin hooks: [menu_links()](https://docs.datasette.io/en/latest/plugin_hooks.html#menu-links-datasette-actor) and [table_actions()](https://docs.datasette.io/en/latest/plugin_hooks.html#table-actions-datasette-actor-database-table). I'm going to close this issue. Further work (on column actions and and database actions) can happen in separate tickets, but I won't include them in Datasette 0.51 since they're much less interesting than table and instance actions. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 573755726  
https://github.com/simonw/datasette/issues/1067#issuecomment-719320948 https://api.github.com/repos/simonw/datasette/issues/1067 719320948 MDEyOklzc3VlQ29tbWVudDcxOTMyMDk0OA== 9599 2020-10-30T07:02:37Z 2020-10-30T07:02:37Z OWNER Yes, this should be possible - no point restricting what plugin authors can do with the feature. Will need to add some extra arguments to the plugin hook for this. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732905360  
https://github.com/simonw/datasette/issues/1067#issuecomment-719322666 https://api.github.com/repos/simonw/datasette/issues/1067 719322666 MDEyOklzc3VlQ29tbWVudDcxOTMyMjY2Ng== 9599 2020-10-30T07:04:02Z 2020-10-30T07:04:02Z OWNER Maybe rename it to `actions_menu` and have it work for database, view, table and query pages using different arguments on each. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732905360  
https://github.com/simonw/datasette/issues/1068#issuecomment-719328661 https://api.github.com/repos/simonw/datasette/issues/1068 719328661 MDEyOklzc3VlQ29tbWVudDcxOTMyODY2MQ== 9599 2020-10-30T07:09:30Z 2020-10-30T07:09:30Z OWNER Then this can make it available to root: https://github.com/simonw/datasette/blob/18a64fbb29271ce607937110bbdb55488c43f4e0/datasette/default_permissions.py {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732939921  
https://github.com/simonw/datasette/issues/1068#issuecomment-719329219 https://api.github.com/repos/simonw/datasette/issues/1068 719329219 MDEyOklzc3VlQ29tbWVudDcxOTMyOTIxOQ== 9599 2020-10-30T07:09:59Z 2020-10-30T07:09:59Z OWNER Permission idea: `debug-menu` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732939921  
https://github.com/simonw/datasette/issues/1068#issuecomment-719331236 https://api.github.com/repos/simonw/datasette/issues/1068 719331236 MDEyOklzc3VlQ29tbWVudDcxOTMzMTIzNg== 9599 2020-10-30T07:11:58Z 2020-10-30T07:11:58Z OWNER Document the new permission here: https://docs.datasette.io/en/stable/authentication.html#built-in-permissions {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732939921  
https://github.com/simonw/datasette/issues/1068#issuecomment-719332460 https://api.github.com/repos/simonw/datasette/issues/1068 719332460 MDEyOklzc3VlQ29tbWVudDcxOTMzMjQ2MA== 9599 2020-10-30T07:13:10Z 2020-10-30T07:13:10Z OWNER I mainly want this so I can add that debug menu to my Dogsheep. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732939921  
https://github.com/simonw/datasette/issues/1068#issuecomment-719630745 https://api.github.com/repos/simonw/datasette/issues/1068 719630745 MDEyOklzc3VlQ29tbWVudDcxOTYzMDc0NQ== 9599 2020-10-30T15:44:13Z 2020-10-30T15:44:13Z OWNER Documentation: https://docs.datasette.io/en/latest/authentication.html#debug-menu {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 732939921  
https://github.com/simonw/datasette/pull/1069#issuecomment-719640430 https://api.github.com/repos/simonw/datasette/issues/1069 719640430 MDEyOklzc3VlQ29tbWVudDcxOTY0MDQzMA== 9599 2020-10-30T16:01:13Z 2020-10-30T16:01:13Z OWNER Next steps: build a demonstration plugin against this. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733303548  
https://github.com/simonw/datasette/pull/1069#issuecomment-719657478 https://api.github.com/repos/simonw/datasette/issues/1069 719657478 MDEyOklzc3VlQ29tbWVudDcxOTY1NzQ3OA== 22429695 2020-10-30T16:31:21Z 2020-10-30T17:46:36Z NONE # [Codecov](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=h1) Report > Merging [#1069](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=desc) into [main](https://codecov.io/gh/simonw/datasette/commit/222f79bb4c6e2aa5426cc5ff25f1b2461e18a300?el=desc) will **increase** coverage by `0.01%`. > The diff coverage is `95.83%`. [![Impacted file tree graph](https://codecov.io/gh/simonw/datasette/pull/1069/graphs/tree.svg?width=650&height=150&src=pr&token=eSahVY7kw1)](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=tree) ```diff @@ Coverage Diff @@ ## main #1069 +/- ## ========================================== + Coverage 91.30% 91.32% +0.01% ========================================== Files 29 29 Lines 3736 3756 +20 ========================================== + Hits 3411 3430 +19 - Misses 325 326 +1 ``` | [Impacted Files](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=tree) | Coverage Δ | | |---|---|---| | [datasette/views/base.py](https://codecov.io/gh/simonw/datasette/pull/1069/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL3ZpZXdzL2Jhc2UucHk=) | `93.94% <ø> (-0.04%)` | :arrow_down: | | [datasette/app.py](https://codecov.io/gh/simonw/datasette/pull/1069/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2FwcC5weQ==) | `96.38% <95.45%> (-0.05%)` | :arrow_down: | | [datasette/hookspecs.py](https://codecov.io/gh/simonw/datasette/pull/1069/diff?src=pr&el=tree#diff-ZGF0YXNldHRlL2hvb2tzcGVjcy5weQ==) | `100.00% <100.00%> (ø)` | | ------ [Continue to review full report at Codecov](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=continue). > **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta) > `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data` > Powered by [Codecov](https://codecov.io/gh/simonw/datasette/pull/1069?src=pr&el=footer). Last update [222f79b...92f3840](https://codecov.io… {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733303548  
https://github.com/simonw/datasette/pull/1069#issuecomment-719664530 https://api.github.com/repos/simonw/datasette/issues/1069 719664530 MDEyOklzc3VlQ29tbWVudDcxOTY2NDUzMA== 9599 2020-10-30T16:43:40Z 2020-10-30T16:43:40Z OWNER I should include an example in the documentation that shows loading templates from a database table. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733303548  
https://github.com/simonw/datasette/pull/1069#issuecomment-719666912 https://api.github.com/repos/simonw/datasette/issues/1069 719666912 MDEyOklzc3VlQ29tbWVudDcxOTY2NjkxMg== 9599 2020-10-30T16:47:44Z 2020-10-30T16:47:44Z OWNER Bringing over a comment from #1042: > I'd like to do this all in the `datasette.render_template()` method to ensure it's available to plugins as well, not just core code that uses the `BaseView` class. > > This code is the problem: > > https://github.com/simonw/datasette/blob/d3e9b0aecb6f8e9b2befd9c654ccb7ce852db3e7/datasette/views/base.py#L114-L133 > > I think I'll fix this by moving the `select_templates` mechanism into `datasette.render_templates()`. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733303548  
https://github.com/simonw/datasette/pull/1069#issuecomment-719670714 https://api.github.com/repos/simonw/datasette/issues/1069 719670714 MDEyOklzc3VlQ29tbWVudDcxOTY3MDcxNA== 9599 2020-10-30T16:53:56Z 2020-10-30T16:53:56Z OWNER I'm having second thoughts about the design of the plugin hook. Consider the following: ```python plugin_template_source = pm.hook.load_template( template=template_name, database=context.get("database"), table=context.get("table"), columns=context.get("columns"), view_name=self.name, request=request, datasette=self.ds, ) ``` It's a bit gross that `database`, `table` and `columns` are pulled out of the context like that. This doesn't make sense for pages that are rendered by plugins, for example. So maybe for the first release of this plugin hook I should cut it down to just seeing `template`, `request` and `datasette`. I can add the table/view/etc stuff back in later if it turns out to be necessary. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733303548  
https://github.com/simonw/datasette/pull/1069#issuecomment-719672967 https://api.github.com/repos/simonw/datasette/issues/1069 719672967 MDEyOklzc3VlQ29tbWVudDcxOTY3Mjk2Nw== 9599 2020-10-30T16:58:01Z 2020-10-30T16:58:01Z OWNER OK, new hook specification is: ```python @hookspec(firstresult=True) def load_template(template, request, datasette): "Load the specified template, returning the template code as a string" ``` {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733303548  
https://github.com/simonw/datasette/issues/1071#issuecomment-719777499 https://api.github.com/repos/simonw/datasette/issues/1071 719777499 MDEyOklzc3VlQ29tbWVudDcxOTc3NzQ5OQ== 9599 2020-10-30T20:20:01Z 2020-10-30T20:20:01Z OWNER Fixed: https://latest.datasette.io/-/messages ![demo-message](https://user-images.githubusercontent.com/9599/97753199-9c142980-1ab2-11eb-9be9-c0be41acc68e.gif) {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733485423  
https://github.com/simonw/datasette/issues/1072#issuecomment-719784606 https://api.github.com/repos/simonw/datasette/issues/1072 719784606 MDEyOklzc3VlQ29tbWVudDcxOTc4NDYwNg== 9599 2020-10-30T20:35:33Z 2020-10-30T20:35:33Z OWNER To fix this I think I need to move the `load_template` implementation into a Jinja template loader. I'm not sure I'll be able to keep the `Templates considered` comment working though: https://github.com/simonw/datasette/blob/a2a709072059c6b3da365df9a332ca744c2079e9/datasette/app.py#L745-L750 {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719785005 https://api.github.com/repos/simonw/datasette/issues/1072 719785005 MDEyOklzc3VlQ29tbWVudDcxOTc4NTAwNQ== 9599 2020-10-30T20:36:22Z 2020-10-30T20:36:22Z OWNER It should be easy enough to show a comment that says which original template names were considered, but I may not be able to show which one was actually used (or which ones came from plugins). {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719803880 https://api.github.com/repos/simonw/datasette/issues/1072 719803880 MDEyOklzc3VlQ29tbWVudDcxOTgwMzg4MA== 9599 2020-10-30T21:17:11Z 2020-10-30T21:17:11Z OWNER Example from the Jinja docs: https://jinja.palletsprojects.com/en/2.11.x/api/#jinja2.BaseLoader ```python from jinja2 import BaseLoader, TemplateNotFound from os.path import join, exists, getmtime class MyLoader(BaseLoader): def __init__(self, path): self.path = path def get_source(self, environment, template): path = join(self.path, template) if not exists(path): raise TemplateNotFound(template) mtime = getmtime(path) with file(path) as f: source = f.read().decode('utf-8') return source, path, lambda: mtime == getmtime(path) ``` Also available: `jinja2.FunctionLoader(load_func)` which lets me pass it a function like this one: ``` >>> def load_template(name): ... if name == 'index.html': ... return '...' ... >>> loader = FunctionLoader(load_template) ``` Just one catch: I need to be able to load templates asynchronously, because they live in the database. Let's hope Jinja has a mechanism for that! {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719807502 https://api.github.com/repos/simonw/datasette/issues/1072 719807502 MDEyOklzc3VlQ29tbWVudDcxOTgwNzUwMg== 9599 2020-10-30T21:26:49Z 2020-10-30T21:26:49Z OWNER It looks like Jinja does not have a mechanism for asynchronous template loading - the loader API is synchronous. One option may be to figure out which templates are needed (including inherited templates and includes) before rendering the template. Then async load those templates from the database into a `DictLoader`, then pass that `DictLoader` to Jinja. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719809259 https://api.github.com/repos/simonw/datasette/issues/1072 719809259 MDEyOklzc3VlQ29tbWVudDcxOTgwOTI1OQ== 9599 2020-10-30T21:31:10Z 2020-10-30T21:31:10Z OWNER How can we tell what template Jinja will need to render? One approach that could work: 1. Set up a dummy template loader which records the name of the template that was requested 2. Load the template 3. Now we know the list of templates that were requested. Async load those 4. The dummy template loader can now return the ones we have loaded. Load the template again. 5. Did it request any more templates? If so, load those, and repeat. 6. Keep on with this loop until a template load (which might even have to be a render) fails to request any templates that we have not yet loaded. 7. Render the template. This is GROSS. It feels like a huge waste of CPU, and it could lead to very weird behaviour if any template variables have side effects. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719809780 https://api.github.com/repos/simonw/datasette/issues/1072 719809780 MDEyOklzc3VlQ29tbWVudDcxOTgwOTc4MA== 9599 2020-10-30T21:32:28Z 2020-10-30T21:32:28Z OWNER Here's an alternative that would definitely work and would be a lot simpler, at the cost of a fair amount of RAM: 1. Before rendering the template, load ALL of the most-recent-versions of the templates that are stored in the DB. Use those to populate a `DictLoader`. 2. Render the template. This does mean loading template bodies that we won't use. Provided an instance has less than 100 templates I imagine this will work just fine. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719810023 https://api.github.com/repos/simonw/datasette/issues/1072 719810023 MDEyOklzc3VlQ29tbWVudDcxOTgxMDAyMw== 9599 2020-10-30T21:33:06Z 2020-10-30T21:33:06Z OWNER The ideal solution is for Jinja to offer `async` template loading. I'll file a feature request, then I'll implement the second option above (async load all templates from the DB before each render). {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719810533 https://api.github.com/repos/simonw/datasette/issues/1072 719810533 MDEyOklzc3VlQ29tbWVudDcxOTgxMDUzMw== 9599 2020-10-30T21:34:38Z 2020-10-30T21:34:38Z OWNER ... no wait, my comments above assume that I'm just building the `datasette-edit-templates` plugin. Does this work as a general solution for all of Datasette? I don't think it does. This may mean I need to delay the whole feature. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719811312 https://api.github.com/repos/simonw/datasette/issues/1072 719811312 MDEyOklzc3VlQ29tbWVudDcxOTgxMTMxMg== 9599 2020-10-30T21:36:49Z 2020-10-30T21:36:49Z OWNER There's one other option: in `datasette-edit-templates` I could maybe use `asyncio.get_event_loop().run_in_executor(...)` to load the templates asynchronously within the Jinja template loader mechanism. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719813212 https://api.github.com/repos/simonw/datasette/issues/1072 719813212 MDEyOklzc3VlQ29tbWVudDcxOTgxMzIxMg== 9599 2020-10-30T21:42:35Z 2020-10-30T21:42:35Z OWNER Filed a feature request here: https://github.com/pallets/jinja/issues/1304 {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719813970 https://api.github.com/repos/simonw/datasette/issues/1072 719813970 MDEyOklzc3VlQ29tbWVudDcxOTgxMzk3MA== 9599 2020-10-30T21:44:40Z 2020-10-30T21:44:40Z OWNER I'm pretty sure that `run_in_executor()` workaround won't work. https://github.com/django/asgiref/blob/7becc9daca2628c46af1cb7e46b4c47c1ea27adf/asgiref/sync.py#L83 for example says "You cannot use AsyncToSync in the same thread as an async event loop". {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719814279 https://api.github.com/repos/simonw/datasette/issues/1072 719814279 MDEyOklzc3VlQ29tbWVudDcxOTgxNDI3OQ== 9599 2020-10-30T21:45:33Z 2020-10-30T21:45:33Z OWNER Sadly I'm going to bump `load_template` from Datasette 0.51 - I don't think I should block the release on resolving this issue. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719819234 https://api.github.com/repos/simonw/datasette/issues/1072 719819234 MDEyOklzc3VlQ29tbWVudDcxOTgxOTIzNA== 9599 2020-10-30T22:00:21Z 2020-10-30T22:00:21Z OWNER There might be a way to save this. Async template loading can't be supported, but what if you could define a `load_template()` hook which returned a sync function that returned templates... Then the `datasette-edit-templates` plugin could reply to `load_template` by loading all DB templates into memory and returning a `load_template` sync function that looked up the values in those already-loaded templates. It could even maintain an in-memory cache that gets updated when a template is edited. If I do this, I could remove the ability to return an `async` function from `load_template()` but add that in the future should Jinja implement a mechanism for async template loading. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719819331 https://api.github.com/repos/simonw/datasette/issues/1072 719819331 MDEyOklzc3VlQ29tbWVudDcxOTgxOTMzMQ== 9599 2020-10-30T22:00:43Z 2020-10-30T22:00:43Z OWNER I'll try getting that to work. If I can't get it to work I'll drop the plugin hook for the moment. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719832651 https://api.github.com/repos/simonw/datasette/issues/1072 719832651 MDEyOklzc3VlQ29tbWVudDcxOTgzMjY1MQ== 9599 2020-10-30T22:46:25Z 2020-10-30T22:46:25Z OWNER I tried using a `FunctionLoader` and got this error on startup: ```python File "/Users/simon/Dropbox/Development/datasette/datasette/app.py", line 989, in __init__ for filepath in self.ds.jinja_env.list_templates() File "/Users/simon/.local/share/virtualenvs/datasette-edit-templates-agoZyE3x/lib/python3.8/site-packages/jinja2/environment.py", line 810, in list_templates names = self.loader.list_templates() File "/Users/simon/.local/share/virtualenvs/datasette-edit-templates-agoZyE3x/lib/python3.8/site-packages/jinja2/loaders.py", line 434, in list_templates found.update(loader.list_templates()) File "/Users/simon/.local/share/virtualenvs/datasette-edit-templates-agoZyE3x/lib/python3.8/site-packages/jinja2/loaders.py", line 99, in list_templates raise TypeError("this loader cannot iterate over all templates") TypeError: this loader cannot iterate over all templates ``` So if I'm going to define a custom Jinja loader I'll need to teach plugins to answer the "list templates" query. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719832853 https://api.github.com/repos/simonw/datasette/issues/1072 719832853 MDEyOklzc3VlQ29tbWVudDcxOTgzMjg1Mw== 9599 2020-10-30T22:47:12Z 2020-10-30T22:47:12Z OWNER Maybe I should ditch this hook entirely in favour of the existing `prepare_jinja2_environment` hook. Could that add new template loaders? {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719833070 https://api.github.com/repos/simonw/datasette/issues/1072 719833070 MDEyOklzc3VlQ29tbWVudDcxOTgzMzA3MA== 9599 2020-10-30T22:48:04Z 2020-10-30T22:48:04Z OWNER https://github.com/simonw/datasette/blob/a2a709072059c6b3da365df9a332ca744c2079e9/datasette/app.py#L310-L318 So yeah that plugin hook can probably modify the list of loaders available to the `Environment`. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1072#issuecomment-719833744 https://api.github.com/repos/simonw/datasette/issues/1072 719833744 MDEyOklzc3VlQ29tbWVudDcxOTgzMzc0NA== 9599 2020-10-30T22:50:57Z 2020-10-30T22:50:57Z OWNER Yeah I'm going to remove the `load_template` plugin hook and see if it's possible to build the edit templates extension against `prepare_jinja2_environment` instead. {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733499930  
https://github.com/simonw/datasette/issues/1073#issuecomment-719834200 https://api.github.com/repos/simonw/datasette/issues/1073 719834200 MDEyOklzc3VlQ29tbWVudDcxOTgzNDIwMA== 9599 2020-10-30T22:52:48Z 2020-10-30T22:52:48Z OWNER Should mostly be a case of backing out the changes from this commit: 81dea4b07ab2b6f4eaaf248307d2b588472054a1 {"total_count": 0, "+1": 0, "-1": 0, "laugh": 0, "hooray": 0, "confused": 0, "heart": 0, "rocket": 0, "eyes": 0} 733560417  
Powered by Datasette · Queries took 25.724ms · About: simonw/datasette-graphql