\r\n and any(r[\"n\"] > 1 for r in distinct_values)\r\nTypeError: '>' not supported between instances of 'str' and 'int'\r\nINFO: 127.0.0.1:56402 - \"GET /databasetest/databasetest HTTP/1.1\" 500 Internal Server Error\r\nINFO: 127.0.0.1:56402 - \"GET /-/static/app.css?4e362c HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56404 - \"GET / HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56404 - \"GET /-/static/app.css?4e362c HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56406 - \"GET /-/static-plugins/datasette_vega/main.2acbb312.css HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56408 - \"GET /-/static-plugins/datasette_vega/main.08f5d3d8.js HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56408 - \"GET /databasetest HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56408 - \"GET /-/static/app.css?4e362c HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56404 - \"GET /-/static-plugins/datasette_vega/main.2acbb312.css HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56406 - \"GET /-/static/codemirror-5.57.0.min.css HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56410 - \"GET /-/static-plugins/datasette_vega/main.08f5d3d8.js HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56414 - \"GET /-/static/codemirror-5.57.0-sql.min.js HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56412 - \"GET /-/static/codemirror-5.57.0.min.js HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56408 - \"GET /-/static/sql-formatter-2.3.3.min.js HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56408 - \"GET /databasetest?sql=select+*+from+databasetest HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56410 - \"GET /-/static/app.css?4e362c HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56408 - \"GET /-/static-plugins/datasette_vega/main.2acbb312.css HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56412 - \"GET /-/static/codemirror-5.57.0.min.css HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56404 - \"GET /-/static/sql-formatter-2.3.3.min.js HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56406 - \"GET /-/static/codemirror-5.57.0.min.js HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56414 - \"GET /-/static-plugins/datasette_vega/main.08f5d3d8.js HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56408 - \"GET /-/static/codemirror-5.57.0-sql.min.js HTTP/1.1\" 200 OK\r\nINFO: 127.0.0.1:56410 - \"GET /databasetest.json?sql=select+*+from+databasetest&_shape=array&_shape=array HTTP/1.1\" 200 OK\r\n^CINFO: Shutting down\r\nINFO: Waiting for application shutdown.\r\nINFO: Application shutdown complete.\r\nINFO: Finished server process [1408482]\r\n```\r\n\r\nNote that there is no error if I go to http://127.0.0.1:8001/databasetest and then click on `Run SQL`.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1228/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1082765654, "node_id": "I_kwDOBm6k_c5AibFW", "number": 1561, "title": "add hash id to \"_memory\" url if hashed url mode is turned on and crossdb is also turned on", "user": {"value": 536941, "label": "fgregg"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 3, "created_at": "2021-12-17T00:45:12Z", "updated_at": "2022-03-19T04:45:40Z", "closed_at": "2022-03-19T04:45:40Z", "author_association": "CONTRIBUTOR", "pull_request": null, "body": "If hashed_url mode is turned on and crossdb is also turned on, then queries to _memory should have a hash_id. \r\n\r\nOne way that it could work is to have the _memory hash be a hash of all the individual databases.\r\n\r\nOtherwise, crossdb queries can get quit out of data if using aggressive caching.\r\n\r\n", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1561/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1161584460, "node_id": "I_kwDOBm6k_c5FPF9M", "number": 1651, "title": "Get rid of the no-longer necessary ?_format=json hack for tables called x.json", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 8, "created_at": "2022-03-07T15:40:42Z", "updated_at": "2022-03-19T04:04:50Z", "closed_at": "2022-03-15T18:25:42Z", "author_association": "OWNER", "pull_request": null, "body": "Tidy up from:\r\n- #1439", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1651/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1169840669, "node_id": "I_kwDOBm6k_c5Fulod", "number": 1658, "title": "Revert main to version that passes tests", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 1, "created_at": "2022-03-15T15:37:02Z", "updated_at": "2022-03-19T04:04:50Z", "closed_at": "2022-03-15T15:42:58Z", "author_association": "OWNER", "pull_request": null, "body": "> I've made a real mess of this. I'm going to revert Datasette`main` back to the last commit that passed the tests and try this again in a branch.\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/1657#issuecomment-1068125636_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1658/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1170554975, "node_id": "I_kwDOBm6k_c5FxUBf", "number": 1663, "title": "Document the internals that were used in datasette-hashed-urls", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 2, "created_at": "2022-03-16T05:17:08Z", "updated_at": "2022-03-19T04:04:50Z", "closed_at": "2022-03-17T21:32:38Z", "author_association": "OWNER", "pull_request": null, "body": "The https://github.com/simonw/datasette-hashed-urls used a couple of currently undocumented features:\r\n- `db.hash`\r\n- `Datasette(..., immutables=[...])`", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1663/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1108235694, "node_id": "I_kwDOBm6k_c5CDlWu", "number": 1603, "title": "A proper favicon", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 19, "created_at": "2022-01-19T15:24:55Z", "updated_at": "2022-03-19T04:04:49Z", "closed_at": "2022-01-20T06:07:31Z", "author_association": "OWNER", "pull_request": null, "body": "Tips here: https://adamj.eu/tech/2022/01/18/how-to-add-a-favicon-to-your-django-site/ - I think a PNG served at `/favicon.ico` is the best option, since safari doesn't support SVG yet.\r\n\r\nRelevant code: https://github.com/simonw/datasette/blob/cb29119db9115b1f40de2fb45263ed77e3bfbb3e/datasette/app.py#L182-L183\r\n\r\nI can reuse the icon for https://datasette.io/desktop", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1603/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1114147905, "node_id": "I_kwDOBm6k_c5CaIxB", "number": 1612, "title": "Move canned queries closer to the SQL input area", "user": {"value": 639012, "label": "jsfenfen"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 5, "created_at": "2022-01-25T17:06:39Z", "updated_at": "2022-03-19T04:04:49Z", "closed_at": "2022-01-25T18:34:21Z", "author_association": "CONTRIBUTOR", "pull_request": null, "body": "*Original title: Consider placing example queries above the sql input?*\r\n\r\nHi! Have been enjoying deploying ad hoc datasettes for collaborators to pick over! \r\n\r\nI keep finding myself manually \"fixing\" the database.html template so that the \"example queries\" (canned queries) appear directly *over* the sql box? So they are sorta more a suggestion for collaborators who aren't inclined to write their own queries? \r\n\r\nMy sense is any time I go to the trouble of writing canned queries my users should see 'em? \r\n\r\n(( I have also considered a client-side reactive-ish option where selecting a query just places the raw SQL in the box and doesn't execute it, but this seems to end up being an inconvenience, rather than a teaching tool. )) \r\n\r\n", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1612/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1122413719, "node_id": "I_kwDOBm6k_c5C5qyX", "number": 1621, "title": "Test against Python 3.11 dev version", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 0, "created_at": "2022-02-02T21:38:57Z", "updated_at": "2022-03-19T04:04:49Z", "closed_at": "2022-02-02T21:58:54Z", "author_association": "OWNER", "pull_request": null, "body": "To avoid another surprise like we got with 3.10: https://simonwillison.net/2021/Oct/9/finding-and-reporting-a-bug/\r\n\r\nFrom a quick GitHub code search it looks like `3.11-dev` should work: https://cs.github.com/urllib3/urllib3/blob/7bec77e81aa0a194c98381053225813f5347c9d2/.github/workflows/ci.yml#L60", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1621/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1122416919, "node_id": "I_kwDOBm6k_c5C5rkX", "number": 1623, "title": "/-/patterns returns link: alternate JSON header to 404", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 2, "created_at": "2022-02-02T21:42:49Z", "updated_at": "2022-03-19T04:04:49Z", "closed_at": "2022-02-02T21:48:56Z", "author_association": "OWNER", "pull_request": null, "body": "Bug from:\r\n- #1620\r\n\r\n```\r\n% curl -s -I 'https://latest.datasette.io/-/patterns' | grep link\r\nlink: https://latest.datasette.io/-/patterns.json; rel=\"alternate\"; type=\"application/json+datasette\"\r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1623/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1126604194, "node_id": "I_kwDOBm6k_c5DJp2i", "number": 1632, "title": "datasette one.db one.db opens database twice, as one and one_2", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 6, "created_at": "2022-02-07T23:14:47Z", "updated_at": "2022-03-19T04:04:49Z", "closed_at": "2022-02-07T23:50:01Z", "author_association": "OWNER", "pull_request": null, "body": "> ```\r\n> % mkdir /tmp/data\r\n> % cp ~/Dropbox/Development/datasette/fixtures.db /tmp/data \r\n> % datasette /tmp/data/*.db /tmp/data/created.db --create -p 8852\r\n> ...\r\n> INFO: Uvicorn running on http://127.0.0.1:8852 (Press CTRL+C to quit)\r\n> ^CINFO: Shutting down\r\n> % datasette /tmp/data/*.db /tmp/data/created.db --create -p 8852\r\n> ...\r\n> INFO: 127.0.0.1:49533 - \"GET / HTTP/1.1\" 200 OK\r\n> ```\r\n> The first time I ran Datasette I got two databases - `fixtures` and `created`\r\n> \r\n> BUT... when I ran Datasette the second time it looked like this:\r\n> \r\n> \r\n> \r\n> This is the same result you get if you run:\r\n> \r\n> datasette /tmp/data/fixtures.db /tmp/data/created.db /tmp/data/created.db\r\n> \r\n> This is caused by this Datasette issue:\r\n> - https://github.com/simonw/datasette/issues/509\r\n> \r\n> So... either I teach Datasette to de-duplicate multiple identical file paths passed to the command, or I can't use `/data/*.db` in the `Dockerfile` here and I need to go back to other solutions for the challenge described in this comment: https://github.com/simonw/datasette-publish-fly/pull/12#issuecomment-1031971831\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette-publish-fly/pull/12#issuecomment-1032029874_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1632/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1170497629, "node_id": "I_kwDOBm6k_c5FxGBd", "number": 1662, "title": "[feature request] Publish to fully static website", "user": {"value": 32609395, "label": "contrun"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2022-03-16T03:32:28Z", "updated_at": "2022-03-19T00:42:23Z", "closed_at": "2022-03-19T00:42:23Z", "author_association": "NONE", "pull_request": null, "body": "It seems currently all datasette publish requires a real backend server which is able to query the database and send results back to the frontend. There are a few projects to on-demand download a portion of data from the database from a sqlite lite database url, and present it directly to the user. These methods leverages web assembly under the hood. I think datasette is a perfect use case for this technology. Below are a few examples of querying sqlite database from frontend directly.\r\n\r\n* [Using sqlite3 as a notekeeping document graph with automatic reference indexing](https://epilys.github.io/bibliothecula/notekeeping.html)\r\n* [Hosting SQLite databases on Github Pages - (or any static file hoster) - phiresky's blog](https://phiresky.github.io/blog/2021/hosting-sqlite-databases-on-github-pages/)\r\n* [Static torrent website with peer-to-peer queries over BitTorrent on 2M records](https://boredcaveman.xyz/post/0x2_static-torrent-website-p2p-queries.html)", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1662/reactions\", \"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1170355774, "node_id": "I_kwDOBm6k_c5FwjY-", "number": 1661, "title": "Remove Hashed URL mode", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 10, "created_at": "2022-03-15T23:13:56Z", "updated_at": "2022-03-19T00:37:37Z", "closed_at": "2022-03-19T00:37:36Z", "author_association": "OWNER", "pull_request": null, "body": "It's now handled by a plugin instead:\r\n- #647\r\n- https://github.com/simonw/datasette-hashed-urls/issues/3\r\n\r\nhttps://github.com/simonw/datasette-hashed-urls\r\n\r\nSub-tasks:\r\n\r\n- [x] Remove hashed URL mode implementation\r\n- [x] Update documentation\r\n- [x] Ensure `--setting hash_urls 1` shows a useful message", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1661/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1173017980, "node_id": "PR_kwDOBm6k_c40oRq-", "number": 1664, "title": "Remove hashed URL mode", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 7, "created_at": "2022-03-17T23:19:10Z", "updated_at": "2022-03-19T00:12:04Z", "closed_at": "2022-03-19T00:12:04Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/1664", "body": "Refs #1661.", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1664/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 1065429936, "node_id": "I_kwDOBm6k_c4_gSuw", "number": 1532, "title": "Use datasette-table Web Component to guide the design of the JSON API for 1.0", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 4, "created_at": "2021-11-28T20:37:18Z", "updated_at": "2022-03-16T20:13:34Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "I realized that one of the reasons I'm having trouble committing to nailing down the JSON API for 1.0 is that I don't use it much myself - I use the `?_shape=array` one quite often, but I don't have any projects that are using the default, more fully-featured API.\r\n\r\nAs an experiment I built a Web Component for embedding Datasette tables on pages - https://github.com/simonw/datasette-table - and I think it's actually going to be a really useful tool for helping me dog food the v1.0 API design.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1532/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 973139047, "node_id": "MDU6SXNzdWU5NzMxMzkwNDc=", "number": 1439, "title": "Rethink how .ext formats (v.s. ?_format=) works before 1.0", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 48, "created_at": "2021-08-17T23:32:51Z", "updated_at": "2022-03-15T20:51:26Z", "closed_at": "2022-03-15T20:51:26Z", "author_association": "OWNER", "pull_request": null, "body": "Datasette currently has surprising special behaviour for if a table name ends in `.csv` - which can happen when a tool like `csvs-to-sqlite` creates tables that match the filename that they were imported from.\r\n\r\nhttps://latest.datasette.io/fixtures/table%2Fwith%2Fslashes.csv illustrates this behaviour: it links to `.csv` and `.json` that look like this:\r\n\r\n- https://latest.datasette.io/fixtures/table%2Fwith%2Fslashes.csv?_format=json\r\n- https://latest.datasette.io/fixtures/table%2Fwith%2Fslashes.csv?_format=csv&_size=max\r\n\r\nWhere normally Datasette would add the `.csv` or `.json` extension to the path component of the URL (as seen on other pages such as https://latest.datasette.io/fixtures/facet_cities) here the [path_with_format() function](https://github.com/simonw/datasette/blob/adb5b70de5cec3c3dd37184defe606a082c232cf/datasette/utils/__init__.py#L710) notices that there is already a `.` in the path and instead adds `?_format=csv` to the query string instead.\r\n\r\nThe problem with this mechanism is that it's pretty surprising. Anyone writing external code to Datasette who wants to get back the `.csv` or `.json` version giving the URL to a table page will need to know about and implement this behaviour themselves. That's likely to cause all kinds of bugs in the future.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1439/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 531502365, "node_id": "MDU6SXNzdWU1MzE1MDIzNjU=", "number": 646, "title": "Make database level information from metadata.json available in the index.html template", "user": {"value": 18017473, "label": "lagolucas"}, "state": "open", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 3, "created_at": "2019-12-02T19:55:10Z", "updated_at": "2022-03-15T20:50:34Z", "closed_at": null, "author_association": "NONE", "pull_request": null, "body": "Did a search on the issues here and didn't find anything related to what I want.\r\n\r\nI want to have information that is on the database level of the JSON like title, source and source_url, and use it on the index page.\r\n\r\nI tried some small tweaks on the python and html files, but failed to get that result.\r\n\r\nIs there a way? Thanks!", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/646/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 626593402, "node_id": "MDU6SXNzdWU2MjY1OTM0MDI=", "number": 780, "title": "Internals documentation for datasette.metadata() method", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 2, "created_at": "2020-05-28T15:14:22Z", "updated_at": "2022-03-15T20:50:34Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "https://github.com/simonw/datasette/blob/40885ef24e32d91502b6b8bbad1c7376f50f2830/datasette/app.py#L297-L328", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/780/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 1054243511, "node_id": "I_kwDOBm6k_c4-1nq3", "number": 1509, "title": "Datasette 1.0 JSON API (and documentation)", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 3, "created_at": "2021-11-15T23:22:45Z", "updated_at": "2022-03-15T20:38:56Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "The new JSON API in a stable, documented form.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1509/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 646737558, "node_id": "MDU6SXNzdWU2NDY3Mzc1NTg=", "number": 870, "title": "Refactor default views to use register_routes", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 10, "created_at": "2020-06-27T18:53:12Z", "updated_at": "2022-03-15T20:07:18Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "It would be much cleaner if Datasette's default views were all registered using the new `register_routes()` plugin hook. Could dramatically reduce the code in `datasette/app.py`.\r\n\r\n> The ideal fix here would be to rework my `BaseView` subclass mechanism to work with `register_routes()` so that those views don't have any special privileges above plugin-provided views.\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/864#issuecomment-648580556_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/870/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 1058072543, "node_id": "I_kwDOBm6k_c4_EOff", "number": 1518, "title": "Complete refactor of TableView and table.html template", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 45, "created_at": "2021-11-19T02:55:16Z", "updated_at": "2022-03-15T18:35:49Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "Split from #878. The current `TableView` class is by far the most complex part of Datasette, and the most difficult to work on: https://github.com/simonw/datasette/blob/0.59.2/datasette/views/table.py\r\n\r\nIn #878 I started exploring a new pattern for building views. In doing so it became clear that `TableView` is the first beast that I need to slay - if I can refactor that into something neat the pattern for building other views will emerge as a natural consequence.\r\n\r\nI've been trying to build this as a `register_routes()` plugin, as originally suggested in #870 - though unfortunately it looks like those plugins can't replace existing Datasette default views at the moment, see #1517. [UPDATE: I was wrong about this, plugins can over-ride default views just fine]\r\n\r\nI also know that I want to have a fully documented template context for `table.html` as a major step on the way to Datasette 1.0, see #1510.\r\n\r\nAll of this adds up to the `TableView` factor being a major project that will unblock a whole flurry of other things - so I'm going to work on that in this separate issue.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1518/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 1168995756, "node_id": "I_kwDOBm6k_c5FrXWs", "number": 1657, "title": "Tilde encoding: use ~ instead of - for dash-encoding", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 12, "created_at": "2022-03-14T22:55:17Z", "updated_at": "2022-03-15T18:25:11Z", "closed_at": "2022-03-15T18:01:58Z", "author_association": "OWNER", "pull_request": null, "body": "Refs #1439", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1657/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1168357113, "node_id": "PR_kwDOBm6k_c40ZRDA", "number": 1656, "title": "Update pytest requirement from <7.1.0,>=5.2.2 to >=5.2.2,<7.2.0", "user": {"value": 49699333, "label": "dependabot[bot]"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2022-03-14T13:11:53Z", "updated_at": "2022-03-15T18:03:03Z", "closed_at": "2022-03-15T18:03:02Z", "author_association": "CONTRIBUTOR", "pull_request": "simonw/datasette/pulls/1656", "body": "Updates the requirements on [pytest](https://github.com/pytest-dev/pytest) to permit the latest version.\n\nRelease notes
\nSourced from pytest's releases.
\n\n7.1.0
\npytest 7.1.0 (2022-03-13)
\nBreaking Changes
\n\n- \n
#8838: As per our policy, the following features have been deprecated in the 6.X series and are now\nremoved:
\n\npytest._fillfuncargs
function. \npytest_warning_captured
hook - use pytest_warning_recorded
instead. \n-k -foobar
syntax - use -k 'not foobar'
instead. \n-k foobar:
syntax. \npytest.collect
module - import from pytest
directly. \n
\nFor more information consult\nDeprecations and Removals in the docs.
\n \n- \n
#9437: Dropped support for Python 3.6, which reached end-of-life at 2021-12-23.
\n \n
\nImprovements
\n\n- \n
#5192: Fixed test output for some data types where -v
would show less information.
\nAlso, when showing diffs for sequences, -q
would produce full diffs instead of the expected diff.
\n \n- \n
#9362: pytest now avoids specialized assert formatting when it is detected that the default __eq__
is overridden in attrs
or dataclasses
.
\n \n- \n
#9536: When -vv
is given on command line, show skipping and xfail reasons in full instead of truncating them to fit the terminal width.
\n \n- \n
#9644: More information about the location of resources that led Python to raise ResourceWarning
{.interpreted-text role="class"} can now\nbe obtained by enabling tracemalloc
{.interpreted-text role="mod"}.
\nSee resource-warnings
{.interpreted-text role="ref"} for more information.
\n \n- \n
#9678: More types are now accepted in the ids
argument to @pytest.mark.parametrize
.\nPreviously only [str]{.title-ref}, [float]{.title-ref}, [int]{.title-ref} and [bool]{.title-ref} were accepted;\nnow [bytes]{.title-ref}, [complex]{.title-ref}, [re.Pattern]{.title-ref}, [Enum]{.title-ref} and anything with a [__name__]{.title-ref} are also accepted.
\n \n- \n
#9692: pytest.approx
{.interpreted-text role="func"} now raises a TypeError
{.interpreted-text role="class"} when given an unordered sequence (such as set
{.interpreted-text role="class"}).
\nNote that this implies that custom classes which only implement __iter__
and __len__
are no longer supported as they don't guarantee order.
\n \n
\nBug Fixes
\n\n- #8242: The deprecation of raising
unittest.SkipTest
{.interpreted-text role="class"} to skip collection of\ntests during the pytest collection phase is reverted - this is now a supported\nfeature again. \n- #9493: Symbolic link components are no longer resolved in conftest paths.\nThis means that if a conftest appears twice in collection tree, using symlinks, it will be executed twice.
\n
\n\n
\n... (truncated)
\n \n\nCommits
\n\n1dbffcc
[pre-commit.ci] auto fixes from pre-commit.com hooks \nd53a5fb
Prepare release version 7.1.0 \nd306ec0
Update upcoming trainings (#9744) \n3e4c14b
Merge pull request #9751 from fabianegli/main \n7f924b1
Fix typo in deprecation documentation \n4a8f8ad
build(deps): Bump django from 4.0.2 to 4.0.3 in /testing/plugins_integration ... \nc0fd2d8
build(deps): Bump pytest-asyncio from 0.18.1 to 0.18.2 in /testing/plugins_in... \n843e018
Merge pull request #9732 from nicoddemus/9730-toml-failure \nbc43d66
[automated] Update plugin list (#9733) \ne38d1ca
Improve error message for malformed pyproject.toml files \n- Additional commits viewable in compare view
\n
\n \n
\n\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.\n\n[//]: # (dependabot-automerge-start)\n[//]: # (dependabot-automerge-end)\n\n---\n\n\nDependabot commands and options
\n
\n\nYou can trigger Dependabot actions by commenting on this PR:\n- `@dependabot rebase` will rebase this PR\n- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it\n- `@dependabot merge` will merge this PR after your CI passes on it\n- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it\n- `@dependabot cancel merge` will cancel a previously requested merge and block automerging\n- `@dependabot reopen` will reopen this PR if it is closed\n- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually\n- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)\n\n\n ", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1656/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 1169895600, "node_id": "PR_kwDOBm6k_c40eW7C", "number": 1659, "title": "Tilde encoding", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2022-03-15T16:19:07Z", "updated_at": "2022-03-15T18:01:58Z", "closed_at": "2022-03-15T18:01:57Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/1659", "body": "Refs #1657", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1659/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 930807135, "node_id": "MDU6SXNzdWU5MzA4MDcxMzU=", "number": 1384, "title": "Plugin hook for dynamic metadata", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 22, "created_at": "2021-06-26T22:36:03Z", "updated_at": "2022-03-14T00:36:42Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "@brandonrobertz contributed an implementation of this in PR #1368, which I just merged. Opening this ticket to track further work on this before it goes out in a Datasette release (likely preceded by an alpha).", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1384/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 1131295060, "node_id": "I_kwDOBm6k_c5DbjFU", "number": 1634, "title": "Update Dockerfile generated by `datasette publish`", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 4, "created_at": "2022-02-11T00:07:26Z", "updated_at": "2022-03-11T17:38:08Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "The generated `Dockerfile` currently looks something like this:\r\n```Dockerfile\r\nFROM python:3.8\r\nCOPY . /app\r\nWORKDIR /app\r\n\r\nENV DATASETTE_SECRET 'edab49cbc5d5f6f33238f54852037e3fee710821960b73edd2ce743454182ae2'\r\nRUN pip install -U datasette datasette-auth-passwords datasette-tiddlywiki datasette-graphql\r\nRUN datasette inspect fixtures.db other.db --inspect-file inspect-data.json\r\nENV PORT 8080\r\nEXPOSE 8080\r\nCMD datasette serve --host 0.0.0.0 -i fixtures.db -i other.db --cors --inspect-file inspect-data.json --metadata metadata.json --create --port $PORT /data/*.db\r\n```\r\nThis is still on Python 3.8, and it generates a pretty large image compared to the `Dockerfile` used for https://hub.docker.com/datasetteproject/datasette - https://github.com/simonw/datasette/blob/0.60.2/Dockerfile\r\n\r\nHere's the code that generates it: https://github.com/simonw/datasette/blob/7d24fd405f3c60e4c852c5d746c91aa2ba23cf5b/datasette/utils/__init__.py#L389-L400", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1634/reactions\", \"total_count\": 2, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 2, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 678760988, "node_id": "MDU6SXNzdWU2Nzg3NjA5ODg=", "number": 932, "title": "End-user documentation", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 6, "created_at": "2020-08-13T22:04:39Z", "updated_at": "2022-03-08T15:20:48Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "Datasette's documentation is aimed at people who install and configure it.\r\n\r\nWhat about end users of preconfigured and deployed Datasette instances?\r\n\r\nSomething that can be linked to from the Datasette UI would be really useful.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/932/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 1154399841, "node_id": "I_kwDOBm6k_c5Ezr5h", "number": 1645, "title": "Sensible `cache-control` headers for static assets, including those served by plugins", "user": {"value": 697092, "label": "curiousleo"}, "state": "open", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 4, "created_at": "2022-02-28T18:12:03Z", "updated_at": "2022-03-08T02:59:29Z", "closed_at": null, "author_association": "NONE", "pull_request": null, "body": "## What I'm seeing\r\n\r\nWith `default_cache_ttl = 86400`, I see the following:\r\n\r\nA table view returns `Cache-control: max-age=86400`:\r\n\r\n![Screenshot_20220228_190000](https://user-images.githubusercontent.com/697092/156034352-4d64683e-39c8-49af-81df-0217a5957bbd.png)\r\n\r\nA static asset returns no `Cache-control` header:\r\n\r\n![Screenshot_20220228_185933](https://user-images.githubusercontent.com/697092/156034363-d0b03cc2-5889-4ed2-b601-8c1846b8469a.png)\r\n\r\n## What I expected to see\r\n\r\nI expected the static asset to return a `Cache-control` header indicating that this response can be cached.\r\n\r\n## Why this matters\r\n\r\nI'm productionising a Datasette deployment right now and was looking into putting it behind a Varnish instance. I was surprised to see requests for static assets being served from Datasette rather than Varnish, this is what led me to look more closely at the response headers.\r\n\r\nWhile Datasette serves those static assets pretty quickly, I don't see why Datasette should serve them. By their nature, static assets like images and JS files are very cacheable, so it should be easy to serve them from a cache like Varnish.\r\n\r\n(Note that Varnish can easily be configured to override this header, enabling caching for static assets. But it would be better if this override was not necessary.)\r\n\r\n## Discussion\r\n\r\nIt seems clear to me that serving static assets without a `Cache-control` header is not ideal.\r\n\r\nI see two options here:\r\n\r\nA. Static assets use the same logic as table / SQL views to set the `Cache-control` header based on `default_cache_ttl`.\r\nB. An additional setting for static assets is introduced (`default_static_cache_ttl`, say).", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1645/reactions\", \"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 1161969891, "node_id": "I_kwDOBm6k_c5FQkDj", "number": 1654, "title": "Adopt a code of conduct", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 5, "created_at": "2022-03-07T22:00:24Z", "updated_at": "2022-03-07T22:19:35Z", "closed_at": "2022-03-07T22:19:35Z", "author_association": "OWNER", "pull_request": null, "body": "This is long overdue, especially given the size of the project now.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1654/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1161937073, "node_id": "I_kwDOBm6k_c5FQcCx", "number": 1653, "title": "Mechanism to default a table to sorting by multiple columns", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2022-03-07T21:20:11Z", "updated_at": "2022-03-07T21:23:39Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "### Discussed in https://github.com/simonw/datasette/discussions/1652\r\n\r\n\r\n\r\nOriginally posted by **zaneselvans** March 7, 2022\r\nIt's easy to tell datasette to sort tables using a single column, as [described in the docs](https://docs.datasette.io/en/stable/metadata.html#setting-a-default-sort-order):\r\n\r\n```yaml\r\ndatabases:\r\n ferc1:\r\n tables:\r\n f1_edcfu_epda:\r\n sort: created_time\r\n```\r\n\r\nBut is there some way to tell it to sort using a composite key, like you would in an `ORDER BY` clause instead? For example, the way it's being done **[in this query](https://data.catalyst.coop/ferc1?sql=select%0D%0A++rowid%2C%0D%0A++respondent_id%2C%0D%0A++report_year%2C%0D%0A++spplmnt_num%2C%0D%0A++row_number%2C%0D%0A++row_seq%2C%0D%0A++row_prvlg%2C%0D%0A++acct_num%2C%0D%0A++depr_plnt_base%2C%0D%0A++est_avg_srvce_lf%2C%0D%0A++net_salvage%2C%0D%0A++apply_depr_rate%2C%0D%0A++mrtlty_crv_typ%2C%0D%0A++avg_remaining_lf%2C%0D%0A++report_prd%0D%0Afrom%0D%0A++f1_edcfu_epda%0D%0Awhere%0D%0A++respondent_id+%3D+210%0D%0A++AND+report_year+%3D+2020%0D%0Aorder+by%0D%0A++report_year%2C+report_prd%2C+respondent_id%2C+spplmnt_num%2C+row_number%0D%0Alimit%0D%0A++1000)** on our Datasette?\r\n\r\n```sql\r\nSELECT\r\n respondent_id,\r\n report_year,\r\n spplmnt_num,\r\n row_number,\r\n row_seq,\r\n row_prvlg,\r\n acct_num,\r\n depr_plnt_base,\r\n est_avg_srvce_lf,\r\n net_salvage,\r\n apply_depr_rate,\r\n mrtlty_crv_typ,\r\n avg_remaining_lf,\r\n report_prd\r\nFROM\r\n f1_edcfu_epda\r\nWHERE\r\n respondent_id = 210\r\n AND report_year = 2020\r\nORDER BY\r\n report_year, report_prd, respondent_id, spplmnt_num, row_number\r\nLIMIT\r\n 1000\r\n```\r\n\r\nThe problem here is that by default it's using `rowid` (the SQLite assigned autoincrementing integer key) to order the records, but the table **should** have a natural composite primary key, but the original database that this data is being migrated from doesn't enforce unique primary keys, so there are dupes, and we don't want to drop those rows, and the records are somehow getting jumbled in the database (the `rowid` ordering isn't lined up with the expected ordering based on the composite primary key, though it's close) and this jumbling is confusing to users that expect to see the data ordered based on the natural primary key.\r\n\r\nI've tried setting the `sort` metadata parameter to a list of column names, a tuple of column names, a quoted string of comma-separated column names, a quoted string of a tuple of column names...\r\n\r\n```yaml\r\ndatabases:\r\n ferc1:\r\n tables:\r\n f1_edcfu_epda:\r\n sort: \"(report_year, report_prd, respondent_id, spplmnt_num, row_number)\"\r\n```\r\n\r\nand they all give me server errors like:\r\n\r\n```\r\nCannot sort table by (report_year, report_prd, respondent_id, spplmnt_num, row_number)\r\n```
", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1653/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 1160750713, "node_id": "I_kwDOBm6k_c5FL6Z5", "number": 1650, "title": "Implement redirects from old % encoding to new dash encoding", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 5, "created_at": "2022-03-06T23:40:02Z", "updated_at": "2022-03-07T19:26:15Z", "closed_at": "2022-03-07T19:26:14Z", "author_association": "OWNER", "pull_request": null, "body": "> One big advantage to this scheme is that redirecting old links to `%2F` pages (e.g. https://fivethirtyeight.datasettes.com/fivethirtyeight/twitter-ratio%2Fsenators) is easy - if you see a `%` in the `raw_path`, redirect to that page with the `%` replaced by `-`.\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/1439#issuecomment-1060044007_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1650/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1160432941, "node_id": "PR_kwDOBm6k_c4z_p6S", "number": 1648, "title": "Use dash encoding for table names and row primary keys in URLs", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 7, "created_at": "2022-03-05T19:50:45Z", "updated_at": "2022-03-07T15:38:30Z", "closed_at": "2022-03-07T15:38:30Z", "author_association": "OWNER", "pull_request": "simonw/datasette/pulls/1648", "body": "Refs #1439.\r\n\r\n- [x] Build `dash_encode` / `dash_decode` functions\r\n- [x] Use dash encoding for row primary keys\r\n- [x] Use dash encoding for `?_next=` pagination tokens\r\n- [x] Use dash encoding for table names in URLs\r\n- [x] Use dash encoding for database name\r\n- ~~Implement redirects from previous `%` URLs that replace those with `-`~~ - separate issue: #1650", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1648/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 1160677684, "node_id": "PR_kwDOBm6k_c40AW_v", "number": 1649, "title": "Add /opt/homebrew to where spatialite extension can be found", "user": {"value": 2182, "label": "danp"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2022-03-06T18:09:35Z", "updated_at": "2022-03-06T22:46:00Z", "closed_at": "2022-03-06T19:39:15Z", "author_association": "CONTRIBUTOR", "pull_request": "simonw/datasette/pulls/1649", "body": "Helps homebrew on Apple Silicon setups find spatialite without needing\r\na full path.\r\n\r\nSimilar to #1114", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1649/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 1098275181, "node_id": "PR_kwDOBm6k_c4wwNCl", "number": 1589, "title": "Typo in docs about default redirect status code", "user": {"value": 3556, "label": "davidbgk"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2022-01-10T19:14:36Z", "updated_at": "2022-03-06T02:27:49Z", "closed_at": "2022-03-06T01:58:32Z", "author_association": "CONTRIBUTOR", "pull_request": "simonw/datasette/pulls/1589", "body": null, "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1589/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 1108084641, "node_id": "PR_kwDOBm6k_c4xQ0uZ", "number": 1602, "title": "Update pytest-timeout requirement from <2.1,>=1.4.2 to >=1.4.2,<2.2", "user": {"value": 49699333, "label": "dependabot[bot]"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2022-01-19T13:11:50Z", "updated_at": "2022-03-06T01:41:50Z", "closed_at": "2022-03-06T01:41:49Z", "author_association": "CONTRIBUTOR", "pull_request": "simonw/datasette/pulls/1602", "body": "Updates the requirements on [pytest-timeout](https://github.com/pytest-dev/pytest-timeout) to permit the latest version.\n\nCommits
\n\n8e4800e
Fixup readme \ndc1efca
2.1.0 release \ndd9d608
Add custom hooks specifications for overriding setup_timeout and teardown_tim... \ned8ecd6
module names, they're difficult \n3ab4319
Add changelog \n4f7ebae
Replace deprecated py.io.get_terminal_width() with shutil.get_terminal_size()... \nb8a2fa6
Prep release \n951972d
Update changelog \n748a9c3
Making detection of whether a debugger is currently attached more flexible. (... \nf8a46a1
Github removed the git protocol (#112) \n- Additional commits viewable in compare view
\n
\n \n
\n\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.\n\n[//]: # (dependabot-automerge-start)\n[//]: # (dependabot-automerge-end)\n\n---\n\n\nDependabot commands and options
\n
\n\nYou can trigger Dependabot actions by commenting on this PR:\n- `@dependabot rebase` will rebase this PR\n- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it\n- `@dependabot merge` will merge this PR after your CI passes on it\n- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it\n- `@dependabot cancel merge` will cancel a previously requested merge and block automerging\n- `@dependabot reopen` will reopen this PR if it is closed\n- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually\n- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)\n\n\n ", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1602/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 1112633417, "node_id": "PR_kwDOBm6k_c4xfryi", "number": 1610, "title": "Update asgiref requirement from <3.5.0,>=3.2.10 to >=3.2.10,<3.6.0", "user": {"value": 49699333, "label": "dependabot[bot]"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2022-01-24T13:14:18Z", "updated_at": "2022-03-06T01:30:27Z", "closed_at": "2022-03-06T01:30:27Z", "author_association": "CONTRIBUTOR", "pull_request": "simonw/datasette/pulls/1610", "body": "Updates the requirements on [asgiref](https://github.com/django/asgiref) to permit the latest version.\n\nChangelog
\nSourced from asgiref's changelog.
\n\n3.5.0 (2022-01-22)
\n\n- \n
Python 3.6 is no longer supported, and asyncio calls have been changed to\nuse only the modern versions of the APIs as a result
\n \n- \n
Several causes of RuntimeErrors in cases where an event loop was assigned\nto a thread but not running
\n \n- \n
Speed improvements in the Local class
\n \n
\n3.4.1 (2021-07-01)
\n\n- Fixed an issue with the deadlock detection where it had false positives\nduring exception handling.
\n
\n3.4.0 (2021-06-27)
\n\n- \n
Calling sync_to_async directly from inside itself (which causes a deadlock\nwhen in the default, thread-sensitive mode) now has deadlock detection.
\n \n- \n
asyncio usage has been updated to use the new versions of get_event_loop,\nensure_future, wait and gather, avoiding deprecation warnings in Python 3.10.\nPython 3.6 installs continue to use the old versions; this is only for 3.7+
\n \n- \n
sync_to_async and async_to_sync now have improved type hints that pass\nthrough the underlying function type correctly.
\n \n- \n
All Websocket* types are now spelled WebSocket, to match our specs and the\nofficial spelling. The old names will work until release 3.5.0, but will\nraise deprecation warnings.
\n \n- \n
The typing for WebSocketScope and HTTPScope's extensions
key has been\nfixed.
\n \n
\n3.3.4 (2021-04-06)
\n\n- The async_to_sync type error is now a warning due the high false negative\nrate when trying to detect coroutine-returning callables in Python.
\n
\n3.3.3 (2021-04-06)
\n\n
\n... (truncated)
\n \n\nCommits
\n\n8b61513
Releasing 3.5.0 \nb2e1c9d
Fixed pytest_asyncio deprecation warning. \n2eda551
Added testing for Python 3.10. \n02fecb6
Drop Python 3.6 (#307) \n6689c0a
Added stacklevel to warning in AsyncToSync. \n4364f9b
Changed how StatelessServer handles event loops \n7bc055c
Update implementations.rst (#295) \nc758984
Move current_task import choice to module definition time \ndfe87b2
Fixed #292: Use get_event_loop in class-level code \nb3a65e3
Removed class variable which has been unused since a0bbe90 \n- Additional commits viewable in compare view
\n
\n \n
\n\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.\n\n[//]: # (dependabot-automerge-start)\n[//]: # (dependabot-automerge-end)\n\n---\n\n\nDependabot commands and options
\n
\n\nYou can trigger Dependabot actions by commenting on this PR:\n- `@dependabot rebase` will rebase this PR\n- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it\n- `@dependabot merge` will merge this PR after your CI passes on it\n- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it\n- `@dependabot cancel merge` will cancel a previously requested merge and block automerging\n- `@dependabot reopen` will reopen this PR if it is closed\n- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually\n- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)\n\n\n ", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1610/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 1124191982, "node_id": "PR_kwDOBm6k_c4yFTCp", "number": 1629, "title": "Update pytest requirement from <6.3.0,>=5.2.2 to >=5.2.2,<7.1.0", "user": {"value": 49699333, "label": "dependabot[bot]"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2022-02-04T13:14:10Z", "updated_at": "2022-03-06T01:30:06Z", "closed_at": "2022-03-06T01:30:06Z", "author_association": "CONTRIBUTOR", "pull_request": "simonw/datasette/pulls/1629", "body": "Updates the requirements on [pytest](https://github.com/pytest-dev/pytest) to permit the latest version.\n\nRelease notes
\nSourced from pytest's releases.
\n\n7.0.0
\npytest 7.0.0 (2022-02-03)
\n(Please see the full set of changes for this release also in the 7.0.0rc1 notes below)
\nDeprecations
\n\n- \n
#9488: If custom subclasses of nodes like pytest.Item
{.interpreted-text role="class"} override the\n__init__
method, they should take **kwargs
. See\nuncooperative-constructors-deprecated
{.interpreted-text role="ref"} for details.
\nNote that a deprection warning is only emitted when there is a conflict in the\narguments pytest expected to pass. This deprecation was already part of pytest\n7.0.0rc1 but wasn't documented.
\n \n
\nBug Fixes
\n\n- #9355: Fixed error message prints function decorators when using assert in Python 3.8 and above.
\n- #9396: Ensure
pytest.Config.inifile
{.interpreted-text role="attr"} is available during the pytest_cmdline_main <_pytest.hookspec.pytest_cmdline_main>
{.interpreted-text role="func"} hook (regression during 7.0.0rc1
). \n
\nImproved Documentation
\n\n- #9404: Added extra documentation on alternatives to common misuses of [pytest.warns(None)]{.title-ref} ahead of its deprecation.
\n- #9505: Clarify where the configuration files are located. To avoid confusions documentation mentions\nthat configuration file is located in the root of the repository.
\n
\nTrivial/Internal Changes
\n\n- #9521: Add test coverage to assertion rewrite path.
\n
\npytest 7.0.0rc1 (2021-12-06)
\nBreaking Changes
\n\n- \n
#7259: The Node.reportinfo() <non-python tests>
{.interpreted-text role="ref"} function first return value type has been expanded from [py.path.local | str]{.title-ref} to [os.PathLike[str] | str]{.title-ref}.
\nMost plugins which refer to [reportinfo()]{.title-ref} only define it as part of a custom pytest.Item
{.interpreted-text role="class"} implementation.\nSince [py.path.local]{.title-ref} is a [os.PathLike[str]]{.title-ref}, these plugins are unaffacted.
\nPlugins and users which call [reportinfo()]{.title-ref}, use the first return value and interact with it as a [py.path.local]{.title-ref}, would need to adjust by calling [py.path.local(fspath)]{.title-ref}.\nAlthough preferably, avoid the legacy [py.path.local]{.title-ref} and use [pathlib.Path]{.title-ref}, or use [item.location]{.title-ref} or [item.path]{.title-ref}, instead.
\nNote: pytest was not able to provide a deprecation period for this change.
\n \n
\n\n
\n... (truncated)
\n \n\nCommits
\n\n3554b83
Add note to changelog \n6ea7f99
Prepare release version 7.0.0 \n737b220
[7.0.x] releasing: Add template for major releases (#9597) \n7fa3972
[7.0.x] releasing: Always set doc_version (#9590) \nb304499
[7.0.x] Make 'warnings' and 'deselected' in assert_outcomes optional (#9566) \nf17525d
[7.0.x] doc: Add ellipsis to warning usecase list (#9562) \n0a7be97
ci: Bump up timeout (#9565) \nc17908c
[7.0.x] doc: Recategorize 7.0.0 changelog items (#9564) \nab549bb
[7.0.x] Add missing cooperative constructor changelog (#9563) \n4b1707f
[7.0.x] Autouse linearization graph (#9557) \n- Additional commits viewable in compare view
\n
\n \n
\n\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.\n\n[//]: # (dependabot-automerge-start)\n[//]: # (dependabot-automerge-end)\n\n---\n\n\nDependabot commands and options
\n
\n\nYou can trigger Dependabot actions by commenting on this PR:\n- `@dependabot rebase` will rebase this PR\n- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it\n- `@dependabot merge` will merge this PR after your CI passes on it\n- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it\n- `@dependabot cancel merge` will cancel a previously requested merge and block automerging\n- `@dependabot reopen` will reopen this PR if it is closed\n- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually\n- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)\n\n\n ", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1629/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 1125973221, "node_id": "PR_kwDOBm6k_c4yK44E", "number": 1631, "title": "Update pytest-asyncio requirement from <0.17,>=0.10 to >=0.10,<0.19", "user": {"value": 49699333, "label": "dependabot[bot]"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2022-02-07T13:13:19Z", "updated_at": "2022-03-06T01:29:54Z", "closed_at": "2022-03-06T01:29:53Z", "author_association": "CONTRIBUTOR", "pull_request": "simonw/datasette/pulls/1631", "body": "Updates the requirements on [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) to permit the latest version.\n\nRelease notes
\nSourced from pytest-asyncio's releases.
\n\npytest-asyncio 0.18.0
\n
\ntitle: 'pytest-asyncio: pytest support for asyncio'
\n
\n
\n
\n
\n
\npytest-asyncio is an Apache2 licensed library, written in Python, for\ntesting asyncio code with pytest.
\nasyncio code is usually written in the form of coroutines, which makes\nit slightly more difficult to test using normal testing tools.\npytest-asyncio provides useful fixtures and markers to make testing\neasier.
\n@pytest.mark.asyncio\nasync def test_some_asyncio_code():\n res = await library.do_something()\n assert b"expected result" == res\n
\npytest-asyncio has been strongly influenced by\npytest-tornado.
\nFeatures
\n\n- fixtures for creating and injecting versions of the asyncio event\nloop
\n- fixtures for injecting unused tcp/udp ports
\n- pytest markers for treating tests as asyncio coroutines
\n- easy testing with non-default event loops
\n- support for [async def]{.title-ref} fixtures and async generator\nfixtures
\n- support auto mode to handle all async fixtures and tests\nautomatically by asyncio; provide strict mode if a test suite\nshould work with different async frameworks simultaneously, e.g.\n
asyncio
and trio
. \n
\nInstallation
\n\n
\n... (truncated)
\n \n\nCommits
\n\n \n
\n\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.\n\n[//]: # (dependabot-automerge-start)\n[//]: # (dependabot-automerge-end)\n\n---\n\n\nDependabot commands and options
\n
\n\nYou can trigger Dependabot actions by commenting on this PR:\n- `@dependabot rebase` will rebase this PR\n- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it\n- `@dependabot merge` will merge this PR after your CI passes on it\n- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it\n- `@dependabot cancel merge` will cancel a previously requested merge and block automerging\n- `@dependabot reopen` will reopen this PR if it is closed\n- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually\n- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)\n\n\n ", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1631/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 1160407071, "node_id": "I_kwDOBm6k_c5FKmgf", "number": 1647, "title": "Test failures with SQLite 3.37.0+ due to column affinity case", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 5, "created_at": "2022-03-05T17:37:46Z", "updated_at": "2022-03-05T19:56:28Z", "closed_at": "2022-03-05T19:47:04Z", "author_association": "OWNER", "pull_request": null, "body": "These three tests are failing on my local machine:\r\n```\r\nFAILED tests/test_internals_database.py::test_table_column_details[facetable-expected0] - AssertionError: assert [Column(cid=0, name='pk', type='INTEGER', no...\r\nFAILED tests/test_internals_database.py::test_table_column_details[sortable-expected1] - AssertionError: assert [Column(cid=0, name='pk1', type='varchar(30)'...\r\nFAILED tests/test_table_html.py::test_sort_links - AssertionError: assert [{'a_href': None,\\n 'attrs': {'class': ['col-Link'],\\n 'data-column': '...\r\n```\r\nI ran `pytest --lf -vv` and the output had things like this in it:\r\n```\r\nE - Column(cid=1, name='created', type='text', notnull=0, default_value=None, is_pk=0, hidden=0),\r\nE ? ^^^^\r\nE + Column(cid=1, name='created', type='TEXT', notnull=0, default_value=None, is_pk=0, hidden=0),\r\n...\r\nE {'a_href': '/fixtures/sortable?_sort=sortable_with_nulls_2',\r\nE 'attrs': {'class': ['col-sortable_with_nulls_2'],\r\nE 'data-column': 'sortable_with_nulls_2',\r\nE 'data-column-not-null': '0',\r\nE - 'data-column-type': 'real',\r\nE ? ^^^^\r\nE + 'data-column-type': 'REAL',\r\nE ? ^^^^\r\n```\r\nSomething is causing column types to come back in uppercase where previously they were lowercase.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1647/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1148725876, "node_id": "I_kwDOBm6k_c5EeCp0", "number": 1640, "title": "Support static assets where file length may change, e.g. logs", "user": {"value": 57859326, "label": "broccolihighkicks"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2022-02-24T00:34:42Z", "updated_at": "2022-03-05T01:19:25Z", "closed_at": null, "author_association": "NONE", "pull_request": null, "body": "This is a bit of an oxymoron.\r\n\r\nI am serving a log.txt file for a background process using the Datasette --static CLI. This is useful as I can observe a background process from the web UI to see any errors that occur (instead of spelunking the logs via docker exec/ssh etc).\r\n\r\nI get this error, which I think is because Datasette assumes that the size of the content does not change (but appending new log lines means the content length changes).\r\n\r\n\r\n```python\r\nTraceback (most recent call last):\r\n File \"/usr/local/lib/python3.9/site-packages/datasette/app.py\", line 1181, in route_path\r\n response = await view(request, send)\r\n File \"/usr/local/lib/python3.9/site-packages/datasette/utils/asgi.py\", line 305, in inner_static\r\n await asgi_send_file(send, full_path, chunk_size=chunk_size)\r\n File \"/usr/local/lib/python3.9/site-packages/datasette/utils/asgi.py\", line 280, in asgi_send_file\r\n await send(\r\n File \"/usr/local/lib/python3.9/site-packages/asgi_csrf.py\", line 104, in wrapped_send\r\n await send(event)\r\n File \"/usr/local/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py\", line 460, in send\r\n output = self.conn.send(event)\r\n File \"/usr/local/lib/python3.9/site-packages/h11/_connection.py\", line 468, in send\r\n data_list = self.send_with_data_passthrough(event)\r\n File \"/usr/local/lib/python3.9/site-packages/h11/_connection.py\", line 501, in send_with_data_passthrough\r\n writer(event, data_list.append)\r\n File \"/usr/local/lib/python3.9/site-packages/h11/_writers.py\", line 58, in __call__\r\n self.send_data(event.data, write)\r\n File \"/usr/local/lib/python3.9/site-packages/h11/_writers.py\", line 78, in send_data\r\n raise LocalProtocolError(\"Too much data for declared Content-Length\")\r\nh11._util.LocalProtocolError: Too much data for declared Content-Length\r\nERROR: Exception in ASGI application\r\nTraceback (most recent call last):\r\n File \"/usr/local/lib/python3.9/site-packages/datasette/app.py\", line 1181, in route_path\r\n response = await view(request, send)\r\n File \"/usr/local/lib/python3.9/site-packages/datasette/utils/asgi.py\", line 305, in inner_static\r\n await asgi_send_file(send, full_path, chunk_size=chunk_size)\r\n File \"/usr/local/lib/python3.9/site-packages/datasette/utils/asgi.py\", line 280, in asgi_send_file\r\n await send(\r\n File \"/usr/local/lib/python3.9/site-packages/asgi_csrf.py\", line 104, in wrapped_send\r\n await send(event)\r\n File \"/usr/local/lib/python3.9/site-packages/uvicorn/protocols/http/h11_impl.py\", line 460, in send\r\n output = self.conn.send(event)\r\n File \"/usr/local/lib/python3.9/site-packages/h11/_connection.py\", line 468, in send\r\n data_list = self.send_with_data_passthrough(event)\r\n File \"/usr/local/lib/python3.9/site-packages/h11/_connection.py\", line 501, in send_with_data_passthrough\r\n writer(event, data_list.append)\r\n File \"/usr/local/lib/python3.9/site-packages/h11/_writers.py\", line 58, in __call__\r\n self.send_data(event.data, write)\r\n File \"/usr/local/lib/python3.9/site-packages/h11/_writers.py\", line 78, in send_data\r\n raise LocalProtocolError(\"Too much data for declared Content-Length\")\r\nh11._util.LocalProtocolError: Too much data for declared Content-Length\r\n```\r\n\r\nThanks, I am finding Datasette very useful.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1640/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 1152072027, "node_id": "I_kwDOBm6k_c5Eqzlb", "number": 1642, "title": "Dependency issue with asgiref and uvicorn", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2022-02-26T18:00:35Z", "updated_at": "2022-03-05T01:11:27Z", "closed_at": "2022-03-05T01:11:17Z", "author_association": "OWNER", "pull_request": null, "body": "```\r\nERROR: After October 2020 you may experience errors when installing or updating packages. This is because pip will change the way that it resolves dependency conflicts.\r\n\r\nWe recommend you use --use-feature=2020-resolver to test your packages with the new resolver before it becomes the default.\r\n\r\ndatasette 0.60.2 requires asgiref<3.5.0,>=3.2.10, but you'll have asgiref 3.5.0 which is incompatible.\r\n```\r\nThat's after I forced an upgrade of `uvicorn` due to this warning:\r\n```\r\nERROR: After October 2020 you may experience errors when installing or updating packages. This is because pip will change the way that it resolves dependency conflicts.\r\n\r\nWe recommend you use --use-feature=2020-resolver to test your packages with the new resolver before it becomes the default.\r\n\r\nuvicorn 0.13.1 requires click==7.*, but you'll have click 8.0.4 which is incompatible.\r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1642/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1149310456, "node_id": "I_kwDOBm6k_c5EgRX4", "number": 1641, "title": "Tweak mobile keyboard settings", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 1, "created_at": "2022-02-24T13:47:10Z", "updated_at": "2022-02-24T13:49:26Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "https://developer.apple.com/library/archive/documentation/StringsTextFonts/Conceptual/TextAndWebiPhoneOS/KeyboardManagement/KeyboardManagement.html#//apple_ref/doc/uid/TP40009542-CH5-SW12\r\n\r\n`autocorrect=\"off\"` is worth experimenting with.\r\n\r\nTwitter: https://twitter.com/forestgregg/status/1496842959563726852", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1641/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 1148638868, "node_id": "I_kwDOBm6k_c5EdtaU", "number": 1639, "title": "Make datasette-redirect-forbidden unneccessary", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2022-02-23T22:18:46Z", "updated_at": "2022-02-23T22:18:46Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "I wrote `datasette-redirect-forbidden` today because I needed 403 errors to redirect to `/-/login` and it was the quickest way to solve that problem.\r\n\r\nThis should be a feature of Datasette core.\r\n\r\n- https://github.com/simonw/datasette-redirect-forbidden/issues/2", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1639/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 677272618, "node_id": "MDU6SXNzdWU2NzcyNzI2MTg=", "number": 928, "title": "Test failures caused by failed attempts to mock pip", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 4, "created_at": "2020-08-11T23:53:18Z", "updated_at": "2022-02-23T16:19:47Z", "closed_at": "2020-08-12T00:07:49Z", "author_association": "OWNER", "pull_request": null, "body": "Errors like this one:\r\n\r\nhttps://github.com/simonw/datasette/pull/927/checks?check_run_id=973559696\r\n\r\n```\r\n2020-08-11T23:36:39.8801334Z =================================== FAILURES ===================================\r\n2020-08-11T23:36:39.8802411Z _________________________________ test_install _________________________________\r\n2020-08-11T23:36:39.8803242Z \r\n2020-08-11T23:36:39.8804935Z thing = \r\n2020-08-11T23:36:39.8806663Z comp = 'main', import_path = 'pip._internal.cli.main'\r\n2020-08-11T23:36:39.8807696Z \r\n2020-08-11T23:36:39.8808728Z def _dot_lookup(thing, comp, import_path):\r\n2020-08-11T23:36:39.8810573Z try:\r\n2020-08-11T23:36:39.8812262Z > return getattr(thing, comp)\r\n2020-08-11T23:36:39.8817136Z E AttributeError: module 'pip._internal.cli' has no attribute 'main'\r\n2020-08-11T23:36:39.8843043Z \r\n2020-08-11T23:36:39.8855951Z /opt/hostedtoolcache/Python/3.8.5/x64/lib/python3.8/unittest/mock.py:1215: AttributeError\r\n2020-08-11T23:36:39.8873372Z \r\n2020-08-11T23:36:39.8877803Z During handling of the above exception, another exception occurred:\r\n2020-08-11T23:36:39.8906532Z \r\n2020-08-11T23:36:39.8925767Z def get_src_prefix():\r\n2020-08-11T23:36:39.8928277Z # type: () -> str\r\n2020-08-11T23:36:39.8930068Z if running_under_virtualenv():\r\n2020-08-11T23:36:39.8949721Z src_prefix = os.path.join(sys.prefix, 'src')\r\n2020-08-11T23:36:39.8951813Z else:\r\n2020-08-11T23:36:39.8969014Z # FIXME: keep src in cwd for now (it is not a temporary folder)\r\n2020-08-11T23:36:39.9012110Z try:\r\n2020-08-11T23:36:39.9013489Z > src_prefix = os.path.join(os.getcwd(), 'src')\r\n2020-08-11T23:36:39.9014538Z E FileNotFoundError: [Errno 2] No such file or directory\r\n2020-08-11T23:36:39.9016122Z \r\n2020-08-11T23:36:39.9017617Z /opt/hostedtoolcache/Python/3.8.5/x64/lib/python3.8/site-packages/pip/_internal/locations.py:50: FileNotFoundError\r\n2020-08-11T23:36:39.9018802Z \r\n2020-08-11T23:36:39.9020070Z During handling of the above exception, another exception occurred:\r\n2020-08-11T23:36:39.9020930Z \r\n2020-08-11T23:36:39.9022275Z args = (), keywargs = {}\r\n2020-08-11T23:36:39.9023183Z \r\n2020-08-11T23:36:39.9024077Z @wraps(func)\r\n2020-08-11T23:36:39.9024984Z def patched(*args, **keywargs):\r\n2020-08-11T23:36:39.9028770Z > with self.decoration_helper(patched,\r\n2020-08-11T23:36:39.9031861Z args,\r\n2020-08-11T23:36:39.9038358Z keywargs) as (newargs, newkeywargs):\r\n2020-08-11T23:36:39.9039654Z \r\n2020-08-11T23:36:39.9040566Z /opt/hostedtoolcache/Python/3.8.5/x64/lib/python3.8/unittest/mock.py:1322: \r\n2020-08-11T23:36:39.9041492Z _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\n```", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/928/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1142107925, "node_id": "I_kwDOBm6k_c5EEy8V", "number": 1638, "title": "`filters_from_request` plugin hook docs should mention that returning an async function is allowed", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 0, "created_at": "2022-02-18T00:08:26Z", "updated_at": "2022-02-18T00:08:26Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "https://docs.datasette.io/en/stable/plugin_hooks.html#filters-from-request-request-database-table-datasette doesn't mention that you can return an `async` function - but you can, and in fact Datasette itself uses that here: https://github.com/simonw/datasette/blob/aa7f0037a46eb76ae6fe9bf2a1f616c58738ecdf/datasette/filters.py#L43-L47", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1638/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 335200136, "node_id": "MDU6SXNzdWUzMzUyMDAxMzY=", "number": 327, "title": "Explore if SquashFS can be used to shrink size of packaged Docker containers", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": null, "comments": 4, "created_at": "2018-06-24T18:15:16Z", "updated_at": "2022-02-17T23:37:24Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "Inspired by this article: https://cldellow.com/2018/06/22/sqlite-parquet-vtable.html#sqlite-database-indexed--squashed\r\n\r\nhttps://en.wikipedia.org/wiki/SquashFS is \"a compressed read-only file system for Linux\" - which means it could be a really nice fit for Datasette and its read-only SQLite databases.\r\n\r\nIt would be interesting to explore a Dockerfile recipe that used SquashFS to compress the SQLite database file that was bundled up by `datasette package` and friends.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/327/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 764059235, "node_id": "MDU6SXNzdWU3NjQwNTkyMzU=", "number": 1143, "title": "More flexible CORS support in core, to encourage good security practices", "user": {"value": 114388, "label": "yurivish"}, "state": "open", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 6, "created_at": "2020-12-12T17:06:35Z", "updated_at": "2022-02-13T17:41:17Z", "closed_at": null, "author_association": "NONE", "pull_request": null, "body": "It would be nice if the `--cors` option accepted an origin regex to more securely allow secure local development.\r\n\r\nAs an example, Observable notebooks namespace every user's notebooks by their username and user content is served from username.observableusercontent.com, so you would set `--cors-origin username.observableusercontent.com` to restrict access to a local development Datasette instance to only your own notebooks, rather than exposing the data to any website that makes a request.\r\n\r\nThank you for all of your work on Datasette!", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1143/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 1109783030, "node_id": "I_kwDOBm6k_c5CJfH2", "number": 1607, "title": "More detailed information about installed SpatiaLite version", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 2, "created_at": "2022-01-20T21:28:03Z", "updated_at": "2022-02-09T06:42:02Z", "closed_at": "2022-02-09T06:32:28Z", "author_association": "OWNER", "pull_request": null, "body": "https://www.gaia-gis.it/gaia-sins/spatialite-sql-5.0.0.html#version has a whole bunch of interesting functions for things like `freexl_version()` and `geos_version()` and `HasMathSQL()` and suchlike.\r\n\r\nThese could be shown on the `/-/versions` page.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1607/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1101705012, "node_id": "PR_kwDOBm6k_c4w7eqc", "number": 1593, "title": "Update pytest-asyncio requirement from <0.17,>=0.10 to >=0.10,<0.18", "user": {"value": 49699333, "label": "dependabot[bot]"}, "state": "closed", "locked": 0, "assignee": null, "milestone": null, "comments": 2, "created_at": "2022-01-13T13:11:50Z", "updated_at": "2022-02-07T13:13:24Z", "closed_at": "2022-02-07T13:13:23Z", "author_association": "CONTRIBUTOR", "pull_request": "simonw/datasette/pulls/1593", "body": "Updates the requirements on [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) to permit the latest version.\n\nRelease notes
\nSourced from pytest-asyncio's releases.
\n\npytest-asyncio 0.17.0
\n
\ntitle: 'pytest-asyncio: pytest support for asyncio'
\n
\n
\n
\n
\n
\npytest-asyncio is an Apache2 licensed library, written in Python, for\ntesting asyncio code with pytest.
\nasyncio code is usually written in the form of coroutines, which makes\nit slightly more difficult to test using normal testing tools.\npytest-asyncio provides useful fixtures and markers to make testing\neasier.
\n@pytest.mark.asyncio\nasync def test_some_asyncio_code():\n res = await library.do_something()\n assert b"expected result" == res\n
\npytest-asyncio has been strongly influenced by\npytest-tornado.
\nFeatures
\n\n- fixtures for creating and injecting versions of the asyncio event\nloop
\n- fixtures for injecting unused tcp/udp ports
\n- pytest markers for treating tests as asyncio coroutines
\n- easy testing with non-default event loops
\n- support for [async def]{.title-ref} fixtures and async generator\nfixtures
\n- support auto mode to handle all async fixtures and tests\nautomatically by asyncio; provide strict mode if a test suite\nshould work with different async frameworks simultaneously, e.g.\n
asyncio
and trio
. \n
\nInstallation
\n\n
\n... (truncated)
\n \n\nCommits
\n\n2e2d5d2
Bump to 0.17 release \n90436c9
Fix pandoc installation procedure \nd291c66
Convert README.rst to Markdown for making githun release \n141937b
Fix release artifacts \n696cf7d
Fix trove classifier for asyncio \n8ccb306
Build on tag \ncd84987
Release process automation (#252) \n2eb12a7
Setup GitHub Workflows linter and yaml-reformatter (#253) \nd28b826
Bump codecov/codecov-action from 1 to 2.1.0 (#251) \n2f523ba
Configure dependabot version updater (#250) \n- Additional commits viewable in compare view
\n
\n \n
\n\n\nDependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting `@dependabot rebase`.\n\n[//]: # (dependabot-automerge-start)\n[//]: # (dependabot-automerge-end)\n\n---\n\n\nDependabot commands and options
\n
\n\nYou can trigger Dependabot actions by commenting on this PR:\n- `@dependabot rebase` will rebase this PR\n- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it\n- `@dependabot merge` will merge this PR after your CI passes on it\n- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it\n- `@dependabot cancel merge` will cancel a previously requested merge and block automerging\n- `@dependabot reopen` will reopen this PR if it is closed\n- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually\n- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)\n- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)\n\n\n ", "repo": {"value": 107914493, "label": "datasette"}, "type": "pull", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1593/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": 0, "state_reason": null}
{"id": 779691739, "node_id": "MDU6SXNzdWU3Nzk2OTE3Mzk=", "number": 1176, "title": "Policy on documenting \"public\" datasette.utils functions", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 13, "created_at": "2021-01-05T22:55:25Z", "updated_at": "2022-02-07T06:43:32Z", "closed_at": "2022-02-07T06:42:58Z", "author_association": "OWNER", "pull_request": null, "body": "https://github.com/simonw/datasette-css-properties starts [like this](https://github.com/simonw/datasette-css-properties/blob/0.1/datasette_css_properties/__init__.py#L1-L3):\r\n\r\n```python\r\nfrom datasette import hookimpl\r\nfrom datasette.utils.asgi import Response\r\nfrom datasette.utils import escape_css_string, to_css_class\r\n```\r\n`escape_css_string` and `to_css_class` are not documented, which means relying on them is risky since there's no promise that they won't change.\r\n\r\nWould be good to figure out a policy on this, and maybe promote some of them to \"documented\" status.", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1176/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1125576543, "node_id": "I_kwDOBm6k_c5DFu9f", "number": 1630, "title": "Review datasette.utils and decide which functions should be documented for 1.0", "user": {"value": 9599, "label": "simonw"}, "state": "open", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 0, "created_at": "2022-02-07T06:39:52Z", "updated_at": "2022-02-07T06:39:52Z", "closed_at": null, "author_association": "OWNER", "pull_request": null, "body": "Follows:\r\n- #1176", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1630/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": null}
{"id": 688622148, "node_id": "MDU6SXNzdWU2ODg2MjIxNDg=", "number": 957, "title": "Simplify imports of common classes", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 7, "created_at": "2020-08-29T23:44:04Z", "updated_at": "2022-02-06T06:36:41Z", "closed_at": "2022-02-06T06:34:37Z", "author_association": "OWNER", "pull_request": null, "body": "There are only a few classes that plugins need to import. It would be nice if these imports were as short and memorable as possible.\r\n\r\nFor example:\r\n```python\r\nfrom datasette.app import Datasette\r\nfrom datasette.utils.asgi import Response\r\n```\r\nCould both become:\r\n```python\r\nfrom datasette import Datasette\r\nfrom datasette import Response\r\n```\r\n", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/957/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}
{"id": 1087181951, "node_id": "I_kwDOBm6k_c5AzRR_", "number": 1576, "title": "Traces should include SQL executed by subtasks created with `asyncio.gather`", "user": {"value": 9599, "label": "simonw"}, "state": "closed", "locked": 0, "assignee": null, "milestone": {"value": 3268330, "label": "Datasette 1.0"}, "comments": 12, "created_at": "2021-12-22T20:52:02Z", "updated_at": "2022-02-05T05:21:35Z", "closed_at": "2022-02-05T05:19:53Z", "author_association": "OWNER", "pull_request": null, "body": "I tried running some parallel SQL queries using `asyncio.gather()` but the SQL that was executed didn't show up in the trace rendered by https://datasette.io/plugins/datasette-pretty-traces\r\n\r\nI realized that was because traces are keyed against the current task ID, which changes when a sub-task is run using `asyncio.gather` or similar.\r\n\r\nThe faceting and suggest faceting queries are missing from this trace:\r\n\r\n![image](https://user-images.githubusercontent.com/9599/147153855-2d611f07-922a-4d18-9e6e-4be89e010dc4.png)\r\n\r\n> The reason they aren't showing up in the traces is that traces are stored just for the currently executing `asyncio` task ID: https://github.com/simonw/datasette/blob/ace86566b28280091b3844cf5fbecd20158e9004/datasette/tracer.py#L13-L25\r\n>\r\n> This is so traces for other incoming requests don't end up mixed together. But there's no current mechanism to track async tasks that are effectively \"child tasks\" of the current request, and hence should be tracked the same.\r\n>\r\n> https://stackoverflow.com/a/69349501/6083 suggests that you pass the task ID as an argument to the child tasks that are executed using `asyncio.gather()` to work around this kind of problem.\r\n\r\n_Originally posted by @simonw in https://github.com/simonw/datasette/issues/1518#issuecomment-999870993_", "repo": {"value": 107914493, "label": "datasette"}, "type": "issue", "active_lock_reason": null, "performed_via_github_app": null, "reactions": "{\"url\": \"https://api.github.com/repos/simonw/datasette/issues/1576/reactions\", \"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "draft": null, "state_reason": "completed"}