{"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1110219185", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1110219185, "node_id": "IC_kwDOBm6k_c5CLJmx", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T20:28:40Z", "updated_at": "2022-04-26T20:56:48Z", "author_association": "OWNER", "body": "The refactor I did in #1719 pretty much clashes with all of the changes in https://github.com/simonw/datasette/commit/5053f1ea83194ecb0a5693ad5dada5b25bf0f7e6 so I'll probably need to start my `api-extras` branch again from scratch.\r\n\r\nUsing a new `tableview-asyncinject` branch.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1110239536", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1110239536, "node_id": "IC_kwDOBm6k_c5CLOkw", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T20:54:53Z", "updated_at": "2022-04-26T20:54:53Z", "author_association": "OWNER", "body": "`pytest tests/test_table_*` runs the tests quickly.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1110238896", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1110238896, "node_id": "IC_kwDOBm6k_c5CLOaw", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T20:53:59Z", "updated_at": "2022-04-26T20:53:59Z", "author_association": "OWNER", "body": "I'm going to rename `database` to `database_name` and `table` to `table_name` to avoid confusion with the `Database` object as opposed to the string name for the database.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1110229319", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1110229319, "node_id": "IC_kwDOBm6k_c5CLMFH", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T20:41:32Z", "updated_at": "2022-04-26T20:44:38Z", "author_association": "OWNER", "body": "This time I'm not going to bother with the `filter_args` thing - I'm going to just try to use `asyncinject` to execute some big high level things in parallel - facets, suggested facets, counts, the query - and then combine it with the `extras` mechanism I'm trying to introduce too.\r\n\r\nMost importantly: I want that `extra_template()` function that adds more template context for the HTML to be executed as part of an `asyncinject` flow!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1110212021", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1110212021, "node_id": "IC_kwDOBm6k_c5CLH21", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T20:20:27Z", "updated_at": "2022-04-26T20:20:27Z", "author_association": "OWNER", "body": "Closing this because I have a good enough idea of the design for now - the details of the parameters can be figured out when I implement this.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109309683", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109309683, "node_id": "IC_kwDOBm6k_c5CHrjz", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T04:12:39Z", "updated_at": "2022-04-26T04:12:39Z", "author_association": "OWNER", "body": "I think the rough shape of the three plugin hooks is right. The detailed decisions that are needed concern what the parameters should be, which I think will mainly happen as part of:\r\n\r\n- #1715", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109306070", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109306070, "node_id": "IC_kwDOBm6k_c5CHqrW", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T04:05:20Z", "updated_at": "2022-04-26T04:05:20Z", "author_association": "OWNER", "body": "The proposed plugin for annotations - allowing users to attach comments to database tables, columns and rows - would be a great application for all three of those `?_extra=` plugin hooks.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109305184", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109305184, "node_id": "IC_kwDOBm6k_c5CHqdg", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T04:03:35Z", "updated_at": "2022-04-26T04:03:35Z", "author_association": "OWNER", "body": "I bet there's all kinds of interesting potential extras that could be calculated by loading the results of the query into a Pandas DataFrame.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109200774", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109200774, "node_id": "IC_kwDOBm6k_c5CHQ-G", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T01:25:43Z", "updated_at": "2022-04-26T01:26:15Z", "author_association": "OWNER", "body": "Had a thought: if a custom HTML template is going to make use of stuff generated using these extras, it will need a way to tell Datasette to execute those extras even in the absence of the `?_extra=...` URL parameters.\r\n\r\nIs that necessary? Or should those kinds of plugins use the existing `extra_template_vars` hook instead?\r\n\r\nOr maybe the `extra_template_vars` hook gets redesigned so it can depend on other `extras` in some way?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109200335", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109200335, "node_id": "IC_kwDOBm6k_c5CHQ3P", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T01:24:47Z", "updated_at": "2022-04-26T01:24:47Z", "author_association": "OWNER", "body": "Sketching out a `?_extra=statistics` table plugin:\r\n\r\n```python\r\nfrom datasette import hookimpl\r\n\r\n@hookimpl\r\ndef register_table_extras(datasette):\r\n return [statistics]\r\n\r\nasync def statistics(datasette, query, columns, sql):\r\n # ... need to figure out which columns are integer/floats\r\n # then build and execute a SQL query that calculates sum/avg/etc for each column\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/428#issuecomment-1109190401", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/428", "id": 1109190401, "node_id": "IC_kwDOCGYnMM5CHOcB", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T01:05:29Z", "updated_at": "2022-04-26T01:05:29Z", "author_association": "OWNER", "body": "Django makes extensive use of savepoints for nested transactions: https://docs.djangoproject.com/en/4.0/topics/db/transactions/#savepoints", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215216249, "label": "Research adding support for savepoints"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109174715", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109174715, "node_id": "IC_kwDOBm6k_c5CHKm7", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T00:40:13Z", "updated_at": "2022-04-26T00:43:33Z", "author_association": "OWNER", "body": "Some of the things I'd like to use `?_extra=` for, that may or not make sense as plugins:\r\n\r\n- Performance breakdown information, maybe including explain output for a query/table\r\n- Information about the tables that were consulted in a query - imagine pulling in additional table metadata\r\n- Statistical aggregates against the full set of results. This may well be a Datasette core feature at some point in the future, but being able to provide it early as a plugin would be really cool.\r\n- For tables, what are the other tables they can join against?\r\n- Suggested facets\r\n- Facet results themselves\r\n- New custom facets I haven't thought of - though the `register_facet_classes` hook covers that already\r\n- Table schema\r\n- Table metadata\r\n- Analytics - how many times has this table been queried? Would be a plugin thing\r\n- For geospatial data, how about a GeoJSON polygon that represents the bounding box for all returned results? Effectively this is an extra aggregation.\r\n\r\nLooking at https://github-to-sqlite.dogsheep.net/github/commits.json?_labels=on&_shape=objects for inspiration.\r\n\r\nI think there's a separate potential mechanism in the future that lets you add custom columns to a table. This would affect `.csv` and the HTML presentation too, which makes it a different concept from the `?_extra=` hook that affects the JSON export (and the context that is fed to the HTML templates).", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109171871", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109171871, "node_id": "IC_kwDOBm6k_c5CHJ6f", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T00:34:48Z", "updated_at": "2022-04-26T00:34:48Z", "author_association": "OWNER", "body": "Let's try sketching out a `register_table_extras` plugin for something new.\r\n\r\nThe first idea I came up with suggests adding new fields to the individual row records that come back - my mental model for extras so far has been that they add new keys to the root object.\r\n\r\nSo if a table result looked like this:\r\n\r\n```json\r\n{\r\n \"rows\": [\r\n {\"id\": 1, \"name\": \"Cleo\"},\r\n {\"id\": 2, \"name\": \"Suna\"}\r\n ],\r\n \"next_url\": null\r\n}\r\n```\r\nI was initially thinking that `?_extra=facets` would add a `\"facets\": {...}` key to that root object.\r\n\r\nHere's a plugin idea I came up with that would probably justify adding to the individual row objects instead:\r\n\r\n- `?_extra=check404s` - does an async `HEAD` request against every column value that looks like a URL and checks if it returns a 404\r\n\r\nThis could also work by adding a `\"check404s\": {\"url-here\": 200}` key to the root object though.\r\n\r\nI think I need some better plugin concepts before committing to this new hook. There's overlap between this and how I want the enrichments mechanism ([see here](https://simonwillison.net/2021/Jan/17/weeknotes-still-pretty-distracted/)) to work.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109165411", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109165411, "node_id": "IC_kwDOBm6k_c5CHIVj", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T00:22:42Z", "updated_at": "2022-04-26T00:22:42Z", "author_association": "OWNER", "body": "Passing `pk_values` to the plugin hook feels odd. I think I'd pass a `row` object instead and let the code look up the primary key values on that row (by introspecting the primary keys for the table).", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109164803", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109164803, "node_id": "IC_kwDOBm6k_c5CHIMD", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T00:21:40Z", "updated_at": "2022-04-26T00:21:40Z", "author_association": "OWNER", "body": "What would the existing https://latest.datasette.io/fixtures/simple_primary_key/1.json?_extras=foreign_key_tables feature look like if it was re-imagined as a `register_row_extras()` plugin?\r\n\r\nRough sketch, copying most of the code from https://github.com/simonw/datasette/blob/579f59dcec43a91dd7d404e00b87a00afd8515f2/datasette/views/row.py#L98\r\n\r\n```python\r\nfrom datasette import hookimpl\r\n\r\n@hookimpl\r\ndef register_row_extras(datasette):\r\n return [foreign_key_tables]\r\n\r\nasync def foreign_key_tables(datasette, database, table, pk_values):\r\n if len(pk_values) != 1:\r\n return []\r\n db = datasette.get_database(database)\r\n all_foreign_keys = await db.get_all_foreign_keys()\r\n foreign_keys = all_foreign_keys[table][\"incoming\"]\r\n if len(foreign_keys) == 0:\r\n return []\r\n\r\n sql = \"select \" + \", \".join(\r\n [\r\n \"(select count(*) from {table} where {column}=:id)\".format(\r\n table=escape_sqlite(fk[\"other_table\"]),\r\n column=escape_sqlite(fk[\"other_column\"]),\r\n )\r\n for fk in foreign_keys\r\n ]\r\n )\r\n try:\r\n rows = list(await db.execute(sql, {\"id\": pk_values[0]}))\r\n except QueryInterrupted:\r\n # Almost certainly hit the timeout\r\n return []\r\n\r\n foreign_table_counts = dict(\r\n zip(\r\n [(fk[\"other_table\"], fk[\"other_column\"]) for fk in foreign_keys],\r\n list(rows[0]),\r\n )\r\n )\r\n foreign_key_tables = []\r\n for fk in foreign_keys:\r\n count = (\r\n foreign_table_counts.get((fk[\"other_table\"], fk[\"other_column\"])) or 0\r\n )\r\n key = fk[\"other_column\"]\r\n if key.startswith(\"_\"):\r\n key += \"__exact\"\r\n link = \"{}?{}={}\".format(\r\n self.ds.urls.table(database, fk[\"other_table\"]),\r\n key,\r\n \",\".join(pk_values),\r\n )\r\n foreign_key_tables.append({**fk, **{\"count\": count, \"link\": link}})\r\n return foreign_key_tables\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109162123", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109162123, "node_id": "IC_kwDOBm6k_c5CHHiL", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T00:16:42Z", "updated_at": "2022-04-26T00:16:51Z", "author_association": "OWNER", "body": "Actually I'm going to imitate the existing `register_*` hooks:\r\n\r\n- `def register_output_renderer(datasette)`\r\n- `def register_facet_classes()`\r\n- `def register_routes(datasette)`\r\n- `def register_commands(cli)`\r\n- `def register_magic_parameters(datasette)`\r\n\r\nSo I'm going to call the new hooks:\r\n\r\n- `register_table_extras(datasette)`\r\n- `register_row_extras(datasette)`\r\n- `register_query_extras(datasette)`\r\n\r\nThey'll return a list of `async def` functions. The names of those functions will become the names of the extras.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109160226", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109160226, "node_id": "IC_kwDOBm6k_c5CHHEi", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T00:14:11Z", "updated_at": "2022-04-26T00:14:11Z", "author_association": "OWNER", "body": "There are four existing plugin hooks that include the word \"extra\" but use it to mean something else - to mean additional CSS/JS/variables to be injected into the page:\r\n\r\n- `def extra_css_urls(...)`\r\n- `def extra_js_urls(...)`\r\n- `def extra_body_script(...)`\r\n- `def extra_template_vars(...)`\r\n\r\nI think `extra_*` and `*_extras` are different enough that they won't be confused with each other.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109159307", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109159307, "node_id": "IC_kwDOBm6k_c5CHG2L", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T00:12:28Z", "updated_at": "2022-04-26T00:12:28Z", "author_association": "OWNER", "body": "I'm going to keep table and row separate. So I think I need to add three new plugin hooks:\r\n\r\n- `table_extras()`\r\n- `row_extras()`\r\n- `query_extras()`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1720#issuecomment-1109158903", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1720", "id": 1109158903, "node_id": "IC_kwDOBm6k_c5CHGv3", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-26T00:11:42Z", "updated_at": "2022-04-26T00:11:42Z", "author_association": "OWNER", "body": "Places this plugin hook (or hooks?) should be able to affect:\r\n\r\n- JSON for a table/view\r\n- JSON for a row\r\n- JSON for a canned query\r\n- JSON for a custom arbitrary query\r\n\r\nI'm going to combine those last two, which means there are three places. But maybe I can combine the table one and the row one as well?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1215174094, "label": "Design plugin hook for extras"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1719#issuecomment-1108907238", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1719", "id": 1108907238, "node_id": "IC_kwDOBm6k_c5CGJTm", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-25T18:34:21Z", "updated_at": "2022-04-25T18:34:21Z", "author_association": "OWNER", "body": "Well this refactor turned out to be pretty quick and really does greatly simplify both the `RowView` and `TableView` classes. Very happy with this.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1214859703, "label": "Refactor `RowView` and remove `RowTableShared`"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/262#issuecomment-1108890170", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/262", "id": 1108890170, "node_id": "IC_kwDOBm6k_c5CGFI6", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-25T18:17:09Z", "updated_at": "2022-04-25T18:18:39Z", "author_association": "OWNER", "body": "I spotted in https://github.com/simonw/datasette/issues/1719#issuecomment-1108888494 that there's actually already an undocumented implementation of `?_extras=foreign_key_tables` - https://latest.datasette.io/fixtures/simple_primary_key/1.json?_extras=foreign_key_tables\r\n\r\nI added that feature all the way back in November 2017! https://github.com/simonw/datasette/commit/a30c5b220c15360d575e94b0e67f3255e120b916", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 323658641, "label": "Add ?_extra= mechanism for requesting extra properties in JSON"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1719#issuecomment-1108888494", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1719", "id": 1108888494, "node_id": "IC_kwDOBm6k_c5CGEuu", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-25T18:15:42Z", "updated_at": "2022-04-25T18:15:42Z", "author_association": "OWNER", "body": "Here's an undocumented feature I forgot existed: https://latest.datasette.io/fixtures/simple_primary_key/1.json?_extras=foreign_key_tables\r\n\r\n`?_extras=foreign_key_tables`\r\n\r\nhttps://github.com/simonw/datasette/blob/0bc5186b7bb4fc82392df08f99a9132f84dcb331/datasette/views/table.py#L1021-L1024\r\n\r\nIt's even covered by the tests:\r\n\r\nhttps://github.com/simonw/datasette/blob/b9c2b1cfc8692b9700416db98721fa3ec982f6be/tests/test_api.py#L691-L703", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1214859703, "label": "Refactor `RowView` and remove `RowTableShared`"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1719#issuecomment-1108884171", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1719", "id": 1108884171, "node_id": "IC_kwDOBm6k_c5CGDrL", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-25T18:10:46Z", "updated_at": "2022-04-25T18:12:45Z", "author_association": "OWNER", "body": "It looks like the only class method from that shared class needed by `RowView` is `self.display_columns_and_rows()`.\r\n\r\nWhich I've been wanting to refactor to provide to `QueryView` too:\r\n\r\n- #715", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1214859703, "label": "Refactor `RowView` and remove `RowTableShared`"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1108875068", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1108875068, "node_id": "IC_kwDOBm6k_c5CGBc8", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-25T18:03:13Z", "updated_at": "2022-04-25T18:06:33Z", "author_association": "OWNER", "body": "The `RowTableShared` class is making this a whole lot more complicated.\r\n\r\nI'm going to split the `RowView` view out into an entirely separate `views/row.py` module.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1108877454", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1108877454, "node_id": "IC_kwDOBm6k_c5CGCCO", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-25T18:04:27Z", "updated_at": "2022-04-25T18:04:27Z", "author_association": "OWNER", "body": "Pushed my WIP on this to the `api-extras` branch: 5053f1ea83194ecb0a5693ad5dada5b25bf0f7e6", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107873311", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107873311, "node_id": "IC_kwDOBm6k_c5CCM4f", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T16:24:14Z", "updated_at": "2022-04-24T16:24:14Z", "author_association": "OWNER", "body": "Wrote up what I learned in a TIL: https://til.simonwillison.net/sphinx/blacken-docs", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107873271", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107873271, "node_id": "IC_kwDOBm6k_c5CCM33", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T16:23:57Z", "updated_at": "2022-04-24T16:23:57Z", "author_association": "OWNER", "body": "Turns out I didn't need that `git diff-index` trick after all - the `blacken-docs` command returns a non-zero exit code if it changes any files.\r\n\r\nSubmitted a documentation PR to that project instead:\r\n\r\n- https://github.com/asottile/blacken-docs/pull/162", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107870788", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107870788, "node_id": "IC_kwDOBm6k_c5CCMRE", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T16:09:23Z", "updated_at": "2022-04-24T16:09:23Z", "author_association": "OWNER", "body": "One more attempt at testing the `git diff-index` trick.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107869884", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107869884, "node_id": "IC_kwDOBm6k_c5CCMC8", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T16:04:03Z", "updated_at": "2022-04-24T16:04:03Z", "author_association": "OWNER", "body": "OK, I'm expecting this one to fail at the `git diff-index --quiet HEAD --` check.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107869556", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107869556, "node_id": "IC_kwDOBm6k_c5CCL90", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T16:02:27Z", "updated_at": "2022-04-24T16:02:27Z", "author_association": "OWNER", "body": "Looking at that first error it appears to be a place where I had deliberately omitted the body of the function:\r\n\r\nhttps://github.com/simonw/datasette/blob/36573638b0948174ae237d62e6369b7d55220d7f/docs/internals.rst#L196-L211\r\n\r\nI can use `...` as the function body here to get it to pass.\r\n\r\nFixing those warnings actually helped me spot a couple of bugs, so I'm glad this happened.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107868585", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107868585, "node_id": "IC_kwDOBm6k_c5CCLup", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T15:57:10Z", "updated_at": "2022-04-24T15:57:19Z", "author_association": "OWNER", "body": "The tests failed there because of what I thought were warnings but turn out to be treated as errors:\r\n```\r\n% blacken-docs -l 60 docs/*.rst \r\ndocs/internals.rst:196: code block parse error Cannot parse: 14:0: \r\ndocs/json_api.rst:449: code block parse error Cannot parse: 1:0: \r\ndocs/testing_plugins.rst:135: code block parse error Cannot parse: 5:0: \r\n% echo $?\r\n1\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107867281", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107867281, "node_id": "IC_kwDOBm6k_c5CCLaR", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T15:49:23Z", "updated_at": "2022-04-24T15:49:23Z", "author_association": "OWNER", "body": "I'm going to push the first commit with a deliberate missing formatting to check that the tests fail.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107866013", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107866013, "node_id": "IC_kwDOBm6k_c5CCLGd", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T15:42:07Z", "updated_at": "2022-04-24T15:42:07Z", "author_association": "OWNER", "body": "In the absence of `--check` I can use this to detect if changes are applied:\r\n```zsh\r\n% git diff-index --quiet HEAD --\r\n% echo $? \r\n0\r\n% blacken-docs -l 60 docs/*.rst\r\ndocs/authentication.rst: Rewriting...\r\n...\r\n% git diff-index --quiet HEAD --\r\n% echo $? \r\n1\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107865493", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107865493, "node_id": "IC_kwDOBm6k_c5CCK-V", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T15:39:02Z", "updated_at": "2022-04-24T15:39:02Z", "author_association": "OWNER", "body": "There's no `blacken-docs --check` option so I filed a feature request:\r\n- https://github.com/asottile/blacken-docs/issues/161", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107863924", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107863924, "node_id": "IC_kwDOBm6k_c5CCKl0", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T15:30:03Z", "updated_at": "2022-04-24T15:30:03Z", "author_association": "OWNER", "body": "On the one hand, I'm not crazy about some of the indentation decisions Black made here - in particular this one, which I had indented deliberately for readability:\r\n```diff\r\n diff --git a/docs/authentication.rst b/docs/authentication.rst\r\nindex 0d98cf8..8008023 100644\r\n--- a/docs/authentication.rst\r\n+++ b/docs/authentication.rst\r\n@@ -381,11 +381,7 @@ Authentication plugins can set signed ``ds_actor`` cookies themselves like so:\r\n .. code-block:: python\r\n \r\n response = Response.redirect(\"/\")\r\n- response.set_cookie(\"ds_actor\", datasette.sign({\r\n- \"a\": {\r\n- \"id\": \"cleopaws\"\r\n- }\r\n- }, \"actor\"))\r\n+ response.set_cookie(\"ds_actor\", datasette.sign({\"a\": {\"id\": \"cleopaws\"}}, \"actor\"))\r\n```\r\nBut... consistency is a virtue. Maybe I'm OK with just this one disagreement?\r\n\r\nAlso: I've been mentally trying to keep the line lengths a bit shorter to help them be more readable on mobile devices.\r\n\r\nI'll try a different line length using `blacken-docs -l 60 docs/*.rst` instead.\r\n\r\nI like this more - here's the result for that example:\r\n```diff\r\ndiff --git a/docs/authentication.rst b/docs/authentication.rst\r\nindex 0d98cf8..2496073 100644\r\n--- a/docs/authentication.rst\r\n+++ b/docs/authentication.rst\r\n@@ -381,11 +381,10 @@ Authentication plugins can set signed ``ds_actor`` cookies themselves like so:\r\n .. code-block:: python\r\n \r\n response = Response.redirect(\"/\")\r\n- response.set_cookie(\"ds_actor\", datasette.sign({\r\n- \"a\": {\r\n- \"id\": \"cleopaws\"\r\n- }\r\n- }, \"actor\"))\r\n+ response.set_cookie(\r\n+ \"ds_actor\",\r\n+ datasette.sign({\"a\": {\"id\": \"cleopaws\"}}, \"actor\"),\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": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107863365", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107863365, "node_id": "IC_kwDOBm6k_c5CCKdF", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T15:26:41Z", "updated_at": "2022-04-24T15:26:41Z", "author_association": "OWNER", "body": "Tried this:\r\n```\r\npip install blacken-docs\r\nblacken-docs docs/*.rst\r\ngit diff | pbcopy\r\n```\r\nGot this:\r\n```diff\r\n diff --git a/docs/authentication.rst b/docs/authentication.rst\r\nindex 0d98cf8..8008023 100644\r\n--- a/docs/authentication.rst\r\n+++ b/docs/authentication.rst\r\n@@ -381,11 +381,7 @@ Authentication plugins can set signed ``ds_actor`` cookies themselves like so:\r\n .. code-block:: python\r\n \r\n response = Response.redirect(\"/\")\r\n- response.set_cookie(\"ds_actor\", datasette.sign({\r\n- \"a\": {\r\n- \"id\": \"cleopaws\"\r\n- }\r\n- }, \"actor\"))\r\n+ response.set_cookie(\"ds_actor\", datasette.sign({\"a\": {\"id\": \"cleopaws\"}}, \"actor\"))\r\n \r\n Note that you need to pass ``\"actor\"`` as the namespace to :ref:`datasette_sign`.\r\n \r\n@@ -412,12 +408,16 @@ To include an expiry, add a ``\"e\"`` key to the cookie value containing a `base62\r\n expires_at = int(time.time()) + (24 * 60 * 60)\r\n \r\n response = Response.redirect(\"/\")\r\n- response.set_cookie(\"ds_actor\", datasette.sign({\r\n- \"a\": {\r\n- \"id\": \"cleopaws\"\r\n- },\r\n- \"e\": baseconv.base62.encode(expires_at),\r\n- }, \"actor\"))\r\n+ response.set_cookie(\r\n+ \"ds_actor\",\r\n+ datasette.sign(\r\n+ {\r\n+ \"a\": {\"id\": \"cleopaws\"},\r\n+ \"e\": baseconv.base62.encode(expires_at),\r\n+ },\r\n+ \"actor\",\r\n+ ),\r\n+ )\r\n \r\n The resulting cookie will encode data that looks something like this:\r\n \r\ndiff --git a/docs/spatialite.rst b/docs/spatialite.rst\r\nindex d1b300b..556bad8 100644\r\n--- a/docs/spatialite.rst\r\n+++ b/docs/spatialite.rst\r\n@@ -58,19 +58,22 @@ Here's a recipe for taking a table with existing latitude and longitude columns,\r\n .. code-block:: python\r\n \r\n import sqlite3\r\n- conn = sqlite3.connect('museums.db')\r\n+\r\n+ conn = sqlite3.connect(\"museums.db\")\r\n # Lead the spatialite extension:\r\n conn.enable_load_extension(True)\r\n- conn.load_extension('/usr/local/lib/mod_spatialite.dylib')\r\n+ conn.load_extension(\"/usr/local/lib/mod_spatialite.dylib\")\r\n # Initialize spatial metadata for this database:\r\n- conn.execute('select InitSpatialMetadata(1)')\r\n+ conn.execute(\"select InitSpatialMetadata(1)\")\r\n # Add a geometry column called point_geom to our museums table:\r\n conn.execute(\"SELECT AddGeometryColumn('museums', 'point_geom', 4326, 'POINT', 2);\")\r\n # Now update that geometry column with the lat/lon points\r\n- conn.execute('''\r\n+ conn.execute(\r\n+ \"\"\"\r\n UPDATE museums SET\r\n point_geom = GeomFromText('POINT('||\"longitude\"||' '||\"latitude\"||')',4326);\r\n- ''')\r\n+ \"\"\"\r\n+ )\r\n # Now add a spatial index to that column\r\n conn.execute('select CreateSpatialIndex(\"museums\", \"point_geom\");')\r\n # If you don't commit your changes will not be persisted:\r\n@@ -186,13 +189,14 @@ Here's Python code to create a SQLite database, enable SpatiaLite, create a plac\r\n .. code-block:: python\r\n \r\n import sqlite3\r\n- conn = sqlite3.connect('places.db')\r\n+\r\n+ conn = sqlite3.connect(\"places.db\")\r\n # Enable SpatialLite extension\r\n conn.enable_load_extension(True)\r\n- conn.load_extension('/usr/local/lib/mod_spatialite.dylib')\r\n+ conn.load_extension(\"/usr/local/lib/mod_spatialite.dylib\")\r\n # Create the masic countries table\r\n- conn.execute('select InitSpatialMetadata(1)')\r\n- conn.execute('create table places (id integer primary key, name text);')\r\n+ conn.execute(\"select InitSpatialMetadata(1)\")\r\n+ conn.execute(\"create table places (id integer primary key, name text);\")\r\n # Add a MULTIPOLYGON Geometry column\r\n conn.execute(\"SELECT AddGeometryColumn('places', 'geom', 4326, 'MULTIPOLYGON', 2);\")\r\n # Add a spatial index against the new column\r\n@@ -201,13 +205,17 @@ Here's Python code to create a SQLite database, enable SpatiaLite, create a plac\r\n from shapely.geometry.multipolygon import MultiPolygon\r\n from shapely.geometry import shape\r\n import requests\r\n- geojson = requests.get('https://data.whosonfirst.org/404/227/475/404227475.geojson').json()\r\n+\r\n+ geojson = requests.get(\r\n+ \"https://data.whosonfirst.org/404/227/475/404227475.geojson\"\r\n+ ).json()\r\n # Convert to \"Well Known Text\" format\r\n- wkt = shape(geojson['geometry']).wkt\r\n+ wkt = shape(geojson[\"geometry\"]).wkt\r\n # Insert and commit the record\r\n- conn.execute(\"INSERT INTO places (id, name, geom) VALUES(null, ?, GeomFromText(?, 4326))\", (\r\n- \"Wales\", wkt\r\n- ))\r\n+ conn.execute(\r\n+ \"INSERT INTO places (id, name, geom) VALUES(null, ?, GeomFromText(?, 4326))\",\r\n+ (\"Wales\", wkt),\r\n+ )\r\n conn.commit()\r\n \r\n Querying polygons using within()\r\ndiff --git a/docs/writing_plugins.rst b/docs/writing_plugins.rst\r\nindex bd60a4b..5af01f6 100644\r\n--- a/docs/writing_plugins.rst\r\n+++ b/docs/writing_plugins.rst\r\n@@ -18,9 +18,10 @@ The quickest way to start writing a plugin is to create a ``my_plugin.py`` file\r\n \r\n from datasette import hookimpl\r\n \r\n+\r\n @hookimpl\r\n def prepare_connection(conn):\r\n- conn.create_function('hello_world', 0, lambda: 'Hello world!')\r\n+ conn.create_function(\"hello_world\", 0, lambda: \"Hello world!\")\r\n \r\n If you save this in ``plugins/my_plugin.py`` you can then start Datasette like this::\r\n \r\n@@ -60,22 +61,18 @@ The example consists of two files: a ``setup.py`` file that defines the plugin:\r\n \r\n from setuptools import setup\r\n \r\n- VERSION = '0.1'\r\n+ VERSION = \"0.1\"\r\n \r\n setup(\r\n- name='datasette-plugin-demos',\r\n- description='Examples of plugins for Datasette',\r\n- author='Simon Willison',\r\n- url='https://github.com/simonw/datasette-plugin-demos',\r\n- license='Apache License, Version 2.0',\r\n+ name=\"datasette-plugin-demos\",\r\n+ description=\"Examples of plugins for Datasette\",\r\n+ author=\"Simon Willison\",\r\n+ url=\"https://github.com/simonw/datasette-plugin-demos\",\r\n+ license=\"Apache License, Version 2.0\",\r\n version=VERSION,\r\n- py_modules=['datasette_plugin_demos'],\r\n- entry_points={\r\n- 'datasette': [\r\n- 'plugin_demos = datasette_plugin_demos'\r\n- ]\r\n- },\r\n- install_requires=['datasette']\r\n+ py_modules=[\"datasette_plugin_demos\"],\r\n+ entry_points={\"datasette\": [\"plugin_demos = datasette_plugin_demos\"]},\r\n+ install_requires=[\"datasette\"],\r\n )\r\n \r\n And a Python module file, ``datasette_plugin_demos.py``, that implements the plugin:\r\n@@ -88,12 +85,12 @@ And a Python module file, ``datasette_plugin_demos.py``, that implements the plu\r\n \r\n @hookimpl\r\n def prepare_jinja2_environment(env):\r\n- env.filters['uppercase'] = lambda u: u.upper()\r\n+ env.filters[\"uppercase\"] = lambda u: u.upper()\r\n \r\n \r\n @hookimpl\r\n def prepare_connection(conn):\r\n- conn.create_function('random_integer', 2, random.randint)\r\n+ conn.create_function(\"random_integer\", 2, random.randint)\r\n \r\n \r\n Having built a plugin in this way you can turn it into an installable package using the following command::\r\n@@ -123,11 +120,13 @@ To bundle the static assets for a plugin in the package that you publish to PyPI\r\n \r\n .. code-block:: python\r\n \r\n- package_data={\r\n- 'datasette_plugin_name': [\r\n- 'static/plugin.js',\r\n- ],\r\n- },\r\n+ package_data = (\r\n+ {\r\n+ \"datasette_plugin_name\": [\r\n+ \"static/plugin.js\",\r\n+ ],\r\n+ },\r\n+ )\r\n \r\n Where ``datasette_plugin_name`` is the name of the plugin package (note that it uses underscores, not hyphens) and ``static/plugin.js`` is the path within that package to the static file.\r\n \r\n@@ -152,11 +151,13 @@ Templates should be bundled for distribution using the same ``package_data`` mec\r\n \r\n .. code-block:: python\r\n \r\n- package_data={\r\n- 'datasette_plugin_name': [\r\n- 'templates/my_template.html',\r\n- ],\r\n- },\r\n+ package_data = (\r\n+ {\r\n+ \"datasette_plugin_name\": [\r\n+ \"templates/my_template.html\",\r\n+ ],\r\n+ },\r\n+ )\r\n \r\n You can also use wildcards here such as ``templates/*.html``. See `datasette-edit-schema `__ for an example of this pattern.\r\n ```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1718#issuecomment-1107862882", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1718", "id": 1107862882, "node_id": "IC_kwDOBm6k_c5CCKVi", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T15:23:56Z", "updated_at": "2022-04-24T15:23:56Z", "author_association": "OWNER", "body": "Found https://github.com/asottile/blacken-docs via\r\n- https://github.com/psf/black/issues/294", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213683988, "label": "Code examples in the documentation should be formatted with Black"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1717#issuecomment-1107848097", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1717", "id": 1107848097, "node_id": "IC_kwDOBm6k_c5CCGuh", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-24T14:02:37Z", "updated_at": "2022-04-24T14:02:37Z", "author_association": "OWNER", "body": "This is a neat feature, thanks!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1213281044, "label": "Add timeout option to Cloudrun build"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1106989581", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1106989581, "node_id": "IC_kwDOBm6k_c5B-1IN", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-22T23:03:29Z", "updated_at": "2022-04-22T23:03:29Z", "author_association": "OWNER", "body": "I'm having second thoughts about injecting `request` - might be better to have the view function pull the relevant pieces out of the request before triggering the rest of the resolution.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1106947168", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1106947168, "node_id": "IC_kwDOBm6k_c5B-qxg", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-22T22:25:57Z", "updated_at": "2022-04-22T22:26:06Z", "author_association": "OWNER", "body": "```python\r\nasync def database(request: Request, datasette: Datasette) -> Database:\r\n database_route = tilde_decode(request.url_vars[\"database\"])\r\n try:\r\n return datasette.get_database(route=database_route)\r\n except KeyError:\r\n raise NotFound(\"Database not found: {}\".format(database_route))\r\n\r\nasync def table_name(request: Request) -> str:\r\n return tilde_decode(request.url_vars[\"table\"])\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1106945876", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1106945876, "node_id": "IC_kwDOBm6k_c5B-qdU", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-22T22:24:29Z", "updated_at": "2022-04-22T22:24:29Z", "author_association": "OWNER", "body": "Looking at the start of `TableView.data()`:\r\n\r\nhttps://github.com/simonw/datasette/blob/d57c347f35bcd8cff15f913da851b4b8eb030867/datasette/views/table.py#L333-L346\r\n\r\nI'm going to resolve `table_name` and `database` from the URL - `table_name` will be a string, `database` will be the DB object returned by `datasette.get_database()`. Then those can be passed in separately too.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1716#issuecomment-1106923258", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1716", "id": 1106923258, "node_id": "IC_kwDOBm6k_c5B-k76", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-22T22:02:07Z", "updated_at": "2022-04-22T22:02:07Z", "author_association": "OWNER", "body": "https://github.com/simonw/datasette/blame/main/datasette/views/base.py\r\n\r\n\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212838949, "label": "Configure git blame to ignore Black commit"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1715#issuecomment-1106908642", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1715", "id": 1106908642, "node_id": "IC_kwDOBm6k_c5B-hXi", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-22T21:47:55Z", "updated_at": "2022-04-22T21:47:55Z", "author_association": "OWNER", "body": "I need a `asyncio.Registry` with functions registered to perform the role of the table view.\r\n\r\nSomething like this perhaps:\r\n```python\r\ndef table_html_context(facet_results, query, datasette, rows):\r\n return {...}\r\n```\r\nThat then gets called like this:\r\n```python\r\nasync def view(request):\r\n registry = Registry(facet_results, query, datasette, rows)\r\n context = await registry.resolve(table_html, request=request, datasette=datasette)\r\n return Reponse.html(await datasette.render(\"table.html\", context)\r\n```\r\nIt's also interesting to start thinking about this from a Python client library point of view. If I'm writing code outside of the HTTP request cycle, what would it look like?\r\n\r\nOne thing I could do: break out is the code that turns a request into a list of pairs extracted from the request - this code here: https://github.com/simonw/datasette/blob/8338c66a57502ef27c3d7afb2527fbc0663b2570/datasette/views/table.py#L442-L449\r\n\r\nI could turn that into a typed dependency injection function like this:\r\n\r\n```python\r\ndef filter_args(request: Request) -> List[Tuple[str, str]]:\r\n # Arguments that start with _ and don't contain a __ are\r\n # special - things like ?_search= - and should not be\r\n # treated as filters.\r\n filter_args = []\r\n for key in request.args:\r\n if not (key.startswith(\"_\") and \"__\" not in key):\r\n for v in request.args.getlist(key):\r\n filter_args.append((key, v))\r\n return filter_args\r\n```\r\nThen I can either pass a `request` into a `.resolve()` call, or I can instead skip that function by passing:\r\n\r\n```python\r\noutput = registry.resolve(table_context, filter_args=[(\"foo\", \"bar\")])\r\n```\r\nI do need to think about where plugins get executed in all of this.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1212823665, "label": "Refactor TableView to use asyncinject"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1101#issuecomment-1105615625", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1101", "id": 1105615625, "node_id": "IC_kwDOBm6k_c5B5lsJ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-21T18:31:41Z", "updated_at": "2022-04-21T18:32:22Z", "author_association": "OWNER", "body": "The `datasette-geojson` plugin is actually an interesting case here, because of the way it converts SpatiaLite geometries into GeoJSON: https://github.com/eyeseast/datasette-geojson/blob/602c4477dc7ddadb1c0a156cbcd2ef6688a5921d/datasette_geojson/__init__.py#L61-L66\r\n\r\n```python\r\n\r\n if isinstance(geometry, bytes):\r\n results = await db.execute(\r\n \"SELECT AsGeoJSON(:geometry)\", {\"geometry\": geometry}\r\n )\r\n return geojson.loads(results.single_value())\r\n```\r\nThat actually seems to work really well as-is, but it does worry me a bit that it ends up having to execute an extra `SELECT` query for every single returned row - especially in streaming mode where it might be asked to return 1m rows at once.\r\n\r\nMy PostgreSQL/MySQL engineering brain says that this would be better handled by doing a chunk of these (maybe 100) at once, to avoid the per-query-overhead - but with SQLite that might not be necessary.\r\n\r\nAt any rate, this is one of the reasons I'm interested in \"iterate over this sequence of chunks of 100 rows at a time\" as a potential option here.\r\n\r\nOf course, a better solution would be for `datasette-geojson` to have a way to influence the SQL query before it is executed, adding a `AsGeoJSON(geometry)` clause to it - so that's something I'm open to as well.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 749283032, "label": "register_output_renderer() should support streaming data"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1101#issuecomment-1105608964", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1101", "id": 1105608964, "node_id": "IC_kwDOBm6k_c5B5kEE", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-21T18:26:29Z", "updated_at": "2022-04-21T18:26:29Z", "author_association": "OWNER", "body": "I'm questioning if the mechanisms should be separate at all now - a single response rendering is really just a case of a streaming response that only pulls the first N records from the iterator.\r\n\r\nIt probably needs to be an `async for` iterator, which I've not worked with much before. Good opportunity to learn.\r\n\r\nThis actually gets a fair bit more complicated due to the work I'm doing right now to improve the default JSON API:\r\n\r\n- #1709\r\n\r\nI want to do things like make faceting results optionally available to custom renderers - which is a separate concern from streaming rows.\r\n\r\nI'm going to poke around with a bunch of prototypes and see what sticks.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 749283032, "label": "register_output_renderer() should support streaming data"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1101#issuecomment-1105571003", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1101", "id": 1105571003, "node_id": "IC_kwDOBm6k_c5B5ay7", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-21T18:10:38Z", "updated_at": "2022-04-21T18:10:46Z", "author_association": "OWNER", "body": "Maybe the simplest design for this is to add an optional `can_stream` to the contract:\r\n\r\n```python\r\n @hookimpl\r\n def register_output_renderer(datasette):\r\n return {\r\n \"extension\": \"tsv\",\r\n \"render\": render_tsv,\r\n \"can_render\": lambda: True,\r\n \"can_stream\": lambda: True\r\n }\r\n```\r\nWhen streaming, a new parameter could be passed to the render function - maybe `chunks` - which is an iterator/generator over a sequence of chunks of rows.\r\n\r\nOr it could use the existing `rows` parameter but treat that as an iterator?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 749283032, "label": "register_output_renderer() should support streaming data"}, "performed_via_github_app": null} {"html_url": "https://github.com/dogsheep/github-to-sqlite/issues/72#issuecomment-1105474232", "issue_url": "https://api.github.com/repos/dogsheep/github-to-sqlite/issues/72", "id": 1105474232, "node_id": "IC_kwDODFdgUs5B5DK4", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-21T17:02:15Z", "updated_at": "2022-04-21T17:02:15Z", "author_association": "MEMBER", "body": "That's interesting - yeah it looks like the number of pages can be derived from the `Link` header, which is enough information to show a progress bar, probably using Click just to avoid adding another dependency.\r\n\r\nhttps://docs.github.com/en/rest/guides/traversing-with-pagination", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1211283427, "label": "feature: display progress bar when downloading multi-page responses"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/425#issuecomment-1101594549", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/425", "id": 1101594549, "node_id": "IC_kwDOCGYnMM5BqP-1", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-18T17:36:14Z", "updated_at": "2022-04-18T17:36:14Z", "author_association": "OWNER", "body": "Releated:\r\n- #408", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1203842656, "label": "`sqlite3.NotSupportedError`: deterministic=True requires SQLite 3.8.3 or higher"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1713#issuecomment-1098628334", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1713", "id": 1098628334, "node_id": "IC_kwDOBm6k_c5Be7zu", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-14T01:43:00Z", "updated_at": "2022-04-14T01:43:13Z", "author_association": "OWNER", "body": "Current workaround for fast publishing to S3:\r\n\r\n datasette fixtures.db --get /fixtures/facetable.json | \\\r\n s3-credentials put-object my-bucket facetable.json -", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1203943272, "label": "Datasette feature for publishing snapshots of query results"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/421#issuecomment-1098548931", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/421", "id": 1098548931, "node_id": "IC_kwDOCGYnMM5BeobD", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-13T22:41:59Z", "updated_at": "2022-04-13T22:41:59Z", "author_association": "OWNER", "body": "I'm going to close this ticket since it looks like this is a bug in the way the Dockerfile builds Python, but I'm going to ship a fix for that issue I found so the `LD_PRELOAD` workaround above should work OK with the next release of `sqlite-utils`. Thanks for the detailed bug report!", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1180427792, "label": "\"Error: near \"(\": syntax error\" when using sqlite-utils indexes CLI"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/424#issuecomment-1098548090", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/424", "id": 1098548090, "node_id": "IC_kwDOCGYnMM5BeoN6", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-13T22:40:15Z", "updated_at": "2022-04-13T22:40:15Z", "author_association": "OWNER", "body": "New error:\r\n```pycon\r\n>>> from sqlite_utils import Database\r\n>>> db = Database(memory=True)\r\n>>> db[\"foo\"].create({})\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"/Users/simon/Dropbox/Development/sqlite-utils/sqlite_utils/db.py\", line 1465, in create\r\n self.db.create_table(\r\n File \"/Users/simon/Dropbox/Development/sqlite-utils/sqlite_utils/db.py\", line 885, in create_table\r\n sql = self.create_table_sql(\r\n File \"/Users/simon/Dropbox/Development/sqlite-utils/sqlite_utils/db.py\", line 771, in create_table_sql\r\n assert columns, \"Tables must have at least one column\"\r\nAssertionError: Tables must have at least one column\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1200866134, "label": "Better error message if you try to create a table with no columns"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/425#issuecomment-1098545390", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/425", "id": 1098545390, "node_id": "IC_kwDOCGYnMM5Benju", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-13T22:34:52Z", "updated_at": "2022-04-13T22:34:52Z", "author_association": "OWNER", "body": "That broke Python 3.7 because it doesn't support `deterministic=True` even being passed:\r\n\r\n> function takes at most 3 arguments (4 given)", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1203842656, "label": "`sqlite3.NotSupportedError`: deterministic=True requires SQLite 3.8.3 or higher"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/425#issuecomment-1098537000", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/425", "id": 1098537000, "node_id": "IC_kwDOCGYnMM5Belgo", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-13T22:18:22Z", "updated_at": "2022-04-13T22:18:22Z", "author_association": "OWNER", "body": "I figured out a workaround in https://github.com/simonw/sqlite-utils/issues/421#issuecomment-1098535531\r\n\r\nThe current `register(fn)` method looks like this: https://github.com/simonw/sqlite-utils/blob/95522ad919f96eb6cc8cd3cd30389b534680c717/sqlite_utils/db.py#L389-L403\r\n\r\nThis alternative implementation worked in the environment where that failed:\r\n\r\n```python\r\n def register(fn):\r\n name = fn.__name__\r\n arity = len(inspect.signature(fn).parameters)\r\n if not replace and (name, arity) in self._registered_functions:\r\n return fn\r\n kwargs = {}\r\n done = False\r\n if deterministic:\r\n # Try this, but fall back if sqlite3.NotSupportedError\r\n try:\r\n self.conn.create_function(name, arity, fn, **dict(kwargs, deterministic=True))\r\n done = True\r\n except sqlite3.NotSupportedError:\r\n pass\r\n if not done:\r\n self.conn.create_function(name, arity, fn, **kwargs)\r\n self._registered_functions.add((name, arity))\r\n return fn\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1203842656, "label": "`sqlite3.NotSupportedError`: deterministic=True requires SQLite 3.8.3 or higher"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/421#issuecomment-1098535531", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/421", "id": 1098535531, "node_id": "IC_kwDOCGYnMM5BelJr", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-13T22:15:48Z", "updated_at": "2022-04-13T22:15:48Z", "author_association": "OWNER", "body": "Trying this alternative implementation of the `register()` method:\r\n\r\n```python\r\n def register(fn):\r\n name = fn.__name__\r\n arity = len(inspect.signature(fn).parameters)\r\n if not replace and (name, arity) in self._registered_functions:\r\n return fn\r\n kwargs = {}\r\n done = False\r\n if deterministic:\r\n # Try this, but fall back if sqlite3.NotSupportedError\r\n try:\r\n self.conn.create_function(name, arity, fn, **dict(kwargs, deterministic=True))\r\n done = True\r\n except sqlite3.NotSupportedError:\r\n pass\r\n if not done:\r\n self.conn.create_function(name, arity, fn, **kwargs)\r\n self._registered_functions.add((name, arity))\r\n return fn\r\n```\r\nWith that fix, the following worked!\r\n```\r\nLD_PRELOAD=./build/sqlite-autoconf-3360000/.libs/libsqlite3.so sqlite-utils indexes /tmp/global.db --table\r\ntable index_name seqno cid name desc coll key\r\n--------- -------------------------- ------- ----- ------- ------ ------ -----\r\ncountries idx_countries_country_name 0 1 country 0 BINARY 1\r\ncountries idx_countries_country_name 1 2 name 0 BINARY 1\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1180427792, "label": "\"Error: near \"(\": syntax error\" when using sqlite-utils indexes CLI"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/421#issuecomment-1098532220", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/421", "id": 1098532220, "node_id": "IC_kwDOCGYnMM5BekV8", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-13T22:09:52Z", "updated_at": "2022-04-13T22:09:52Z", "author_association": "OWNER", "body": "That error is weird - it's not supposed to happen according to this code here: https://github.com/simonw/sqlite-utils/blob/95522ad919f96eb6cc8cd3cd30389b534680c717/sqlite_utils/db.py#L389-L400", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1180427792, "label": "\"Error: near \"(\": syntax error\" when using sqlite-utils indexes CLI"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/421#issuecomment-1098531354", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/421", "id": 1098531354, "node_id": "IC_kwDOCGYnMM5BekIa", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-13T22:08:20Z", "updated_at": "2022-04-13T22:08:20Z", "author_association": "OWNER", "body": "OK I figured out what's going on here. First I added an extra `print(sql)` statement to the `indexes` command to see what SQL it was running:\r\n```\r\n(app-root) sqlite-utils indexes global.db --table\r\n\r\n select\r\n sqlite_master.name as \"table\",\r\n indexes.name as index_name,\r\n xinfo.*\r\n from sqlite_master\r\n join pragma_index_list(sqlite_master.name) indexes\r\n join pragma_index_xinfo(index_name) xinfo\r\n where\r\n sqlite_master.type = 'table'\r\n and xinfo.key = 1\r\nError: near \"(\": syntax error\r\n```\r\nThis made me suspicious that the SQLite version being used here didn't support joining against the `pragma_index_list(...)` table-valued functions in that way. So I checked the version:\r\n```\r\n(app-root) sqlite3\r\nSQLite version 3.36.0 2021-06-18 18:36:39\r\n```\r\nThat version should be fine - it's the one you compiled in the Dockerfile.\r\n\r\nThen I checked the version that `sqlite-utils` itself was using:\r\n```\r\n(app-root) sqlite-utils memory 'select sqlite_version()'\r\n[{\"sqlite_version()\": \"3.7.17\"}]\r\n```\r\nIt's running SQLite 3.7.17!\r\n\r\nSo the problem here is that the Python in that Docker image is running a very old version of SQLite.\r\n\r\nI tried using the trick in https://til.simonwillison.net/sqlite/ld-preload as a workaround, and it almost worked:\r\n\r\n```\r\n(app-root) python3 -c 'import sqlite3; print(sqlite3.connect(\":memory\").execute(\"select sqlite_version()\").fetchone())'\r\n('3.7.17',)\r\n(app-root) LD_PRELOAD=./build/sqlite-autoconf-3360000/.libs/libsqlite3.so python3 -c 'import sqlite3; print(sqlite3.connect(\":memory\").execute(\"select sqlite_version()\").fetchone())'\r\n('3.36.0',)\r\n```\r\nBut when I try to run `sqlite-utils` like that I get an error:\r\n\r\n```\r\n(app-root) LD_PRELOAD=./build/sqlite-autoconf-3360000/.libs/libsqlite3.so sqlite-utils indexes /tmp/global.db \r\n...\r\n File \"/opt/app-root/lib64/python3.8/site-packages/sqlite_utils/cli.py\", line 1624, in query\r\n db.register_fts4_bm25()\r\n File \"/opt/app-root/lib64/python3.8/site-packages/sqlite_utils/db.py\", line 412, in register_fts4_bm25\r\n self.register_function(rank_bm25, deterministic=True)\r\n File \"/opt/app-root/lib64/python3.8/site-packages/sqlite_utils/db.py\", line 408, in register_function\r\n register(fn)\r\n File \"/opt/app-root/lib64/python3.8/site-packages/sqlite_utils/db.py\", line 401, in register\r\n self.conn.create_function(name, arity, fn, **kwargs)\r\nsqlite3.NotSupportedError: deterministic=True requires SQLite 3.8.3 or higher\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1180427792, "label": "\"Error: near \"(\": syntax error\" when using sqlite-utils indexes CLI"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/421#issuecomment-1098295517", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/421", "id": 1098295517, "node_id": "IC_kwDOCGYnMM5Bdqjd", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-13T17:16:20Z", "updated_at": "2022-04-13T17:16:20Z", "author_association": "OWNER", "body": "Aha! I was able to replicate the bug using your `Dockerfile` - thanks very much for providing that.\r\n```\r\n(app-root) sqlite-utils indexes global.db --table\r\nError: near \"(\": syntax error\r\n```\r\n(That wa sbefore I even ran the `extract` command.)\r\n\r\nTo build your `Dockerfile` I copied it into an empty folder and ran the following:\r\n```\r\nwget https://www.sqlite.org/2021/sqlite-autoconf-3360000.tar.gz\r\ndocker build . -t centos-sqlite-utils\r\ndocker run -it centos-sqlite-utils /bin/bash\r\n```\r\nThis gave me a shell in which I could replicate the bug.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1180427792, "label": "\"Error: near \"(\": syntax error\" when using sqlite-utils indexes CLI"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/421#issuecomment-1098288158", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/421", "id": 1098288158, "node_id": "IC_kwDOCGYnMM5Bdowe", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-13T17:07:53Z", "updated_at": "2022-04-13T17:07:53Z", "author_association": "OWNER", "body": "I can't replicate the bug I'm afraid:\r\n```\r\n% wget \"https://github.com/wri/global-power-plant-database/blob/232a6666/output_database/global_power_plant_database.csv?raw=true\" \r\n...\r\n2022-04-13 10:06:29 (8.97 MB/s) - \u2018global_power_plant_database.csv?raw=true\u2019 saved [8856038/8856038]\r\n% sqlite-utils insert global.db power_plants \\ \r\n 'global_power_plant_database.csv?raw=true' --csv\r\n [------------------------------------] 0%\r\n [###################################-] 99% 00:00:00%\r\n% sqlite-utils indexes global.db --table \r\ntable index_name seqno cid name desc coll key\r\n------- ------------ ------- ----- ------ ------ ------ -----\r\n% sqlite-utils extract global.db power_plants country country_long \\\r\n --table countries \\\r\n --fk-column country_id \\\r\n --rename country_long name\r\n% sqlite-utils indexes global.db --table \r\ntable index_name seqno cid name desc coll key\r\n--------- -------------------------- ------- ----- ------- ------ ------ -----\r\ncountries idx_countries_country_name 0 1 country 0 BINARY 1\r\ncountries idx_countries_country_name 1 2 name 0 BINARY 1\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1180427792, "label": "\"Error: near \"(\": syntax error\" when using sqlite-utils indexes CLI"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1712#issuecomment-1097115034", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1712", "id": 1097115034, "node_id": "IC_kwDOBm6k_c5BZKWa", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-12T19:12:21Z", "updated_at": "2022-04-12T19:12:21Z", "author_association": "OWNER", "body": "Got a TIL out of this too: https://til.simonwillison.net/spatialite/gunion-to-combine-geometries", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1202227104, "label": "Make \"\" easier to read"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1712#issuecomment-1097076622", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1712", "id": 1097076622, "node_id": "IC_kwDOBm6k_c5BZA-O", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-12T18:42:04Z", "updated_at": "2022-04-12T18:42:04Z", "author_association": "OWNER", "body": "I'm not going to show the tooltip if the formatted number is in bytes.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1202227104, "label": "Make \"\" easier to read"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1712#issuecomment-1097068474", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1712", "id": 1097068474, "node_id": "IC_kwDOBm6k_c5BY--6", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-12T18:38:18Z", "updated_at": "2022-04-12T18:38:18Z", "author_association": "OWNER", "body": "\"image\"\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1202227104, "label": "Make \"\" easier to read"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1708#issuecomment-1095687566", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1708", "id": 1095687566, "node_id": "IC_kwDOBm6k_c5BTt2O", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-11T23:24:30Z", "updated_at": "2022-04-11T23:24:30Z", "author_association": "OWNER", "body": "## Redesigned template context\r\n\r\n**Warning:** if you use any custom templates with your Datasette instance they are likely to break when you upgrade to 1.0.\r\n\r\nThe template context has been redesigned to be based on the documented JSON API. This means that the template context can be considered stable going forward, so any custom templates you implement should continue to work when you upgrade Datasette in the future.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1200649124, "label": "Datasette 1.0 alpha upcoming release notes"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1705#issuecomment-1095673947", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1705", "id": 1095673947, "node_id": "IC_kwDOBm6k_c5BTqhb", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-11T23:03:49Z", "updated_at": "2022-04-11T23:03:49Z", "author_association": "OWNER", "body": "I'll also encourage testing against both Datasette 0.x and Datasette 1.0 using a GitHub Actions matrix.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1197926598, "label": "How to upgrade your plugin for 1.0 documentation"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1710#issuecomment-1095673670", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1710", "id": 1095673670, "node_id": "IC_kwDOBm6k_c5BTqdG", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-11T23:03:25Z", "updated_at": "2022-04-11T23:03:25Z", "author_association": "OWNER", "body": "Dupe of:\r\n- #1705", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1200649889, "label": "Guide for plugin authors to upgrade their plugins for 1.0"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1709#issuecomment-1095671940", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1709", "id": 1095671940, "node_id": "IC_kwDOBm6k_c5BTqCE", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-11T23:00:39Z", "updated_at": "2022-04-11T23:01:41Z", "author_association": "OWNER", "body": "- #262\r\n- #782 \r\n- #1509", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1200649502, "label": "Redesigned JSON API with ?_extra= parameters"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1711#issuecomment-1095672127", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1711", "id": 1095672127, "node_id": "IC_kwDOBm6k_c5BTqE_", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-11T23:00:58Z", "updated_at": "2022-04-11T23:00:58Z", "author_association": "OWNER", "body": "- #1510", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1200650491, "label": "Template context powered entirely by the JSON API format"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1707#issuecomment-1095277937", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1707", "id": 1095277937, "node_id": "IC_kwDOBm6k_c5BSJ1x", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-11T16:32:31Z", "updated_at": "2022-04-11T16:33:00Z", "author_association": "OWNER", "body": "That's a really interesting idea!\r\n\r\nThat page is one of the least developed at the moment. There's plenty of room for it to grow new useful features.\r\n\r\nI like this suggestion because it feels like a good opportunity to introduce some unobtrusive JavaScript. Could use a details/summary element that uses `fetch()` to load in the extra data for example.\r\n\r\nCould even do something with the `` Web Component here... https://github.com/simonw/datasette-table", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1200224939, "label": "[feature] expanded detail page"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1706#issuecomment-1094152642", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1706", "id": 1094152642, "node_id": "IC_kwDOBm6k_c5BN3HC", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-10T01:11:54Z", "updated_at": "2022-04-10T01:11:54Z", "author_association": "OWNER", "body": "This relates to this much larger vision:\r\n- #417 ", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1198822563, "label": "[feature] immutable mode for a directory, not just individual sqlite file"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1706#issuecomment-1094152173", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1706", "id": 1094152173, "node_id": "IC_kwDOBm6k_c5BN2_t", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-10T01:08:50Z", "updated_at": "2022-04-10T01:08:50Z", "author_association": "OWNER", "body": "This is a good idea - it matches the way `datasette .` works for mutable database files already.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1198822563, "label": "[feature] immutable mode for a directory, not just individual sqlite file"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/pull/1693#issuecomment-1093454899", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1693", "id": 1093454899, "node_id": "IC_kwDOBm6k_c5BLMwz", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-08T23:07:04Z", "updated_at": "2022-04-08T23:07:04Z", "author_association": "OWNER", "body": "Tests failed here due to this issue:\r\n- https://github.com/psf/black/pull/2987\r\n\r\nA future Black release should fix that.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1184850337, "label": "Bump black from 22.1.0 to 22.3.0"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1699#issuecomment-1092361727", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1699", "id": 1092361727, "node_id": "IC_kwDOBm6k_c5BHB3_", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-08T01:47:43Z", "updated_at": "2022-04-08T01:47:43Z", "author_association": "OWNER", "body": "A render mode for that plugin hook that writes to a stream is exactly what I have in mind:\r\n- #1062 ", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1193090967, "label": "Proposal: datasette query"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1699#issuecomment-1092321966", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1699", "id": 1092321966, "node_id": "IC_kwDOBm6k_c5BG4Ku", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-08T00:20:32Z", "updated_at": "2022-04-08T00:20:56Z", "author_association": "OWNER", "body": "If we do this I'm keen to have it be more than just an alternative to the existing `sqlite-utils` command - especially since if I add `sqlite-utils` as a dependency of Datasette in the future that command will be installed as part of `pip install datasette` anyway.\r\n\r\nMy best thought on how to differentiate them so far is plugins: if Datasette plugins that provide alternative outputs - like `.geojson` and `.yml` and suchlike - also work for the `datasette query` command that would make a lot of sense to me.\r\n\r\nOne way that could work: a `--fmt geojson` option to this command which uses the plugin that was registered for the specified extension.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1193090967, "label": "Proposal: datasette query"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1698#issuecomment-1086784547", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1698", "id": 1086784547, "node_id": "IC_kwDOBm6k_c5AxwQj", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-03T06:10:24Z", "updated_at": "2022-04-03T06:10:24Z", "author_association": "OWNER", "body": "Warning added here: https://docs.datasette.io/en/latest/publish.html#publishing-to-google-cloud-run", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1190828163, "label": "Add a warning about bots and Cloud Run"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1697#issuecomment-1085323192", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1697", "id": 1085323192, "node_id": "IC_kwDOBm6k_c5AsLe4", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-04-01T02:01:51Z", "updated_at": "2022-04-01T02:01:51Z", "author_association": "OWNER", "body": "Huh, turns out `Request.fake()` wasn't yet documented.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1189113609, "label": "`Request.fake(..., url_vars={})`"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1696#issuecomment-1083351437", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1696", "id": 1083351437, "node_id": "IC_kwDOBm6k_c5AkqGN", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-30T16:20:49Z", "updated_at": "2022-03-30T16:21:02Z", "author_association": "OWNER", "body": "Maybe like this:\r\n\r\n\"image\"\r\n\r\n```html\r\n

283 rows\r\n where dcode = 3 (Human Related: Other)\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": 1186696202, "label": "Show foreign key label when filtering"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1692#issuecomment-1082663746", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1692", "id": 1082663746, "node_id": "IC_kwDOBm6k_c5AiCNC", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-30T06:14:39Z", "updated_at": "2022-03-30T06:14:51Z", "author_association": "OWNER", "body": "I like your design, though I think it should be `\"nomodule\": True` for consistency with the other options.\r\n\r\nI think `\"async\": True` is worth supporting too.", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1182227211, "label": "[plugins][feature request]: Support additional script tag attributes when loading custom JS"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1692#issuecomment-1082661795", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1692", "id": 1082661795, "node_id": "IC_kwDOBm6k_c5AiBuj", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-30T06:11:41Z", "updated_at": "2022-03-30T06:11:41Z", "author_association": "OWNER", "body": "This is a good idea.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1182227211, "label": "[plugins][feature request]: Support additional script tag attributes when loading custom JS"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1695#issuecomment-1082617386", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1695", "id": 1082617386, "node_id": "IC_kwDOBm6k_c5Ah24q", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-30T04:46:18Z", "updated_at": "2022-03-30T04:46:18Z", "author_association": "OWNER", "body": "` selected = (column_qs, str(row[\"value\"])) in qs_pairs` is wrong.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1185868354, "label": "Option to un-filter facet not shown for `?col__exact=value`"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1695#issuecomment-1082617241", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1695", "id": 1082617241, "node_id": "IC_kwDOBm6k_c5Ah22Z", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-30T04:45:55Z", "updated_at": "2022-03-30T04:45:55Z", "author_association": "OWNER", "body": "Relevant template: https://github.com/simonw/datasette/blob/e73fa72917ca28c152208d62d07a490c81cadf52/datasette/templates/table.html#L168-L172\r\n\r\nPopulated from here: https://github.com/simonw/datasette/blob/c496f2b663ff0cef908ffaaa68b8cb63111fb5f2/datasette/facets.py#L246-L253", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1185868354, "label": "Option to un-filter facet not shown for `?col__exact=value`"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/420#issuecomment-1081047053", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/420", "id": 1081047053, "node_id": "IC_kwDOCGYnMM5Ab3gN", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-28T19:22:37Z", "updated_at": "2022-03-28T19:22:37Z", "author_association": "OWNER", "body": "Wrote about this in my weeknotes: https://simonwillison.net/2022/Mar/28/datasette-auth0/#new-features-as-documentation", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178546862, "label": "Document how to use a `--convert` function that runs initialization code first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/420#issuecomment-1080141111", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/420", "id": 1080141111, "node_id": "IC_kwDOCGYnMM5AYaU3", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-28T03:25:57Z", "updated_at": "2022-03-28T03:54:37Z", "author_association": "OWNER", "body": "So now this should solve your problem:\r\n```\r\necho '[{\"name\": \"notaword\"}, {\"name\": \"word\"}]\r\n' | python3 -m sqlite_utils insert listings.db listings - --convert '\r\nimport enchant\r\nd = enchant.Dict(\"en_US\")\r\n\r\ndef convert(row):\r\n global d\r\n row[\"is_dictionary_word\"] = d.check(row[\"name\"])\r\n'\r\n```", "reactions": "{\"total_count\": 1, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 1, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178546862, "label": "Document how to use a `--convert` function that runs initialization code first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1690#issuecomment-1079788375", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1690", "id": 1079788375, "node_id": "IC_kwDOBm6k_c5AXENX", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-26T22:43:00Z", "updated_at": "2022-03-26T22:43:00Z", "author_association": "OWNER", "body": "Then I can update this section of the documentation which currently recommends the above pattern: https://docs.datasette.io/en/stable/authentication.html#the-ds-actor-cookie", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1182141761, "label": "Idea: `datasette.set_actor_cookie(response, actor)`"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1690#issuecomment-1079788346", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1690", "id": 1079788346, "node_id": "IC_kwDOBm6k_c5AXEM6", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-26T22:42:40Z", "updated_at": "2022-03-26T22:42:40Z", "author_association": "OWNER", "body": "I don't want to do a `response.set_actor_cookie()` method because I like `Response` not to carry too many Datasette-specific features.\r\n\r\nSo `datasette.set_actor_cookie(response, actor, expire_after=None)` would be a better place for this I think.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1182141761, "label": "Idea: `datasette.set_actor_cookie(response, actor)`"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1689#issuecomment-1079779040", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1689", "id": 1079779040, "node_id": "IC_kwDOBm6k_c5AXB7g", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-26T21:35:57Z", "updated_at": "2022-03-26T21:35:57Z", "author_association": "OWNER", "body": "Fixed: https://docs.datasette.io/en/latest/internals.html#add-message-request-message-type-datasette-info", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1182065616, "label": "datasette.add_message() documentation is incorrect"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1688#issuecomment-1079582485", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1688", "id": 1079582485, "node_id": "IC_kwDOBm6k_c5AWR8V", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-26T03:15:34Z", "updated_at": "2022-03-26T03:15:34Z", "author_association": "OWNER", "body": "Yup, you're right in what you figured out here: stand-alone plugins can't currently package static assets other then using the static folder.\r\n\r\nThe `datasette-plugin` cookiecutter template should make creating a Python package pretty easy though: https://github.com/simonw/datasette-plugin\r\n\r\nYou can run that yourself, or you can run it using this GitHub template repository: https://github.com/simonw/datasette-plugin-template-repository \r\n\r\n", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1181432624, "label": "[plugins][documentation] Is it possible to serve per-plugin static folders when writing one-off (single file) plugins?"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/417#issuecomment-1079441621", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/417", "id": 1079441621, "node_id": "IC_kwDOCGYnMM5AVvjV", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-25T21:18:37Z", "updated_at": "2022-03-25T21:18:37Z", "author_association": "OWNER", "body": "Updated documentation: https://sqlite-utils.datasette.io/en/latest/cli.html#inserting-newline-delimited-json", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1175744654, "label": "insert fails on JSONL with whitespace"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/421#issuecomment-1079407962", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/421", "id": 1079407962, "node_id": "IC_kwDOCGYnMM5AVnVa", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-25T20:25:10Z", "updated_at": "2022-03-25T20:25:18Z", "author_association": "OWNER", "body": "Can you share either your whole `global.db` table or a shrunk down example that illustrates the bug?\r\n\r\nMy hunch is that you may have a table or column with a name that triggers the error.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1180427792, "label": "\"Error: near \"(\": syntax error\" when using sqlite-utils indexes CLI"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/422#issuecomment-1079406708", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/422", "id": 1079406708, "node_id": "IC_kwDOCGYnMM5AVnB0", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-25T20:23:21Z", "updated_at": "2022-03-25T20:23:21Z", "author_association": "OWNER", "body": "Fixing this would require a bump to 4.0 because it would break existing code.\r\n\r\nThe alternative would be to introduce a new `ignore_nulls=True` parameter which users can change to `ignore_nulls=False`. Or come up with better wording for that.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1181236173, "label": "Reconsider not running convert functions against null values"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/420#issuecomment-1079404281", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/420", "id": 1079404281, "node_id": "IC_kwDOCGYnMM5AVmb5", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-25T20:19:50Z", "updated_at": "2022-03-25T20:19:50Z", "author_association": "OWNER", "body": "Now documented here: https://sqlite-utils.datasette.io/en/latest/cli.html#using-a-convert-function-to-execute-initialization", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178546862, "label": "Document how to use a `--convert` function that runs initialization code first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/420#issuecomment-1079384771", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/420", "id": 1079384771, "node_id": "IC_kwDOCGYnMM5AVhrD", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-25T19:51:34Z", "updated_at": "2022-03-25T19:53:01Z", "author_association": "OWNER", "body": "This works:\r\n```\r\n% sqlite-utils insert dogs.db dogs dogs.json --convert '\r\nimport random\r\nprint(\"seeding\")\r\nrandom.seed(10)\r\nprint(random.random())\r\n\r\ndef convert(row):\r\n global random\r\n print(row)\r\n row[\"random_score\"] = random.random()\r\n'\r\nseeding\r\n0.5714025946899135\r\n{'id': 1, 'name': 'Cleo'}\r\n{'id': 2, 'name': 'Pancakes'}\r\n{'id': 3, 'name': 'New dog'}\r\n(sqlite-utils) sqlite-utils % sqlite-utils rows dogs.db dogs\r\n[{\"id\": 1, \"name\": \"Cleo\", \"random_score\": 0.4288890546751146},\r\n {\"id\": 2, \"name\": \"Pancakes\", \"random_score\": 0.5780913011344704},\r\n {\"id\": 3, \"name\": \"New dog\", \"random_score\": 0.20609823213950174}]\r\n```\r\nHaving to use `global random` inside the function is frustrating but apparently necessary. https://stackoverflow.com/a/56552138/6083", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178546862, "label": "Document how to use a `--convert` function that runs initialization code first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/420#issuecomment-1079376283", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/420", "id": 1079376283, "node_id": "IC_kwDOCGYnMM5AVfmb", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-25T19:39:30Z", "updated_at": "2022-03-25T19:43:35Z", "author_association": "OWNER", "body": "Actually this doesn't work as I thought. This demo shows that the initialization code is run once per item, not a single time at the start of the run:\r\n```\r\n% sqlite-utils insert dogs.db dogs dogs.json --convert '\r\nimport random\r\nprint(\"seeding\")\r\nrandom.seed(10)\r\nprint(random.random())\r\n\r\ndef convert(row):\r\n print(row)\r\n row[\"random_score\"] = random.random()\r\n'\r\nseeding\r\n0.5714025946899135\r\nseeding\r\n0.5714025946899135\r\nseeding\r\n0.5714025946899135\r\nseeding\r\n0.5714025946899135\r\n```\r\nAlso that `print(row)` line is not being printed anywhere that gets to the console for some reason.\r\n\r\n... my mistake, that happened because I changed this line in order to try to get local imports to work:\r\n```python\r\n try:\r\n exec(code, globals, locals)\r\n return globals[\"convert\"]\r\n except (AttributeError, SyntaxError, NameError, KeyError, TypeError):\r\n```\r\nIt should be `locals[\"convert\"]`", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178546862, "label": "Document how to use a `--convert` function that runs initialization code first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/420#issuecomment-1079243535", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/420", "id": 1079243535, "node_id": "IC_kwDOCGYnMM5AU_MP", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-25T17:25:12Z", "updated_at": "2022-03-25T17:25:12Z", "author_association": "OWNER", "body": "That documentation is split across a few places. This is the only bit that talks about `def convert()` pattern right now:\r\n\r\n- https://sqlite-utils.datasette.io/en/stable/cli.html#converting-data-in-columns\r\n\r\nBut that's for `sqlite-utils convert` - the documentation for `sqlite-utils insert --convert` at https://sqlite-utils.datasette.io/en/stable/cli.html#applying-conversions-while-inserting-data doesn't mention it.\r\n\r\nSince both `sqlite-utils convert` and `sqlite-utils insert --convert` apply the same rules to the code, they should link to a shared explanation in the documentation.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178546862, "label": "Document how to use a `--convert` function that runs initialization code first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/420#issuecomment-1078343231", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/420", "id": 1078343231, "node_id": "IC_kwDOCGYnMM5ARjY_", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-24T21:16:10Z", "updated_at": "2022-03-24T21:17:20Z", "author_association": "OWNER", "body": "Aha! This may be possible already: https://github.com/simonw/sqlite-utils/blob/396f80fcc60da8dd844577114f7920830a2e5403/sqlite_utils/utils.py#L311-L316\r\n\r\nAnd yes, this does indeed work - you can do something like this:\r\n\r\n```\r\necho '{\"name\": \"harry\"}' | sqlite-utils insert db.db people - --convert '\r\nimport time\r\n# Simulate something expensive\r\ntime.sleep(1)\r\n\r\ndef convert(row):\r\n row[\"upper\"] = row[\"name\"].upper()\r\n'\r\n```\r\nAnd after running that:\r\n```\r\nsqlite-utils dump db.db \r\nBEGIN TRANSACTION;\r\nCREATE TABLE [people] (\r\n [name] TEXT,\r\n [upper] TEXT\r\n);\r\nINSERT INTO \"people\" VALUES('harry','HARRY');\r\nCOMMIT;\r\n```\r\nSo this is a documentation issue - there's a trick for it but I didn't know what the trick was!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178546862, "label": "Document how to use a `--convert` function that runs initialization code first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/420#issuecomment-1078328774", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/420", "id": 1078328774, "node_id": "IC_kwDOCGYnMM5ARf3G", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-24T21:12:33Z", "updated_at": "2022-03-24T21:12:33Z", "author_association": "OWNER", "body": "Here's how the `_compile_code()` mechanism works at the moment: https://github.com/simonw/sqlite-utils/blob/396f80fcc60da8dd844577114f7920830a2e5403/sqlite_utils/utils.py#L308-L342\r\n\r\nAt the end it does this:\r\n```python\r\n return locals[\"fn\"]\r\n```\r\nSo it's already building and then returning a function.\r\n\r\nThe question is if there's a sensible way to allow people to further customize that function by executing some code first, in a way that's easy to explain.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178546862, "label": "Document how to use a `--convert` function that runs initialization code first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/420#issuecomment-1078322301", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/420", "id": 1078322301, "node_id": "IC_kwDOCGYnMM5AReR9", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-24T21:10:52Z", "updated_at": "2022-03-24T21:10:52Z", "author_association": "OWNER", "body": "I can think of three ways forward:\r\n\r\n- Figure out a pattern that gets that local file import workaround to work\r\n- Add another option such as `--convert-init` that lets you pass code that will be executed once at the start\r\n- Come up with a pattern where the `--convert` code can run some initialization code and then return a function which will be called against each value\r\n\r\nI quite like the idea of that third option - I'm going to prototype it and see if I can work something out.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178546862, "label": "Document how to use a `--convert` function that runs initialization code first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/sqlite-utils/issues/420#issuecomment-1078315922", "issue_url": "https://api.github.com/repos/simonw/sqlite-utils/issues/420", "id": 1078315922, "node_id": "IC_kwDOCGYnMM5ARcuS", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-24T21:09:27Z", "updated_at": "2022-03-24T21:09:27Z", "author_association": "OWNER", "body": "Yeah, this is WAY harder than it should be.\r\n\r\nThere's a clumsy workaround you could use which looks something like this: create a file `my_enchant.py` containing:\r\n\r\n```python\r\nimport enchant\r\nd = enchant.Dict(\"en_US\")\r\n\r\ndef check(word):\r\n return d.check(word)\r\n```\r\nThen run `sqlite-utils` like this:\r\n\r\n```\r\nPYTHONPATH=. cat items.json | jq '.data' | sqlite-utils insert listings.db listings - --convert 'my_enchant.check(value)' --import my_enchant\r\n```\r\nExcept I tried that and it doesn't work! I don't know the right pattern for getting `--import` to work with modules in the same directory.\r\n\r\nSo yeah, this is definitely a big feature gap.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178546862, "label": "Document how to use a `--convert` function that runs initialization code first"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1682#issuecomment-1076696791", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1682", "id": 1076696791, "node_id": "IC_kwDOBm6k_c5ALRbX", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-23T18:45:49Z", "updated_at": "2022-03-23T18:45:49Z", "author_association": "OWNER", "body": "The problem is here in `QueryView`: https://github.com/simonw/datasette/blob/d7c793d7998388d915f8d270079c68a77a785051/datasette/views/database.py#L206-L238\r\n\r\nIt should be resolving `database` based on the route path, as seen in other methods like this one: https://github.com/simonw/datasette/blob/d7c793d7998388d915f8d270079c68a77a785051/datasette/views/table.py#L270-L279\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1178521513, "label": "SQL queries against databases with different routes are broken"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1670#issuecomment-1076683297", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1670", "id": 1076683297, "node_id": "IC_kwDOBm6k_c5ALOIh", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-23T18:32:32Z", "updated_at": "2022-03-23T18:32:32Z", "author_association": "OWNER", "body": "Added this to news on https://datasette.io/ https://github.com/simonw/datasette.io/commit/fd3ec57cdd5b935f75cbf52a86b3aabf2c97d217", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1174423568, "label": "Ship Datasette 0.61"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1670#issuecomment-1076666293", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1670", "id": 1076666293, "node_id": "IC_kwDOBm6k_c5ALJ-1", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-23T18:16:29Z", "updated_at": "2022-03-23T18:16:29Z", "author_association": "OWNER", "body": "https://docs.datasette.io/en/stable/changelog.html#v0-61", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1174423568, "label": "Ship Datasette 0.61"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1670#issuecomment-1076665837", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1670", "id": 1076665837, "node_id": "IC_kwDOBm6k_c5ALJ3t", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-03-23T18:16:01Z", "updated_at": "2022-03-23T18:16:01Z", "author_association": "OWNER", "body": "https://github.com/simonw/datasette/releases/tag/0.61\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": 1174423568, "label": "Ship Datasette 0.61"}, "performed_via_github_app": null}