{"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-592621235", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 592621235, "node_id": "MDEyOklzc3VlQ29tbWVudDU5MjYyMTIzNQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-02-28T17:24:06Z", "updated_at": "2020-05-27T21:12:21Z", "author_association": "OWNER", "body": "Rather than pass a request object (hence promoting that object into part of the documented, stable API) I think I'll pass the ASGI scope - that's already a stable, documented standard.\r\n\r\nUPDATE: changed my mind since `request` is used by other plugins too, see #706.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/744#issuecomment-634395343", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/744", "id": 634395343, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDM5NTM0Mw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T02:49:26Z", "updated_at": "2020-05-27T02:49:26Z", "author_association": "OWNER", "body": "OK, here's a new branch you can try. Install it like this:\r\n\r\n pip install https://github.com/simonw/datasette/archive/shutil-backport.zip\r\n\r\nIf it works for you I'll merge that branch into master.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 608058890, "label": "link_or_copy_directory() error - Invalid cross-device link"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/744#issuecomment-634446887", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/744", "id": 634446887, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDQ0Njg4Nw==", "user": {"value": 30607, "label": "aborruso"}, "created_at": "2020-05-27T06:01:28Z", "updated_at": "2020-05-27T06:01:28Z", "author_association": "NONE", "body": "Dear @simonw thank you for your time, now IT WORKS!!!\r\n\r\nI hope that this edit to datasette code is not for an exceptional case (my PC configuration) and that it will be useful to other users. \r\n\r\nThank you again!!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 608058890, "label": "link_or_copy_directory() error - Invalid cross-device link"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/744#issuecomment-634850676", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/744", "id": 634850676, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg1MDY3Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T18:18:01Z", "updated_at": "2020-05-27T18:18:01Z", "author_association": "OWNER", "body": "Thanks for helping test this @aborruso!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 608058890, "label": "link_or_copy_directory() error - Invalid cross-device link"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634852196", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634852196, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg1MjE5Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T18:20:46Z", "updated_at": "2020-05-27T18:20:46Z", "author_association": "OWNER", "body": "Here's the code that calls the renderers - this needs to be expanded to check for those extra optional arguments:\r\n\r\nhttps://github.com/simonw/datasette/blob/2d099ad9c657d2cab59de91cdb8bfed2da236ef6/datasette/views/base.py#L387-L398", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634853296", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634853296, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg1MzI5Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T18:22:46Z", "updated_at": "2020-05-27T18:22:46Z", "author_association": "OWNER", "body": "While I'm doing this, another feature I would like is the ability for renderers to opt-in / opt-out of being displayed as options on the page.\r\n\r\nhttps://www.niche-museums.com/browse/museums for example shows a `atom` link because the `datasette-atom` plugin is installed... but clicking it will give you a 400 error because the correct columns are not present:\r\n\r\n\"browse__museums__102_rows\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634856748", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634856748, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg1Njc0OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T18:28:32Z", "updated_at": "2020-05-27T18:28:32Z", "author_association": "OWNER", "body": "Here's the code that passes a list of renderers to the template:\r\n\r\nhttps://github.com/simonw/datasette/blob/2d099ad9c657d2cab59de91cdb8bfed2da236ef6/datasette/views/base.py#L411-L423\r\n\r\nA renderer is currently defined as a two-key dictionary:\r\n```python\r\n@hookimpl\r\ndef register_output_renderer(datasette):\r\n return {\r\n 'extension': 'test',\r\n 'callback': render_test\r\n }\r\n```\r\nI can add a third key, `\"should_suggest\"` which is a function that returns `True` or `False` for a given query. If that key is missing it is assumed to return `True`.\r\n\r\nOne catch: what arguments should be passed to the `should_suggest(...)` function?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634857975", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634857975, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg1Nzk3NQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T18:30:29Z", "updated_at": "2020-05-27T18:30:29Z", "author_association": "OWNER", "body": "I'll use #770 for the `should_suggest` mechanism - this issue is for the extra arguments passed to the rendering callback.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634865620", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634865620, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg2NTYyMA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T18:44:06Z", "updated_at": "2020-05-27T18:44:06Z", "author_association": "OWNER", "body": "The existing render callback takes the following arguments:\r\n\r\n> `args` - dictionary\r\n> The GET parameters of the request\r\n> \r\n> `data` - dictionary\r\n> The data to be rendered\r\n> \r\n> `view_name` - string\r\n> The name of the view where the renderer is being called. (`index`, `database`, `table`, and `row` are the most important ones.)\r\n\r\nThe `data` argument is a bit of a problem, because it tightly couples plugins to a currently undocumented datastructure within Datasette. Here's how `datasette-atom` picks that apart for example: https://github.com/simonw/datasette-atom/blob/095941c23c81b70c4787cdeef873c556b573b5fa/datasette_atom/__init__.py#L15-L66 - it does things like access `data[\"query\"][\"sql\"]` to figure out the SQL query that was used.\r\n\r\nI'm going to change the design of part of this ticket. I won't break the old `data` value just yet, but I'll mark it to be deprecated by Datasette 1.0.\r\n\r\nI think the only plugins using it right now are my `datasette-atom` and `datasette-ics` and @russss's [datasette-geo](https://github.com/russss/datasette-geo/blob/0d24c9fd782eeae8d136ee791143ee8cbf49ebdf/datasette_plugin_geo/__init__.py#L94-L101) so hopefully changing this won't cause any wider damage.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634879734", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634879734, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg3OTczNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T19:10:17Z", "updated_at": "2020-05-27T19:12:36Z", "author_association": "OWNER", "body": "The `should_suggest` callback will take the same arguments: https://github.com/simonw/datasette/issues/770#issuecomment-634880090", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/770#issuecomment-634880474", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/770", "id": 634880474, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg4MDQ3NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T19:11:39Z", "updated_at": "2020-05-27T19:11:39Z", "author_association": "OWNER", "body": "I'm going to rename `callback` to `render` but continue supporting `callback` until Datasette 1.0.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 625930207, "label": "register_output_renderer can_render mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634881287", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634881287, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg4MTI4Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T19:13:09Z", "updated_at": "2020-05-27T19:13:09Z", "author_association": "OWNER", "body": "I think I need a utility function for \"call this function with this dictionary of arguments, but only pass the arguments which are inspected by the function\".", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634882112", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634882112, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg4MjExMg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T19:14:55Z", "updated_at": "2020-05-27T19:14:55Z", "author_association": "OWNER", "body": "https://docs.python.org/3/library/inspect.html#introspecting-callables-with-the-signature-object\r\n\r\n> New in version 3.3.\r\n> \r\n> The Signature object represents the call signature of a callable object and its return annotation. To retrieve a Signature object, use the [`signature()`](https://docs.python.org/3/library/inspect.html#inspect.signature \"inspect.signature\") function.\r\n> \r\n> `inspect.``signature`(*callable*, ***, *follow_wrapped=True*)\r\n> \r\n> Return a [`Signature`](https://docs.python.org/3/library/inspect.html#inspect.Signature \"inspect.Signature\") object for the given `callable`:\r\n> \r\n> ```\r\n> >>> from inspect import signature\r\n> >>> def foo(a, *, b:int, **kwargs):\r\n> ... pass\r\n> \r\n> >>> sig = signature(foo)\r\n> \r\n> >>> str(sig)\r\n> '(a, *, b:int, **kwargs)'\r\n> \r\n> >>> str(sig.parameters['b'])\r\n> 'b:int'\r\n> \r\n> >>> sig.parameters['b'].annotation\r\n> \r\n> ```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634882770", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634882770, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg4Mjc3MA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T19:16:19Z", "updated_at": "2020-05-27T19:16:19Z", "author_association": "OWNER", "body": "```\r\nIn [1]: import inspect \r\n\r\nIn [2]: def foo(view, sql, inspect): \r\n ...: pass \r\n ...: \r\n\r\nIn [3]: inspect.signature(foo) \r\nOut[3]: \r\n\r\nIn [4]: inspect.signature(foo).parameters \r\nOut[4]: \r\nmappingproxy({'view': ,\r\n 'sql': ,\r\n 'inspect': })\r\n\r\nIn [5]: inspect.signature(foo).parameters.keys() \r\nOut[5]: odict_keys(['view', 'sql', 'inspect'])\r\n\r\nIn [6]: set(inspect.signature(foo).parameters.keys()) \r\nOut[6]: {'inspect', 'sql', 'view'}\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634888582", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634888582, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg4ODU4Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T19:23:23Z", "updated_at": "2020-05-27T19:23:23Z", "author_association": "OWNER", "body": "Here's the function I just wrote for this:\r\n```python\r\ndef call_with_supported_arguments(fn, **kwargs):\r\n parameters = inspect.signature(fn).parameters.keys()\r\n call_with = []\r\n for parameter in parameters:\r\n if parameter not in kwargs:\r\n raise TypeError(\"{} requires parameters {}\".format(fn, tuple(parameters)))\r\n call_with.append(kwargs[parameter])\r\n return fn(*call_with)\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634893744", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634893744, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDg5Mzc0NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T19:32:08Z", "updated_at": "2020-05-27T19:32:08Z", "author_association": "OWNER", "body": "Need to figure out how best to unit test this plugin hook.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/773#issuecomment-634940522", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/773", "id": 634940522, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk0MDUyMg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T21:07:48Z", "updated_at": "2020-05-27T21:07:48Z", "author_association": "OWNER", "body": "Remove this `xfail` decorator once they are all tested: https://github.com/simonw/datasette/blob/da87e963bff24e47878a5bc2025c8bfc63d4bc93/tests/test_plugins.py#L23-L28", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 626001501, "label": "All plugin hooks should have unit tests"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634943336", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634943336, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk0MzMzNg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T21:13:04Z", "updated_at": "2020-05-27T21:13:04Z", "author_association": "OWNER", "body": "Since I'm passing `request` I won't pass `scope` - if people want that they can access `request.scope`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634944832", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634944832, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk0NDgzMg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T21:15:50Z", "updated_at": "2020-05-27T21:16:28Z", "author_association": "OWNER", "body": "It bothers me that `query_name` here means the configured name of the canned query, but `view_name` means the name of the Datasette view class, NOT the name of an associated SQL view. That's in `table`.\r\n\r\nCan I come up with clearer names for these?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634946197", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634946197, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk0NjE5Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T21:18:30Z", "updated_at": "2020-05-27T21:18:30Z", "author_association": "OWNER", "body": "I'm going to break backwards compatibility directly here, without waiting for Datasette 1.0.\r\n\r\nThe reason is that https://github.com/russss/datasette-geo hasn't been updated in 13 months so is already broken against current Datasette, and the other two plugins using this hook are owned by me so I can upgrade them myself.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634946319", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634946319, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk0NjMxOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T21:18:50Z", "updated_at": "2020-05-27T21:18:50Z", "author_association": "OWNER", "body": "(I used GitHub code search to find code using this plugin hook: https://github.com/search?q=register_output_renderer&type=Code )", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/758#issuecomment-634950200", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/758", "id": 634950200, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk1MDIwMA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T21:26:37Z", "updated_at": "2020-05-27T21:26:37Z", "author_association": "OWNER", "body": "https://latest.datasette.io/.json currently returns:\r\n\r\n```\r\n{\r\n \"fixtures\": {\r\n \"name\": \"fixtures\",\r\n \"hash\": \"87b3f2c55dfb81ff1452dd306c2623fa5550b90982cfa32bad404c4d8bbedde2\",\r\n \"color\": \"87b3f2\",\r\n \"path\": \"/fixtures\",\r\n \"tables_and_views_truncated\": [\r\n```\r\n\r\nI published `fixtures.db` here like this:\r\n\r\n datasette publish cloudrun fixtures.db --service datasette-hash-urls --extra-options '--config hash_urls:1'\r\n\r\nhttps://datasette-hash-urls-j7hipcg4aq-uw.a.run.app/.json\r\n```\r\n{\r\n \"fixtures\": {\r\n \"name\": \"fixtures\",\r\n \"hash\": \"bda7daa889c23f9a8f06e46d7d280dd423c76275e9593c4c1cad7c53b19032fe\",\r\n \"color\": \"bda7da\",\r\n \"path\": \"/fixtures-bda7daa\",\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 612382643, "label": "Question: Access to immutable database-path"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/758#issuecomment-634951605", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/758", "id": 634951605, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk1MTYwNQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T21:29:19Z", "updated_at": "2020-05-27T21:29:19Z", "author_association": "OWNER", "body": "But... https://datasette-hash-urls-j7hipcg4aq-uw.a.run.app/fixtures-bda7daa.json doesn't expose that hash:\r\n```\r\n{\r\n \"database\": \"fixtures\",\r\n \"size\": 258048,\r\n \"tables\": [\r\n {\r\n \"name\": \"123_starts_with_digits\",\r\n```\r\nLikewise https://datasette-hash-urls-j7hipcg4aq-uw.a.run.app/fixtures-bda7daa/complex_foreign_keys.json\r\n```\r\n{\r\n \"database\": \"fixtures\",\r\n \"table\": \"complex_foreign_keys\",\r\n \"is_view\": false,\r\n \"human_description_en\": \"\",\r\n \"rows\": [\r\n [\r\n \"1\",\r\n \"1\",\r\n \"2\",\r\n \"1\"\r\n ]\r\n ],\r\n```\r\nAnd https://datasette-hash-urls-j7hipcg4aq-uw.a.run.app/fixtures-bda7daa/complex_foreign_keys/1.json\r\n```\r\n{\r\n \"database\": \"fixtures\",\r\n \"table\": \"complex_foreign_keys\",\r\n \"rows\": [\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 612382643, "label": "Question: Access to immutable database-path"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634964294", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634964294, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk2NDI5NA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T21:57:10Z", "updated_at": "2020-05-27T21:57:10Z", "author_association": "OWNER", "body": "Right now a rendering callback returns the following:\r\n```\r\nbody - string or bytes, optional\r\n The response body, default empty\r\ncontent_type - string, optional\r\n The Content-Type header, default text/plain\r\nstatus_code - integer, optional\r\n The HTTP status code, default 200 \r\n```\r\nI'm going to add an optional `headers` dictionary key, too.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634964457", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634964457, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk2NDQ1Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T21:57:35Z", "updated_at": "2020-05-27T21:57:35Z", "author_association": "OWNER", "body": "(I wonder if this would be enough to allow really smart plugins to implement ETag/conditional get)", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/706#issuecomment-634965148", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/706", "id": 634965148, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk2NTE0OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T21:59:07Z", "updated_at": "2020-05-27T21:59:07Z", "author_association": "OWNER", "body": "This is the full current implementation of the request object:\r\nhttps://github.com/simonw/datasette/blob/9424687e9e94401438896116898a071702b09d40/datasette/utils/asgi.py#L15-L95", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 585633142, "label": "Documentation for the \"request\" object"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/706#issuecomment-634973596", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/706", "id": 634973596, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk3MzU5Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T22:19:02Z", "updated_at": "2020-05-27T22:19:02Z", "author_association": "OWNER", "body": "New documentation can be seen here: https://github.com/simonw/datasette/blob/6d7cb02f00010d3cb4b4bac0460d41277652b80e/docs/internals.rst#request-object\r\n\r\nIt's inspired me to reconsider the name of the `.raw_args` property, which isn't particularly clear.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 585633142, "label": "Documentation for the \"request\" object"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/706#issuecomment-634974088", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/706", "id": 634974088, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk3NDA4OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T22:20:20Z", "updated_at": "2020-05-27T22:20:20Z", "author_association": "OWNER", "body": "It looks like I inherited `.raw_args` from Sanic - I use it in a few places: https://github.com/search?q=user%3Asimonw+raw_args&type=Code", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 585633142, "label": "Documentation for the \"request\" object"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/706#issuecomment-634974819", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/706", "id": 634974819, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk3NDgxOQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T22:22:20Z", "updated_at": "2020-05-27T22:22:20Z", "author_association": "OWNER", "body": "What would a better name be?\r\n\r\n- `.simple_args`\r\n- `.kv_args`\r\n- `.pair_args`\r\n- `.dict_args`\r\n- `.args_dict`\r\n\r\nI dislike the last two the least.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 585633142, "label": "Documentation for the \"request\" object"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/706#issuecomment-634975252", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/706", "id": 634975252, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk3NTI1Mg==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T22:23:26Z", "updated_at": "2020-05-27T22:30:05Z", "author_association": "OWNER", "body": "I'm going to leave `.raw_args` in for the moment but deliberately not document it. I'll hope to phase it out entirely at a later date.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 585633142, "label": "Documentation for the \"request\" object"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634978388", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634978388, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk3ODM4OA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T22:32:03Z", "updated_at": "2020-05-27T22:32:03Z", "author_association": "OWNER", "body": "Request object is now documented: https://datasette.readthedocs.io/en/latest/internals.html#request-object", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/770#issuecomment-634980179", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/770", "id": 634980179, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDk4MDE3OQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T22:37:19Z", "updated_at": "2020-05-27T22:37:19Z", "author_association": "OWNER", "body": "Can I come up with a better name than `should_suggest`?\r\n\r\nIt's a check that sees if the current query is supported by the renderer plugin. Some options:\r\n\r\n- `can_render`\r\n- `supports_query`\r\n- `is_supported`\r\n\r\nI like `can_render`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 625930207, "label": "register_output_renderer can_render mechanism"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/771#issuecomment-634900776", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/771", "id": 634900776, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDkwMDc3Ng==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T19:44:25Z", "updated_at": "2020-05-27T19:44:25Z", "author_association": "OWNER", "body": "This seems to work:\r\n```diff\r\ndiff --git a/tests/test_plugins.py b/tests/test_plugins.py\r\nindex 8b6a6b4..e9a40aa 100644\r\n--- a/tests/test_plugins.py\r\n+++ b/tests/test_plugins.py\r\n@@ -7,7 +7,7 @@ from .fixtures import (\r\n TestClient as _TestClient,\r\n ) # noqa\r\n from datasette.app import Datasette\r\n-from datasette.plugins import get_plugins, DEFAULT_PLUGINS\r\n+from datasette.plugins import get_plugins, DEFAULT_PLUGINS, pm\r\n from datasette.utils import sqlite3\r\n import base64\r\n import json\r\n@@ -20,6 +20,21 @@ import pytest\r\n import urllib\r\n \r\n \r\n+def test_plugin_hooks_have_tests():\r\n+ \"Every plugin hook should be referenced in this test module\"\r\n+ hooks = [name for name in dir(pm.hook) if not name.startswith(\"_\")]\r\n+ tests_in_this_module = [t for t in globals().keys() if t.startswith('test_')]\r\n+ untested = []\r\n+ for hook in hooks:\r\n+ ok = False\r\n+ for test in tests_in_this_module:\r\n+ if hook in test:\r\n+ ok = True\r\n+ if not ok:\r\n+ untested.append(hook)\r\n+ assert not untested, 'These plugin hooks are missing tests: {}'.format(untested)\r\n+\r\n+\r\n def test_plugins_dir_plugin_prepare_connection(app_client):\r\n response = app_client.get(\r\n \"/fixtures.json?sql=select+convert_units(100%2C+'m'%2C+'ft')\"\r\n```\r\nBased on how the documentation unit tests work.\r\n\r\nCurrently fails with:\r\n\r\n AssertionError: These plugin hooks are missing tests:\r\n ['prepare_jinja2_environment', 'publish_subcommand', 'register_facet_classes', 'register_output_renderer']\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 625980317, "label": "Unit test that checks that all plugin hooks have corresponding unit tests"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/771#issuecomment-634909347", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/771", "id": 634909347, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDkwOTM0Nw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T20:01:52Z", "updated_at": "2020-05-27T20:01:52Z", "author_association": "OWNER", "body": "I'll do the work for this in the pull request #772.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 625980317, "label": "Unit test that checks that all plugin hooks have corresponding unit tests"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/771#issuecomment-634909818", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/771", "id": 634909818, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDkwOTgxOA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T20:02:52Z", "updated_at": "2020-05-27T20:02:52Z", "author_association": "OWNER", "body": "Actually I'll land this using `@pytest.mark.xfail`.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 625980317, "label": "Unit test that checks that all plugin hooks have corresponding unit tests"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/771#issuecomment-634915104", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/771", "id": 634915104, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDkxNTEwNA==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T20:14:32Z", "updated_at": "2020-05-27T20:14:32Z", "author_association": "OWNER", "body": "```\r\n$ pytest -k test_plugin_hooks_have_tests -vv\r\n====================================== test session starts ======================================\r\nplatform darwin -- Python 3.7.7, pytest-5.2.4, py-1.8.1, pluggy-0.13.1 -- /Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/bin/python\r\ncachedir: .pytest_cache\r\nrootdir: /Users/simon/Dropbox/Development/datasette, inifile: pytest.ini\r\nplugins: asyncio-0.10.0\r\ncollected 486 items / 475 deselected / 11 selected \r\n\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[asgi_wrapper] XPASS [ 9%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[extra_body_script] XPASS [ 18%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[extra_css_urls] XPASS [ 27%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[extra_js_urls] XPASS [ 36%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[extra_template_vars] XPASS [ 45%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[prepare_connection] XPASS [ 54%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[prepare_jinja2_environment] XFAIL [ 63%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[publish_subcommand] XFAIL [ 72%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[register_facet_classes] XFAIL [ 81%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[register_output_renderer] XFAIL [ 90%]\r\ntests/test_plugins.py::test_plugin_hooks_have_tests[render_cell] XPASS [100%]\r\n\r\n========================= 475 deselected, 4 xfailed, 7 xpassed in 1.70s =========================", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 625980317, "label": "Unit test that checks that all plugin hooks have corresponding unit tests"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/771#issuecomment-634916313", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/771", "id": 634916313, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDkxNjMxMw==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T20:17:13Z", "updated_at": "2020-05-27T20:17:13Z", "author_association": "OWNER", "body": "Closed in da87e963bff24e47878a5bc2025c8bfc63d4bc93", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 625980317, "label": "Unit test that checks that all plugin hooks have corresponding unit tests"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/581#issuecomment-634921101", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/581", "id": 634921101, "node_id": "MDEyOklzc3VlQ29tbWVudDYzNDkyMTEwMQ==", "user": {"value": 9599, "label": "simonw"}, "created_at": "2020-05-27T20:27:36Z", "updated_at": "2020-05-27T20:27:36Z", "author_association": "OWNER", "body": "Actually passing the `request` object would be OK if I document it see https://github.com/simonw/datasette/issues/706", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 502993509, "label": "Redesign register_output_renderer callback"}, "performed_via_github_app": null}