{"html_url": "https://github.com/simonw/datasette/issues/1746#issuecomment-1133254599", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1746", "id": 1133254599, "node_id": "IC_kwDOBm6k_c5DjBfH", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T19:33:08Z", "updated_at": "2022-05-20T19:33:08Z", "author_association": "OWNER", "body": "Actually maybe I don't? I just noticed that on other pages on https://docs.datasette.io/en/stable/installation.html the only way to get back to that useful table of context / index page at https://docs.datasette.io/en/stable/index.html is by clicking the tiny house icon. Can I do better or should I have the logo do that?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243498298, "label": "Switch documentation theme to Furo"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1746#issuecomment-1133252598", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1746", "id": 1133252598, "node_id": "IC_kwDOBm6k_c5DjA_2", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T19:31:30Z", "updated_at": "2022-05-20T19:31:30Z", "author_association": "OWNER", "body": "I'd also like to bring back this stable / latest / version indicator:\r\n\r\n![CleanShot 2022-05-20 at 12 30 49@2x](https://user-images.githubusercontent.com/9599/169598732-e2093ec1-7eaf-40dd-acfa-1a7c31091ff1.png)\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243498298, "label": "Switch documentation theme to Furo"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1746#issuecomment-1133250151", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1746", "id": 1133250151, "node_id": "IC_kwDOBm6k_c5DjAZn", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T19:29:37Z", "updated_at": "2022-05-20T19:29:37Z", "author_association": "OWNER", "body": "I want the Datasette logo in the sidebar to link to https://datasette.io/\r\n\r\nLooks like I can do that by dropping in my own `sidebar/brand.html` template based on this:\r\n\r\nhttps://github.com/pradyunsg/furo/blob/0c2acbbd23f8146dd0ae50a2ba57258c1f63ea9f/src/furo/theme/furo/sidebar/brand.html", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243498298, "label": "Switch documentation theme to Furo"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1746#issuecomment-1133246791", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1746", "id": 1133246791, "node_id": "IC_kwDOBm6k_c5Di_lH", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T19:26:49Z", "updated_at": "2022-05-20T19:26:49Z", "author_association": "OWNER", "body": "Putting this in the `css/custom.css` file seems to work for fixing that logo problem:\r\n\r\n```css\r\nbody[data-theme=\"dark\"] .sidebar-logo-container {\r\n background-color: white;\r\n padding: 5px;\r\n opacity: 0.6;\r\n}\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243498298, "label": "Switch documentation theme to Furo"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1746#issuecomment-1133242063", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1746", "id": 1133242063, "node_id": "IC_kwDOBm6k_c5Di-bP", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T19:22:49Z", "updated_at": "2022-05-20T19:22:49Z", "author_association": "OWNER", "body": "I have some custom CSS in this file:\r\n\r\nhttps://github.com/simonw/datasette/blob/1465fea4798599eccfe7e8f012bd8d9adfac3039/docs/_static/css/custom.css#L1-L7\r\n\r\nI tested and the `overflow-wrap: anywhere` is still needed for this fix:\r\n- #828\r\n\r\nThe `.wy-side-nav-search` bit is no longer needed with the new theme.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243498298, "label": "Switch documentation theme to Furo"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1748#issuecomment-1133232301", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1748", "id": 1133232301, "node_id": "IC_kwDOBm6k_c5Di8Ct", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T19:15:00Z", "updated_at": "2022-05-20T19:15:00Z", "author_association": "OWNER", "body": "Now live on https://docs.datasette.io/en/latest/testing_plugins.html\r\n\r\n![copy](https://user-images.githubusercontent.com/9599/169596586-396eb6c7-ef5a-405a-bb21-348499478d9b.gif)\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243517592, "label": "Add copy buttons next to code examples in the documentation"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1747#issuecomment-1133229196", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1747", "id": 1133229196, "node_id": "IC_kwDOBm6k_c5Di7SM", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T19:12:30Z", "updated_at": "2022-05-20T19:12:30Z", "author_association": "OWNER", "body": "https://docs.datasette.io/en/latest/getting_started.html#follow-a-tutorial", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243512344, "label": "Add tutorials to the getting started guide"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1748#issuecomment-1133225441", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1748", "id": 1133225441, "node_id": "IC_kwDOBm6k_c5Di6Xh", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T19:09:13Z", "updated_at": "2022-05-20T19:09:13Z", "author_association": "OWNER", "body": "I'm going to add this Sphinx plugin: https://github.com/executablebooks/sphinx-copybutton", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243517592, "label": "Add copy buttons next to code examples in the documentation"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1153#issuecomment-1133222848", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1153", "id": 1133222848, "node_id": "IC_kwDOBm6k_c5Di5vA", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T19:07:10Z", "updated_at": "2022-05-20T19:07:10Z", "author_association": "OWNER", "body": "I could use https://github.com/pradyunsg/sphinx-inline-tabs for this - recommended by https://pradyunsg.me/furo/recommendations/", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 771202454, "label": "Use YAML examples in documentation by default, not JSON"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1746#issuecomment-1133217219", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1746", "id": 1133217219, "node_id": "IC_kwDOBm6k_c5Di4XD", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T18:58:54Z", "updated_at": "2022-05-20T18:58:54Z", "author_association": "OWNER", "body": "Need to address other customizations I've made in https://github.com/simonw/datasette/blob/0.62a0/docs/_templates/layout.html - such as Plausible analytics and some custom JavaScript.\r\n\r\nhttps://github.com/simonw/datasette/blob/943aa2e1f7341cb51e60332cde46bde650c64217/docs/_templates/layout.html#L1-L61", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243498298, "label": "Switch documentation theme to Furo"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1746#issuecomment-1133215684", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1746", "id": 1133215684, "node_id": "IC_kwDOBm6k_c5Di3_E", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T18:56:29Z", "updated_at": "2022-05-20T18:56:29Z", "author_association": "OWNER", "body": "One other problem: in dark mode the Datasette logo looks bad:\r\n\r\n\r\n\r\nThis helps a bit:\r\n\r\n```css\r\n.sidebar-logo-container {\r\n background-color: white;\r\n padding: 5px;\r\n opacity: 0.6;\r\n}\r\n```\r\n\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243498298, "label": "Switch documentation theme to Furo"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1746#issuecomment-1133210942", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1746", "id": 1133210942, "node_id": "IC_kwDOBm6k_c5Di20-", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T18:49:40Z", "updated_at": "2022-05-20T18:49:40Z", "author_association": "OWNER", "body": "And for those local table of contents, do this:\r\n\r\n```rst\r\n.. contents::\r\n :local:\r\n :class: this-will-duplicate-information-and-it-is-still-useful-here\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243498298, "label": "Switch documentation theme to Furo"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1746#issuecomment-1133210651", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1746", "id": 1133210651, "node_id": "IC_kwDOBm6k_c5Di2wb", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T18:49:11Z", "updated_at": "2022-05-20T18:49:11Z", "author_association": "OWNER", "body": "I found a workaround for the no-longer-nested left hand navigation: drop this into `_templates/sidebar/navigation.html`:\r\n```html+jinja\r\n
\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243498298, "label": "Switch documentation theme to Furo"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1746#issuecomment-1133210032", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1746", "id": 1133210032, "node_id": "IC_kwDOBm6k_c5Di2mw", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-20T18:48:17Z", "updated_at": "2022-05-20T18:48:17Z", "author_association": "OWNER", "body": "A couple of changes I want to make. First, I don't really like the way Furo keeps the in-page titles in a separate menu on the right rather than expanding them on the left.\r\n\r\nI like this:\r\n\r\n![CleanShot 2022-05-20 at 11 43 33@2x](https://user-images.githubusercontent.com/9599/169592611-ac0f9bd2-ff99-49b6-88d3-92dace9d85a6.png)\r\n\r\nFuro wants to do this instead:\r\n\r\n\r\n\r\nI also still want to include those inline tables of contents on the two pages that have them:\r\n\r\n- https://docs.datasette.io/en/stable/installation.html\r\n- https://docs.datasette.io/en/stable/plugin_hooks.html", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1243498298, "label": "Switch documentation theme to Furo"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1744#issuecomment-1129251699", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1744", "id": 1129251699, "node_id": "IC_kwDOBm6k_c5DTwNz", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-17T19:44:47Z", "updated_at": "2022-05-17T19:46:38Z", "author_association": "OWNER", "body": "Updated docs: https://docs.datasette.io/en/latest/getting_started.html#using-datasette-on-your-own-computer and https://docs.datasette.io/en/latest/cli-reference.html#datasette-serve-help", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1239008850, "label": "`--nolock` feature for opening locked databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1745#issuecomment-1129252603", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1745", "id": 1129252603, "node_id": "IC_kwDOBm6k_c5DTwb7", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-17T19:45:51Z", "updated_at": "2022-05-17T19:45:51Z", "author_association": "OWNER", "body": "Now documented here: https://docs.datasette.io/en/latest/contributing.html#running-cog", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1239080102, "label": "Documentation on running cog"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1744#issuecomment-1129243427", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1744", "id": 1129243427, "node_id": "IC_kwDOBm6k_c5DTuMj", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-17T19:35:02Z", "updated_at": "2022-05-17T19:35:02Z", "author_association": "OWNER", "body": "One thing to note is that the `datasette-copy-to-memory` plugin broke with a locked file, because it does this: https://github.com/simonw/datasette-copy-to-memory/blob/d541c18a78ae6f707a8f9b1e7fc4c020a9f68f2e/datasette_copy_to_memory/__init__.py#L27\r\n```python\r\ntmp.execute(\"ATTACH DATABASE ? AS _copy_from\", [db.path])\r\n```\r\nThat would need to use a URI filename too for it to work with locked files.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1239008850, "label": "`--nolock` feature for opening locked databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1744#issuecomment-1129241873", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1744", "id": 1129241873, "node_id": "IC_kwDOBm6k_c5DTt0R", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-17T19:33:16Z", "updated_at": "2022-05-17T19:33:16Z", "author_association": "OWNER", "body": "I'm going to skip adding a test for this - the test logic would have to be pretty convoluted to exercise it properly, and it's a pretty minor and low-risk feature in the scheme of things.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1239008850, "label": "`--nolock` feature for opening locked databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1744#issuecomment-1129241283", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1744", "id": 1129241283, "node_id": "IC_kwDOBm6k_c5DTtrD", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-17T19:32:35Z", "updated_at": "2022-05-17T19:32:35Z", "author_association": "OWNER", "body": "I tried writing a test like this:\r\n\r\n```python\r\n@pytest.mark.parametrize(\"locked\", (True, False))\r\ndef test_locked_sqlite_db(tmp_path_factory, locked):\r\n dir = tmp_path_factory.mktemp(\"test_locked_sqlite_db\")\r\n test_db = str(dir / \"test.db\")\r\n sqlite3.connect(test_db).execute(\"create table t (id integer primary key)\")\r\n if locked:\r\n fp = open(test_db, \"w\")\r\n fcntl.lockf(fp.fileno(), fcntl.LOCK_EX)\r\n runner = CliRunner()\r\n result = runner.invoke(\r\n cli,\r\n [\r\n \"serve\",\r\n \"--memory\",\r\n \"--get\",\r\n \"/test\",\r\n ],\r\n catch_exceptions=False,\r\n )\r\n```\r\nBut it didn't work, because the test runs in the same process - so taking an exclusive lock on that file didn't cause an error when the test later tried to access it via Datasette!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1239008850, "label": "`--nolock` feature for opening locked databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1744#issuecomment-1129187486", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1744", "id": 1129187486, "node_id": "IC_kwDOBm6k_c5DTgie", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-17T18:28:49Z", "updated_at": "2022-05-17T18:28:49Z", "author_association": "OWNER", "body": "I think I do that with `fcntl.flock()`: https://docs.python.org/3/library/fcntl.html#fcntl.flock", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1239008850, "label": "`--nolock` feature for opening locked databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1744#issuecomment-1129185356", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1744", "id": 1129185356, "node_id": "IC_kwDOBm6k_c5DTgBM", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-17T18:26:26Z", "updated_at": "2022-05-17T18:26:26Z", "author_association": "OWNER", "body": "Not sure how to test this - I'd need to open my own lock against a database file somehow.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1239008850, "label": "`--nolock` feature for opening locked databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1744#issuecomment-1129184908", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1744", "id": 1129184908, "node_id": "IC_kwDOBm6k_c5DTf6M", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-17T18:25:57Z", "updated_at": "2022-05-17T18:25:57Z", "author_association": "OWNER", "body": "I knocked out a quick prototype of this and it worked!\r\n\r\n datasette ~/Library/Application\\ Support/Google/Chrome/Default/History --nolock\r\n\r\nHere's the prototype diff:\r\n\r\n```diff\r\ndiff --git a/datasette/app.py b/datasette/app.py\r\nindex b7b8437..f43700d 100644\r\n--- a/datasette/app.py\r\n+++ b/datasette/app.py\r\n@@ -213,6 +213,7 @@ class Datasette:\r\n config_dir=None,\r\n pdb=False,\r\n crossdb=False,\r\n+ nolock=False,\r\n ):\r\n assert config_dir is None or isinstance(\r\n config_dir, Path\r\n@@ -238,6 +239,7 @@ class Datasette:\r\n self.databases = collections.OrderedDict()\r\n self._refresh_schemas_lock = asyncio.Lock()\r\n self.crossdb = crossdb\r\n+ self.nolock = nolock\r\n if memory or crossdb or not self.files:\r\n self.add_database(Database(self, is_memory=True), name=\"_memory\")\r\n # memory_name is a random string so that each Datasette instance gets its own\r\ndiff --git a/datasette/cli.py b/datasette/cli.py\r\nindex 3c6e1b2..7e44665 100644\r\n--- a/datasette/cli.py\r\n+++ b/datasette/cli.py\r\n@@ -452,6 +452,11 @@ def uninstall(packages, yes):\r\n is_flag=True,\r\n help=\"Enable cross-database joins using the /_memory database\",\r\n )\r\n+@click.option(\r\n+ \"--nolock\",\r\n+ is_flag=True,\r\n+ help=\"Ignore locking and open locked files in read-only mode\",\r\n+)\r\n @click.option(\r\n \"--ssl-keyfile\",\r\n help=\"SSL key file\",\r\n@@ -486,6 +491,7 @@ def serve(\r\n open_browser,\r\n create,\r\n crossdb,\r\n+ nolock,\r\n ssl_keyfile,\r\n ssl_certfile,\r\n return_instance=False,\r\n@@ -545,6 +551,7 @@ def serve(\r\n version_note=version_note,\r\n pdb=pdb,\r\n crossdb=crossdb,\r\n+ nolock=nolock,\r\n )\r\n \r\n # if files is a single directory, use that as config_dir=\r\ndiff --git a/datasette/database.py b/datasette/database.py\r\nindex 44d3266..fa55804 100644\r\n--- a/datasette/database.py\r\n+++ b/datasette/database.py\r\n@@ -89,6 +89,8 @@ class Database:\r\n # mode=ro or immutable=1?\r\n if self.is_mutable:\r\n qs = \"?mode=ro\"\r\n+ if self.ds.nolock:\r\n+ qs += \"&nolock=1\"\r\n else:\r\n qs = \"?immutable=1\"\r\n assert not (write and not self.is_mutable)\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1239008850, "label": "`--nolock` feature for opening locked databases"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1742#issuecomment-1128052948", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1742", "id": 1128052948, "node_id": "IC_kwDOBm6k_c5DPLjU", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-16T19:28:31Z", "updated_at": "2022-05-16T19:28:31Z", "author_association": "OWNER", "body": "The trace mechanism is a bit gnarly - it's actually done by some ASGI middleware I wrote, so I'm pretty sure the bug is in there somewhere: https://github.com/simonw/datasette/blob/280ff372ab30df244f6c54f6f3002da57334b3d7/datasette/tracer.py#L73", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1237586379, "label": "?_trace=1 fails with datasette-geojson for some reason"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1742#issuecomment-1128033018", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1742", "id": 1128033018, "node_id": "IC_kwDOBm6k_c5DPGr6", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-16T19:06:38Z", "updated_at": "2022-05-16T19:06:38Z", "author_association": "OWNER", "body": "The same URL with `.json` instead works fine: https://calands.datasettes.com/calands/CPAD_2020a_SuperUnits.json?_sort=id&id__exact=4&_labels=on&_trace=1", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1237586379, "label": "?_trace=1 fails with datasette-geojson for some reason"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1739#issuecomment-1117662420", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1739", "id": 1117662420, "node_id": "IC_kwDOBm6k_c5CnizU", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-04T18:21:18Z", "updated_at": "2022-05-04T18:21:18Z", "author_association": "OWNER", "body": "That prototype is now public: https://github.com/simonw/datasette-lite", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223699280, "label": ".db downloads should be served with an ETag"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1739#issuecomment-1116215371", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1739", "id": 1116215371, "node_id": "IC_kwDOBm6k_c5CiBhL", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-03T15:12:16Z", "updated_at": "2022-05-03T15:12:16Z", "author_association": "OWNER", "body": "That worked - both DBs are 304 for me now on a subsequent load of the page:\r\n\r\n\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223699280, "label": ".db downloads should be served with an ETag"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1739#issuecomment-1116183369", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1739", "id": 1116183369, "node_id": "IC_kwDOBm6k_c5Ch5tJ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-03T14:43:14Z", "updated_at": "2022-05-03T14:43:14Z", "author_association": "OWNER", "body": "Relevant tests start here: https://github.com/simonw/datasette/blob/d60f163528f466b1127b2935c3b6869c34fd6545/tests/test_html.py#L395", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223699280, "label": ".db downloads should be served with an ETag"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1739#issuecomment-1116180599", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1739", "id": 1116180599, "node_id": "IC_kwDOBm6k_c5Ch5B3", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-03T14:40:32Z", "updated_at": "2022-05-03T14:40:32Z", "author_association": "OWNER", "body": "Database downloads are served here: https://github.com/simonw/datasette/blob/d60f163528f466b1127b2935c3b6869c34fd6545/datasette/views/database.py#L186-L192\r\n\r\nHere's `AsgiFileDownload`: https://github.com/simonw/datasette/blob/d60f163528f466b1127b2935c3b6869c34fd6545/datasette/utils/asgi.py#L410-L430\r\n\r\nI can add an `etag=` parameter to that and populate it with `db.hash`, if it is populated (which it always should be for immutable databases that can be downloaded).", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223699280, "label": ".db downloads should be served with an ETag"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1739#issuecomment-1116178727", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1739", "id": 1116178727, "node_id": "IC_kwDOBm6k_c5Ch4kn", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-03T14:38:46Z", "updated_at": "2022-05-03T14:38:46Z", "author_association": "OWNER", "body": "Reminded myself how this works by reviewing `conditional-get`: https://github.com/simonw/conditional-get/blob/db6dfec0a296080aaf68fcd80e55fb3f0714e738/conditional_get/cli.py#L33-L52\r\n\r\nSimply add a `If-None-Match: last-known-etag` header to the request and check that the response is a status 304 with an empty body.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223699280, "label": ".db downloads should be served with an ETag"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1739#issuecomment-1115760104", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1739", "id": 1115760104, "node_id": "IC_kwDOBm6k_c5CgSXo", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-03T05:50:19Z", "updated_at": "2022-05-03T05:50:19Z", "author_association": "OWNER", "body": "Here's how Starlette does it: https://github.com/encode/starlette/blob/830f3486537916bae6b46948ff922adc14a22b7c/starlette/staticfiles.py#L213", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223699280, "label": ".db downloads should be served with an ETag"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1732#issuecomment-1115533820", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1732", "id": 1115533820, "node_id": "IC_kwDOBm6k_c5CfbH8", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-03T01:42:25Z", "updated_at": "2022-05-03T01:42:25Z", "author_association": "OWNER", "body": "Thanks, this definitely sounds like a bug. Do you have simple steps to reproduce this?", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1221849746, "label": "Custom page variables aren't decoded"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1737#issuecomment-1115470180", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1737", "id": 1115470180, "node_id": "IC_kwDOBm6k_c5CfLlk", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T23:39:29Z", "updated_at": "2022-05-02T23:39:29Z", "author_association": "OWNER", "body": "Test ran in 38 seconds and passed! https://github.com/simonw/datasette/runs/6265954274?check_suite_focus=true\r\n\r\nI'm going to have it run on every commit and PR.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223459734, "label": "Automated test for Pyodide compatibility"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1737#issuecomment-1115468193", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1737", "id": 1115468193, "node_id": "IC_kwDOBm6k_c5CfLGh", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T23:35:26Z", "updated_at": "2022-05-02T23:35:26Z", "author_association": "OWNER", "body": "https://github.com/simonw/datasette/runs/6265915080?check_suite_focus=true failed but looks like it passed because I forgot to use `set -e` at the start of the bash script.\r\n\r\nIt failed because it didn't have `build` available.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223459734, "label": "Automated test for Pyodide compatibility"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1737#issuecomment-1115464097", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1737", "id": 1115464097, "node_id": "IC_kwDOBm6k_c5CfKGh", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T23:27:40Z", "updated_at": "2022-05-02T23:27:40Z", "author_association": "OWNER", "body": "I'm going to start off by running this manually - I may run it on every commit once this is all a little bit more stable.\r\n\r\nI can base the workflow on https://github.com/simonw/scrape-hacker-news-by-domain/blob/main/.github/workflows/scrape.yml", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223459734, "label": "Automated test for Pyodide compatibility"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1737#issuecomment-1115462720", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1737", "id": 1115462720, "node_id": "IC_kwDOBm6k_c5CfJxA", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T23:25:03Z", "updated_at": "2022-05-02T23:25:03Z", "author_association": "OWNER", "body": "Here's a script that seems to work. It builds the wheel, starts a Python web server that serves the wheel, runs a test with `shot-scraper` and then shuts down the server again.\r\n\r\n```bash\r\n#!/bin/bash\r\n\r\n# Build the wheel\r\npython3 -m build\r\n\r\n# Find name of wheel\r\nwheel=$(basename $(ls dist/*.whl))\r\n# strip off the dist/\r\n\r\n\r\n# Create a blank index page\r\necho '\r\n\r\n' > dist/index.html\r\n\r\n# Run a server for that dist/ folder\r\ncd dist\r\npython3 -m http.server 8529 &\r\ncd ..\r\n\r\nshot-scraper javascript http://localhost:8529/ \"\r\nasync () => {\r\n let pyodide = await loadPyodide();\r\n await pyodide.loadPackage(['micropip', 'ssl', 'setuptools']);\r\n let output = await pyodide.runPythonAsync(\\`\r\n import micropip\r\n await micropip.install('h11==0.12.0')\r\n await micropip.install('http://localhost:8529/$wheel')\r\n import ssl\r\n import setuptools\r\n from datasette.app import Datasette\r\n ds = Datasette(memory=True, settings={'num_sql_threads': 0})\r\n (await ds.client.get('/_memory.json?sql=select+55+as+itworks&_shape=array')).text\r\n \\`);\r\n if (JSON.parse(output)[0].itworks != 55) {\r\n throw 'Got ' + output + ', expected itworks: 55';\r\n }\r\n return 'Test passed!';\r\n}\r\n\"\r\n\r\n# Shut down the server\r\npkill -f 'http.server 8529'\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223459734, "label": "Automated test for Pyodide compatibility"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1733#issuecomment-1115404729", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1733", "id": 1115404729, "node_id": "IC_kwDOBm6k_c5Ce7m5", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T21:49:01Z", "updated_at": "2022-05-02T21:49:38Z", "author_association": "OWNER", "body": "That alpha release works!\r\n\r\nhttps://pyodide.org/en/stable/console.html\r\n\r\n```pycon\r\nWelcome to the Pyodide terminal emulator \ud83d\udc0d\r\nPython 3.10.2 (main, Apr 9 2022 20:52:01) on WebAssembly VM\r\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\r\n>>> import micropip\r\n>>> await micropip.install(\"datasette==0.62a0\")\r\n>>> import ssl\r\n>>> import setuptools\r\n>>> from datasette.app import Datasette\r\n>>> ds = Datasette(memory=True, settings={\"num_sql_threads\": 0})\r\n>>> await ds.client.get(\"/.json\")\r\n