{"html_url": "https://github.com/simonw/datasette/issues/1886#issuecomment-1356842576", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1886", "id": 1356842576, "node_id": "IC_kwDOBm6k_c5Q38ZQ", "user": {"value": 18738650, "label": "stevecrawshaw"}, "created_at": "2022-12-18T17:34:20Z", "updated_at": "2022-12-18T17:34:20Z", "author_association": "NONE", "body": "A bit late to this, but I have made an app to publish air quality data in Bristol, UK. \r\n[air quality data in Bristol, UK.](https://brisaq-wfzqhmj43q-ew.a.run.app/)\r\nNext step to see if I can make a streamlit app based on this to produce some nice charts.", "reactions": "{\"total_count\": 1, \"+1\": 1, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1447050738, "label": "Call for birthday presents: if you're using Datasette, let us know how you're using it here"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/pull/1965#issuecomment-1356827218", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1965", "id": 1356827218, "node_id": "IC_kwDOBm6k_c5Q34pS", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T16:01:36Z", "updated_at": "2022-12-18T16:01:36Z", "author_association": "OWNER", "body": "Will link to this from my TIL shortly.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1501843596, "label": "Detect server start/stop more reliably."}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/pull/1965#issuecomment-1356827167", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1965", "id": 1356827167, "node_id": "IC_kwDOBm6k_c5Q34of", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T16:01:22Z", "updated_at": "2022-12-18T16:01:22Z", "author_association": "OWNER", "body": "This is great, thank you!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1501843596, "label": "Detect server start/stop more reliably."}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1964#issuecomment-1356697705", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1964", "id": 1356697705, "node_id": "IC_kwDOBm6k_c5Q3ZBp", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T06:37:23Z", "updated_at": "2022-12-18T06:37:23Z", "author_association": "OWNER", "body": "I'm certain the two other cog menus (the app menu on the right of the nav bar and the column action menus) have the same problem.\r\n\r\nWould be great to figure out the right ARIA attributes for these too.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1501778647, "label": "Cog menu is not keyboard accessible (also no ARIA)"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1771#issuecomment-1356694671", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1771", "id": 1356694671, "node_id": "IC_kwDOBm6k_c5Q3YSP", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T06:34:20Z", "updated_at": "2022-12-18T06:34:20Z", "author_association": "OWNER", "body": "Now live on https://latest.datasette.io/fixtures/attraction_characteristic", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1306984363, "label": "minor a11y: has no visual indicator when tabbed to"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1771#issuecomment-1356680769", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1771", "id": 1356680769, "node_id": "IC_kwDOBm6k_c5Q3U5B", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T05:56:05Z", "updated_at": "2022-12-18T05:56:05Z", "author_association": "OWNER", "body": "This does the trick:\r\n\r\n```css\r\ndiv.select-wrapper:focus-within {\r\n border: 1px solid black;\r\n}\r\n```\r\n![tab-select-border-fix](https://user-images.githubusercontent.com/9599/208283826-de48212f-a213-40fc-9b37-9d66f0858f21.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": 1306984363, "label": "minor a11y: has no visual indicator when tabbed to"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1771#issuecomment-1356657451", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1771", "id": 1356657451, "node_id": "IC_kwDOBm6k_c5Q3PMr", "user": {"value": 1473102, "label": "mustafa0x"}, "created_at": "2022-12-18T04:04:32Z", "updated_at": "2022-12-18T04:04:32Z", "author_association": "NONE", "body": "the problem is:\r\n```\r\n.select-wrapper select:focus {\r\n outline: none;\r\n}\r\n```\r\n\r\nI sometimes add this js:\r\n```\r\nwindow.addEventListener('keydown', function check_tab(e) {\r\n if (e.key === 'Tab') {\r\n document.documentElement.classList.add('user-is-tabbing')\r\n window.removeEventListener('keydown', check_tab)\r\n }\r\n})\r\n```\r\n\r\nand then in the css, using a `html.user-is-tabbing` selector undo any outlines I removed.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1306984363, "label": "minor a11y: has no visual indicator when tabbed to"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1771#issuecomment-1356655630", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1771", "id": 1356655630, "node_id": "IC_kwDOBm6k_c5Q3OwO", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T03:43:12Z", "updated_at": "2022-12-18T03:43:12Z", "author_association": "OWNER", "body": "The border is actually on the div that wraps the select box:\r\n\r\n \r\n\r\nI tried adding a `border: 1px dotted black` to `select:focus` but it's not quite right - it jumps around a bit like this:\r\n\r\n![Tabbing to the selects shows a 1px border but the element expands in size by one pixel, causing a visual jump](https://user-images.githubusercontent.com/9599/208280271-41a07f68-b8b1-4908-a4e2-aac4304d6c09.gif)\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": 1306984363, "label": "minor a11y: has no visual indicator when tabbed to"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1771#issuecomment-1356655217", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1771", "id": 1356655217, "node_id": "IC_kwDOBm6k_c5Q3Opx", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T03:38:16Z", "updated_at": "2022-12-18T03:38:16Z", "author_association": "OWNER", "body": "OK I see what you mean:\r\n\r\nhttps://latest.datasette.io/fixtures/attraction_characteristic\r\n\r\n![Animated GIF of the table page hitting tab a bunch - the cog icon highlights and so does the text input but the two select boxes do not](https://user-images.githubusercontent.com/9599/208280176-1e2de671-fe69-43e8-8d62-bf7aa8f4d36e.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": 1306984363, "label": "minor a11y: has no visual indicator when tabbed to"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1963#issuecomment-1356652057", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1963", "id": 1356652057, "node_id": "IC_kwDOBm6k_c5Q3N4Z", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T03:23:22Z", "updated_at": "2022-12-18T03:23:22Z", "author_association": "OWNER", "body": "https://pypi.org/project/datasette/0.63.3/ is released.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1501713288, "label": "0.63.3 bugfix release"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1963#issuecomment-1356651943", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1963", "id": 1356651943, "node_id": "IC_kwDOBm6k_c5Q3N2n", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T03:23:03Z", "updated_at": "2022-12-18T03:23:03Z", "author_association": "OWNER", "body": "Oh that's annoying... every step in publish succeeded except the static docs one:\r\n\r\n \r\n\r\nhttps://github.com/simonw/datasette/actions/runs/3723015082/jobs/6314292722\r\n\r\nThis means the documentation database used to update the search engine on https://datasette.io/ won't reflect the very latest changelog. I'm OK with that - I'll fix this workflow so that next time I publish a release this will work correctly.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1501713288, "label": "0.63.3 bugfix release"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356640463", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356640463, "node_id": "IC_kwDOBm6k_c5Q3LDP", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:45:18Z", "updated_at": "2022-12-18T02:45:18Z", "author_association": "OWNER", "body": "... and with this change, the following now works correctly:\r\n\r\n```\r\n% datasette install datasette-gunicorn\r\n% datasette gunicorn fixtures.db -p 8855\r\n[2022-12-17 18:44:29 -0800] [7651] [INFO] Starting gunicorn 20.1.0\r\n[2022-12-17 18:44:29 -0800] [7651] [INFO] Listening at: http://127.0.0.1:8855 (7651)\r\n[2022-12-17 18:44:29 -0800] [7651] [INFO] Using worker: uvicorn.workers.UvicornWorker\r\n[2022-12-17 18:44:29 -0800] [7653] [INFO] Booting worker with pid: 7653\r\n[2022-12-17 18:44:29 -0800] [7653] [INFO] Started server process [7653]\r\n[2022-12-17 18:44:29 -0800] [7653] [INFO] Waiting for application startup.\r\n[2022-12-17 18:44:29 -0800] [7653] [INFO] Application startup complete.\r\n```\r\nSo this issue is now fixed!", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356640266", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356640266, "node_id": "IC_kwDOBm6k_c5Q3LAK", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:43:00Z", "updated_at": "2022-12-18T02:43:00Z", "author_association": "OWNER", "body": "https://github.com/simonw/datasette/actions/runs/3722908296/jobs/6314093163 shows that new test passing in CI:\r\n\r\n```\r\nGenerated a certificate for 'localhost', '127.0.0.1', '::1'\r\nConfigure your server to use the following files:\r\n cert=/home/runner/work/datasette/datasette/server.pem\r\n key=/home/runner/work/datasette/datasette/server.key\r\nConfigure your client to use the following files:\r\n cert=/home/runner/work/datasette/datasette/client.pem\r\nINFO: Started server process [4036]\r\nINFO: Waiting for application startup.\r\nINFO: Application startup complete.\r\nINFO: Uvicorn running on https://127.0.0.1:8152/ (Press CTRL+C to quit)\r\n % Total % Received % Xferd Average Speed Time Time Time Current\r\n Dload Upload Total Spent Left Speed\r\n\r\nINFO: 127.0.0.1:56726 - \"GET /_memory.json HTTP/1.1\" 200 OK\r\n 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0\r\n100 213 0 213 0 0 11542 0 --:--:-- --:--:-- --:--:-- 11833\r\nINFO: Shutting down\r\nINFO: Waiting for application shutdown.\r\nINFO: Application shutdown complete.\r\nINFO: Finished server process [4036]\r\n{\"database\": \"_memory\", \"private\": false, \"path\": \"/_memory\", \"size\": 0, \"tables\": [], \"hidden_count\": 0, \"views\": [], \"queries\": [], \"allow_execute_sql\": true, \"table_columns\": {}, \"query_ms\": 1.4545189999921604}0\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1221#issuecomment-1356639873", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1221", "id": 1356639873, "node_id": "IC_kwDOBm6k_c5Q3K6B", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:39:04Z", "updated_at": "2022-12-18T02:39:04Z", "author_association": "OWNER", "body": "I ended up moving this test out of Python and into a `bash` script here: https://github.com/simonw/datasette/commit/d1d369456a7319b9de39175605568cbc9b852478", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 806849424, "label": "Support SSL/TLS directly"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356633937", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356633937, "node_id": "IC_kwDOBm6k_c5Q3JdR", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:28:22Z", "updated_at": "2022-12-18T02:28:22Z", "author_association": "OWNER", "body": "This `bash` script does the job:\r\n```bash\r\n#!/bin/bash\r\n\r\n# Generate certificates\r\npython -m trustme\r\n# This creates server.pem, server.key, client.pem\r\n\r\n# Start the server in the background\r\ndatasette --memory \\\r\n --ssl-keyfile=server.key \\\r\n --ssl-certfile=server.pem \\\r\n -p 8152 &\r\n\r\n# Store the background process ID in a variable\r\nserver_pid=$!\r\n\r\n# Wait for the server to start\r\nsleep 2\r\n\r\n# Make a test request using curl\r\ncurl -f --cacert client.pem 'https://localhost:8152/_memory.json'\r\n\r\n# Save curl's exit code (-f option causes it to return one on HTTP errors)\r\ncurl_exit_code=$?\r\n\r\n# Shut down the server\r\nkill $server_pid\r\nsleep 1\r\n\r\n# Clean up the certificates\r\nrm server.pem server.key client.pem\r\n\r\necho $curl_exit_code\r\nexit $curl_exit_code\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356630092", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356630092, "node_id": "IC_kwDOBm6k_c5Q3IhM", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:20:01Z", "updated_at": "2022-12-18T02:20:01Z", "author_association": "OWNER", "body": "Asked ChatGPT:\r\n\r\n> Write a bash script which starts a server in the background using \"datasette -p 8002\", then uses curl to make a test request against it, then shuts the server down again at the end\r\n\r\nIt gave me:\r\n\r\n```bash\r\n#!/bin/bash\r\n\r\n# Start the server in the background\r\ndatasette -p 8002 &\r\n\r\n# Store the background process ID in a variable\r\nserver_pid=$!\r\n\r\n# Make a test request using curl\r\ncurl http://localhost:8002\r\n\r\n# Shut down the server\r\nkill $server_pid\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356629783", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356629783, "node_id": "IC_kwDOBm6k_c5Q3IcX", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:18:43Z", "updated_at": "2022-12-18T02:18:43Z", "author_association": "OWNER", "body": "Various attempts at a fix which didn't work:\r\n\r\n```diff\r\ndiff --git a/tests/conftest.py b/tests/conftest.py\r\nindex 69dee68b..899d36fd 100644\r\n--- a/tests/conftest.py\r\n+++ b/tests/conftest.py\r\n@@ -1,4 +1,3 @@\r\n-import asyncio\r\n import httpx\r\n import os\r\n import pathlib\r\n@@ -6,6 +5,7 @@ import pytest\r\n import pytest_asyncio\r\n import re\r\n import subprocess\r\n+import sys\r\n import tempfile\r\n import time\r\n import trustme\r\n@@ -27,13 +27,23 @@ UNDOCUMENTED_PERMISSIONS = {\r\n _ds_client = None\r\n \r\n \r\n-def wait_until_responds(url, timeout=5.0, client=httpx, **kwargs):\r\n+def wait_until_responds(url, timeout=5.0, client=None, **kwargs):\r\n+ client = client or httpx.Client(**kwargs)\r\n start = time.time()\r\n while time.time() - start < timeout:\r\n try:\r\n- client.get(url, **kwargs)\r\n+ if \"verify\" in kwargs:\r\n+ print(kwargs[\"verify\"])\r\n+ print(\r\n+ \"Contents of verify file: {}\".format(\r\n+ open(kwargs.get(\"verify\")).read()\r\n+ )\r\n+ )\r\n+ print(\"client = {}, kwargs = {}\".format(client, kwargs))\r\n+ client.get(url)\r\n return\r\n- except httpx.ConnectError:\r\n+ except (httpx.ConnectError, httpx.RemoteProtocolError) as ex:\r\n+ print(ex)\r\n time.sleep(0.1)\r\n raise AssertionError(\"Timed out waiting for {} to respond\".format(url))\r\n \r\n@@ -166,7 +176,7 @@ def check_permission_actions_are_documented():\r\n @pytest.fixture(scope=\"session\")\r\n def ds_localhost_http_server():\r\n ds_proc = subprocess.Popen(\r\n- [\"datasette\", \"--memory\", \"-p\", \"8041\"],\r\n+ [sys.executable, \"-m\", \"datasette\", \"--memory\", \"-p\", \"8041\"],\r\n stdout=subprocess.PIPE,\r\n stderr=subprocess.STDOUT,\r\n # Avoid FileNotFoundError: [Errno 2] No such file or directory:\r\n@@ -180,7 +190,7 @@ def ds_localhost_http_server():\r\n ds_proc.terminate()\r\n \r\n \r\n-@pytest.fixture(scope=\"session\")\r\n+@pytest.fixture\r\n def ds_localhost_https_server(tmp_path_factory):\r\n cert_directory = tmp_path_factory.mktemp(\"certs\")\r\n ca = trustme.CA()\r\n@@ -194,6 +204,8 @@ def ds_localhost_https_server(tmp_path_factory):\r\n ca.cert_pem.write_to_path(path=client_cert)\r\n ds_proc = subprocess.Popen(\r\n [\r\n+ sys.executable,\r\n+ \"-m\",\r\n \"datasette\",\r\n \"--memory\",\r\n \"-p\",\r\n@@ -207,7 +219,11 @@ def ds_localhost_https_server(tmp_path_factory):\r\n stderr=subprocess.STDOUT,\r\n cwd=tempfile.gettempdir(),\r\n )\r\n- wait_until_responds(\"http://localhost:8042/\", verify=client_cert)\r\n+ wait_until_responds(\r\n+ \"http://localhost:8042/_memory.json\",\r\n+ verify=client_cert,\r\n+ headers={\"Connection\": \"close\"},\r\n+ )\r\n # Check it started successfully\r\n assert not ds_proc.poll(), ds_proc.stdout.read().decode(\"utf-8\")\r\n yield ds_proc, client_cert\r\ndiff --git a/tests/test_cli_serve_server.py b/tests/test_cli_serve_server.py\r\nindex 1c31e2a3..9320b623 100644\r\n--- a/tests/test_cli_serve_server.py\r\n+++ b/tests/test_cli_serve_server.py\r\n@@ -16,7 +16,11 @@ def test_serve_localhost_http(ds_localhost_http_server):\r\n @pytest.mark.serial\r\n def test_serve_localhost_https(ds_localhost_https_server):\r\n _, client_cert = ds_localhost_https_server\r\n- response = httpx.get(\"https://localhost:8042/_memory.json\", verify=client_cert)\r\n+ response = httpx.get(\r\n+ \"https://localhost:8042/_memory.json\",\r\n+ verify=client_cert,\r\n+ headers={\"Connection\": \"close\"},\r\n+ )\r\n assert {\r\n \"database\": \"_memory\",\r\n \"path\": \"/_memory\",\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356627931", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356627931, "node_id": "IC_kwDOBm6k_c5Q3H_b", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:13:01Z", "updated_at": "2022-12-18T02:13:01Z", "author_association": "OWNER", "body": "Rather than continue to bang my head against this, I'm tempted to rewrite this test to happen outside of Python world - in a bash script run by GitHub Actions, for example.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356627331", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356627331, "node_id": "IC_kwDOBm6k_c5Q3H2D", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:11:17Z", "updated_at": "2022-12-18T02:11:17Z", "author_association": "OWNER", "body": "This issue might be relevant, but I tried the suggested fix in there (`Connection: close` on the incoming requests) and it didn't fix my problem:\r\n- https://github.com/encode/httpx/discussions/2056", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356626334", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356626334, "node_id": "IC_kwDOBm6k_c5Q3Hme", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:04:01Z", "updated_at": "2022-12-18T02:04:07Z", "author_association": "OWNER", "body": "I used the steps to test manually from this comment: https://github.com/simonw/datasette/issues/1221#issuecomment-777901052\r\n\r\nIn one terminal:\r\n```\r\ncd /tmp\r\npython -m trustme\r\ndatasette --memory --ssl-keyfile=/tmp/server.key --ssl-certfile=/tmp/server.pem -p 8003\r\n```\r\nThen in another terminal:\r\n```\r\ncurl --cacert /tmp/client.pem 'https://localhost:8003/_memory.json'\r\n```\r\nThis worked correctly, outputting the expected JSON.\r\n\r\nSo the feature still works, it's just the test that is broken for some reason.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356625642", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356625642, "node_id": "IC_kwDOBm6k_c5Q3Hbq", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:00:57Z", "updated_at": "2022-12-18T02:00:57Z", "author_association": "OWNER", "body": "I added the TLS support here:\r\n- https://github.com/simonw/datasette/issues/1221", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356625556", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356625556, "node_id": "IC_kwDOBm6k_c5Q3HaU", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T02:00:18Z", "updated_at": "2022-12-18T02:00:18Z", "author_association": "OWNER", "body": "Maybe the reason the ASGI lifespan stuff broke was this line: https://github.com/simonw/datasette/blob/8b73fc6b47dffd8836f5c58aae1e57c1f66a5754/datasette/cli.py#L630-L632", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356620233", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356620233, "node_id": "IC_kwDOBm6k_c5Q3GHJ", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T01:31:10Z", "updated_at": "2022-12-18T01:31:10Z", "author_association": "OWNER", "body": "During the polling loop it constantly raises:\r\n\r\n`httpx.RemoteProtocolError`: Server disconnected without sending a response", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356618913", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356618913, "node_id": "IC_kwDOBm6k_c5Q3Fyh", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T01:29:05Z", "updated_at": "2022-12-18T01:29:05Z", "author_association": "OWNER", "body": "Now the only failure is in the `https` test - which fails like this (in CI and on my laptop):\r\n\r\n```\r\n message = str(exc)\r\n> raise mapped_exc(message) from exc\r\nE httpx.RemoteProtocolError: Server disconnected without sending a response.\r\n\r\n/opt/hostedtoolcache/Python/3.11.1/x64/lib/python3.11/site-packages/httpx/_transports/default.py:77: RemoteProtocolError\r\n=========================== short test summary info ============================\r\nERROR tests/test_cli_serve_server.py::test_serve_localhost_https - httpx.RemoteProtocolError: Server disconnected without sending a response.\r\n================= 30 passed, 1264 deselected, 1 error in 6.15s =================\r\n```\r\nThat's this test: https://github.com/simonw/datasette/blob/63fb750f39cac6f49b451387fdff659ecd9edc5c/tests/test_cli_serve_server.py#L16-L24\r\n\r\nAnd this fixture: https://github.com/simonw/datasette/blob/63fb750f39cac6f49b451387fdff659ecd9edc5c/tests/conftest.py#L178-L215\r\n", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356610089", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356610089, "node_id": "IC_kwDOBm6k_c5Q3Dop", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T01:12:39Z", "updated_at": "2022-12-18T01:12:39Z", "author_association": "OWNER", "body": "... and it turns out those tests saved me. Because I forgot to check if `datasette` would actually start a server correctly!\r\n\r\n```\r\n % datasette fixtures.db -p 8852\r\nINFO: Started server process [3538]\r\nINFO: Waiting for application startup.\r\nERROR: Exception in 'lifespan' protocol\r\nTraceback (most recent call last):\r\n File \"/Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/lib/python3.10/site-packages/uvicorn/lifespan/on.py\", line 86, in main\r\n await app(scope, self.receive, self.send)\r\n File \"/Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py\", line 78, in __call__\r\n return await self.app(scope, receive, send)\r\n File \"/Users/simon/Dropbox/Development/datasette/datasette/utils/asgi.py\", line 437, in __call__\r\n return await self.asgi(scope, receive, send)\r\n File \"/Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/lib/python3.10/site-packages/asgi_csrf.py\", line 39, in app_wrapped_with_csrf\r\n await app(scope, receive, send)\r\n File \"/Users/simon/Dropbox/Development/datasette/datasette/app.py\", line 1457, in __call__\r\n path = scope[\"path\"]\r\nKeyError: 'path'\r\nERROR: Application startup failed. Exiting.\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356609095", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356609095, "node_id": "IC_kwDOBm6k_c5Q3DZH", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T01:10:43Z", "updated_at": "2022-12-18T01:10:43Z", "author_association": "OWNER", "body": "Improved version of that fixture:\r\n```diff\r\ndiff --git a/tests/conftest.py b/tests/conftest.py\r\nindex 44c44f87..69dee68b 100644\r\n--- a/tests/conftest.py\r\n+++ b/tests/conftest.py\r\n@@ -27,6 +27,17 @@ UNDOCUMENTED_PERMISSIONS = {\r\n _ds_client = None\r\n \r\n \r\n+def wait_until_responds(url, timeout=5.0, client=httpx, **kwargs):\r\n+ start = time.time()\r\n+ while time.time() - start < timeout:\r\n+ try:\r\n+ client.get(url, **kwargs)\r\n+ return\r\n+ except httpx.ConnectError:\r\n+ time.sleep(0.1)\r\n+ raise AssertionError(\"Timed out waiting for {} to respond\".format(url))\r\n+\r\n+\r\n @pytest_asyncio.fixture\r\n async def ds_client():\r\n from datasette.app import Datasette\r\n@@ -161,13 +172,7 @@ def ds_localhost_http_server():\r\n # Avoid FileNotFoundError: [Errno 2] No such file or directory:\r\n cwd=tempfile.gettempdir(),\r\n )\r\n- # Loop until port 8041 serves traffic\r\n- while True:\r\n- try:\r\n- httpx.get(\"http://localhost:8041/\")\r\n- break\r\n- except httpx.ConnectError:\r\n- time.sleep(0.1)\r\n+ wait_until_responds(\"http://localhost:8041/\")\r\n # Check it started successfully\r\n assert not ds_proc.poll(), ds_proc.stdout.read().decode(\"utf-8\")\r\n yield ds_proc\r\n@@ -202,12 +207,7 @@ def ds_localhost_https_server(tmp_path_factory):\r\n stderr=subprocess.STDOUT,\r\n cwd=tempfile.gettempdir(),\r\n )\r\n- while True:\r\n- try:\r\n- httpx.get(\"https://localhost:8042/\", verify=client_cert)\r\n- break\r\n- except httpx.ConnectError:\r\n- time.sleep(0.1)\r\n+ wait_until_responds(\"http://localhost:8042/\", verify=client_cert)\r\n # Check it started successfully\r\n assert not ds_proc.poll(), ds_proc.stdout.read().decode(\"utf-8\")\r\n yield ds_proc, client_cert\r\n@@ -231,12 +231,7 @@ def ds_unix_domain_socket_server(tmp_path_factory):\r\n # Poll until available\r\n transport = httpx.HTTPTransport(uds=uds)\r\n client = httpx.Client(transport=transport)\r\n- while True:\r\n- try:\r\n- client.get(\"http://localhost/_memory.json\")\r\n- break\r\n- except httpx.ConnectError:\r\n- time.sleep(0.1)\r\n+ wait_until_responds(\"http://localhost/_memory.json\", client=client)\r\n # Check it started successfully\r\n assert not ds_proc.poll(), ds_proc.stdout.read().decode(\"utf-8\")\r\n yield ds_proc, uds\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356600917", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356600917, "node_id": "IC_kwDOBm6k_c5Q3BZV", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T01:02:26Z", "updated_at": "2022-12-18T01:02:26Z", "author_association": "OWNER", "body": "This bit here looks like it could hang!\r\n```python\r\n # Loop until port 8041 serves traffic \r\n while True: \r\n try: \r\n httpx.get(\"http://localhost:8041/\") \r\n break \r\n except httpx.ConnectError: \r\n time.sleep(0.1) \r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356599930", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356599930, "node_id": "IC_kwDOBm6k_c5Q3BJ6", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T01:01:47Z", "updated_at": "2022-12-18T01:01:47Z", "author_association": "OWNER", "body": "I think that's this test: https://github.com/simonw/datasette/blob/63fb750f39cac6f49b451387fdff659ecd9edc5c/tests/test_cli_serve_server.py#L6-L13\r\n\r\nUsing this fixture: https://github.com/simonw/datasette/blob/63fb750f39cac6f49b451387fdff659ecd9edc5c/tests/conftest.py#L155-L175", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356596740", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356596740, "node_id": "IC_kwDOBm6k_c5Q3AYE", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T00:59:47Z", "updated_at": "2022-12-18T00:59:47Z", "author_association": "OWNER", "body": "Hitting `Ctrl+C` while using `--full-trace` gave me more clues:\r\n\r\n```\r\n% pytest -m serial tests/test_cli_serve_server.py --full-trace\r\n======================================================= test session starts ========================================================\r\nplatform darwin -- Python 3.10.3, pytest-7.1.3, pluggy-1.0.0\r\nSQLite: 3.39.4\r\nrootdir: /Users/simon/Dropbox/Development/datasette, configfile: pytest.ini\r\nplugins: anyio-3.6.1, xdist-2.5.0, forked-1.4.0, asyncio-0.19.0, timeout-2.1.0, profiling-1.7.0\r\nasyncio: mode=strict\r\ncollected 3 items \r\n\r\ntests/test_cli_serve_server.py ^C^C\r\n\r\n====================================================== no tests ran in 3.49s =======================================================\r\nTraceback (most recent call last):\r\n File \"/Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/lib/python3.10/site-packages/httpcore/_exceptions.py\", line 8, in map_exceptions\r\n yield\r\n File \"/Users/simon/.local/share/virtualenvs/datasette-AWNrQs95/lib/python3.10/site-packages/httpcore/backends/sync.py\", line 86, in connect_tcp\r\n sock = socket.create_connection(\r\n File \"/Users/simon/.pyenv/versions/3.10.3/lib/python3.10/socket.py\", line 845, in create_connection\r\n raise err\r\n File \"/Users/simon/.pyenv/versions/3.10.3/lib/python3.10/socket.py\", line 833, in create_connection\r\n sock.connect(sa)\r\nConnectionRefusedError: [Errno 61] Connection refused\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": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}
{"html_url": "https://github.com/simonw/datasette/issues/1955#issuecomment-1356595665", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1955", "id": 1356595665, "node_id": "IC_kwDOBm6k_c5Q3AHR", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-12-18T00:58:16Z", "updated_at": "2022-12-18T00:58:16Z", "author_association": "OWNER", "body": "`pytest -m serial` on my Mac laptop also freezes:\r\n\r\n```\r\n(datasette) datasette % pytest -m serial\r\n======================================================= test session starts ========================================================\r\nplatform darwin -- Python 3.10.3, pytest-7.1.3, pluggy-1.0.0\r\nSQLite: 3.39.4\r\nrootdir: /Users/simon/Dropbox/Development/datasette, configfile: pytest.ini\r\nplugins: anyio-3.6.1, xdist-2.5.0, forked-1.4.0, asyncio-0.19.0, timeout-2.1.0, profiling-1.7.0\r\nasyncio: mode=strict\r\ncollected 1295 items / 1264 deselected / 31 selected \r\n\r\ntests/test_package.py . [ 3%]\r\ntests/test_cli_serve_server.py \r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1496652622, "label": "invoke_startup() is not run in some conditions, e.g. gunicorn/uvicorn workers, breaking lots of things"}, "performed_via_github_app": null}