{"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\r\n>>> (await ds.client.get(\"/.json\")).json()\r\n{'_memory': {'name': '_memory', 'hash': None, 'color': 'a6c7b9', 'path': '/_memory', 'tables_and_views_truncated': [], 'tab\r\nles_and_views_more': False, 'tables_count': 0, 'table_rows_sum': 0, 'show_table_row_counts': False, 'hidden_table_rows_sum'\r\n: 0, 'hidden_tables_count': 0, 'views_count': 0, 'private': False}}\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": 1223234932, "label": "Get Datasette compatible with Pyodide"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1733#issuecomment-1115318417", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1733", "id": 1115318417, "node_id": "IC_kwDOBm6k_c5CemiR", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T20:13:43Z", "updated_at": "2022-05-02T20:13:43Z", "author_association": "OWNER", "body": "This is good enough to push an alpha.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223234932, "label": "Get Datasette compatible with Pyodide"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1733#issuecomment-1115318303", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1733", "id": 1115318303, "node_id": "IC_kwDOBm6k_c5Cemgf", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T20:13:36Z", "updated_at": "2022-05-02T20:13:36Z", "author_association": "OWNER", "body": "I got a build from the `pyodide` branch to work!\r\n\r\n```\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(\"https://s3.amazonaws.com/simonwillison-cors-allowed-public/datasette-0.62a0-py3-none-any.whl\")\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"/lib/python3.10/asyncio/futures.py\", line 284, in __await__\r\n yield self # This tells Task to wait for completion.\r\n File \"/lib/python3.10/asyncio/tasks.py\", line 304, in __wakeup\r\n future.result()\r\n File \"/lib/python3.10/asyncio/futures.py\", line 201, in result\r\n raise self._exception\r\n File \"/lib/python3.10/asyncio/tasks.py\", line 234, in __step\r\n result = coro.throw(exc)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 183, in install\r\n transaction = await self.gather_requirements(requirements, ctx, keep_going)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 173, in gather_requirements\r\n await gather(*requirement_promises)\r\n File \"/lib/python3.10/asyncio/futures.py\", line 284, in __await__\r\n yield self # This tells Task to wait for completion.\r\n File \"/lib/python3.10/asyncio/tasks.py\", line 304, in __wakeup\r\n future.result()\r\n File \"/lib/python3.10/asyncio/futures.py\", line 201, in result\r\n raise self._exception\r\n File \"/lib/python3.10/asyncio/tasks.py\", line 232, in __step\r\n result = coro.send(None)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 245, in add_requirement\r\n await self.add_wheel(name, wheel, version, (), ctx, transaction)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 316, in add_wheel\r\n await self.add_requirement(recurs_req, ctx, transaction)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 291, in add_requirement\r\n await self.add_wheel(\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 316, in add_wheel\r\n await self.add_requirement(recurs_req, ctx, transaction)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 291, in add_requirement\r\n await self.add_wheel(\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 316, in add_wheel\r\n await self.add_requirement(recurs_req, ctx, transaction)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 276, in add_requirement\r\n raise ValueError(\r\nValueError: Requested 'h11<0.13,>=0.11', but h11==0.13.0 is already installed\r\n>>> await micropip.install(\"https://s3.amazonaws.com/simonwillison-cors-allowed-public/datasette-0.62a0-py3-none-any.whl\")\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"/lib/python3.10/asyncio/futures.py\", line 284, in __await__\r\n yield self # This tells Task to wait for completion.\r\n File \"/lib/python3.10/asyncio/tasks.py\", line 304, in __wakeup\r\n future.result()\r\n File \"/lib/python3.10/asyncio/futures.py\", line 201, in result\r\n raise self._exception\r\n File \"/lib/python3.10/asyncio/tasks.py\", line 234, in __step\r\n result = coro.throw(exc)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 183, in install\r\n transaction = await self.gather_requirements(requirements, ctx, keep_going)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 173, in gather_requirements\r\n await gather(*requirement_promises)\r\n File \"/lib/python3.10/asyncio/futures.py\", line 284, in __await__\r\n yield self # This tells Task to wait for completion.\r\n File \"/lib/python3.10/asyncio/tasks.py\", line 304, in __wakeup\r\n future.result()\r\n File \"/lib/python3.10/asyncio/futures.py\", line 201, in result\r\n raise self._exception\r\n File \"/lib/python3.10/asyncio/tasks.py\", line 232, in __step\r\n result = coro.send(None)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 245, in add_requirement\r\n await self.add_wheel(name, wheel, version, (), ctx, transaction)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 316, in add_wheel\r\n await self.add_requirement(recurs_req, ctx, transaction)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 291, in add_requirement\r\n await self.add_wheel(\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 316, in add_wheel\r\n await self.add_requirement(recurs_req, ctx, transaction)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 291, in add_requirement\r\n await self.add_wheel(\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 316, in add_wheel\r\n await self.add_requirement(recurs_req, ctx, transaction)\r\n File \"/lib/python3.10/site-packages/micropip/_micropip.py\", line 276, in add_requirement\r\n raise ValueError(\r\nValueError: Requested 'h11<0.13,>=0.11', but h11==0.13.0 is already installed\r\n>>> await micropip.install(\"h11==0.12\")\r\n>>> await micropip.install(\"https://s3.amazonaws.com/simonwillison-cors-allowed-public/datasette-0.62a0-py3-none-any.whl\")\r\n>>> import datasette\r\n>>> from datasette.app import Datasette\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"/lib/python3.10/site-packages/datasette/app.py\", line 9, in \r\n import httpx\r\n File \"/lib/python3.10/site-packages/httpx/__init__.py\", line 2, in \r\n from ._api import delete, get, head, options, patch, post, put, request, stream\r\n File \"/lib/python3.10/site-packages/httpx/_api.py\", line 4, in \r\n from ._client import Client\r\n File \"/lib/python3.10/site-packages/httpx/_client.py\", line 9, in \r\n from ._auth import Auth, BasicAuth, FunctionAuth\r\n File \"/lib/python3.10/site-packages/httpx/_auth.py\", line 10, in \r\n from ._models import Request, Response\r\n File \"/lib/python3.10/site-packages/httpx/_models.py\", line 16, in \r\n from ._content import ByteStream, UnattachedStream, encode_request, encode_response\r\n File \"/lib/python3.10/site-packages/httpx/_content.py\", line 17, in \r\n from ._multipart import MultipartStream\r\n File \"/lib/python3.10/site-packages/httpx/_multipart.py\", line 7, in \r\n from ._types import (\r\n File \"/lib/python3.10/site-packages/httpx/_types.py\", line 5, in \r\n import ssl\r\n File \"/lib/python3.10/ssl.py\", line 98, in \r\n import _ssl # if we can't import it, let the error propagate\r\nModuleNotFoundError: No module named '_ssl'\r\n>>> import ssl\r\n>>> from datasette.app import Datasette\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"/lib/python3.10/site-packages/datasette/app.py\", line 14, in \r\n import pkg_resources\r\nModuleNotFoundError: No module named 'pkg_resources'\r\n>>> import setuptools\r\n>>> from datasette.app import Datasette\r\n>>> ds = Datasette(memory=True)\r\n>>> ds\r\n\r\n>>> await ds.client.get(\"/\")\r\nTraceback (most recent call last):\r\n File \"/lib/python3.10/site-packages/datasette/app.py\", line 1268, in route_path\r\n response = await view(request, send)\r\n File \"/lib/python3.10/site-packages/datasette/views/base.py\", line 134, in view\r\n return await self.dispatch_request(request)\r\n File \"/lib/python3.10/site-packages/datasette/views/base.py\", line 89, in dispatch_request\r\n await self.ds.refresh_schemas()\r\n File \"/lib/python3.10/site-packages/datasette/app.py\", line 353, in refresh_schemas\r\n await self._refresh_schemas()\r\n File \"/lib/python3.10/site-packages/datasette/app.py\", line 358, in _refresh_schemas\r\n await init_internal_db(internal_db)\r\n File \"/lib/python3.10/site-packages/datasette/utils/internal_db.py\", line 65, in init_internal_db\r\n await db.execute_write_script(create_tables_sql)\r\n File \"/lib/python3.10/site-packages/datasette/database.py\", line 116, in execute_write_script\r\n results = await self.execute_write_fn(_inner, block=block)\r\n File \"/lib/python3.10/site-packages/datasette/database.py\", line 155, in execute_write_fn\r\n self._write_thread.start()\r\n File \"/lib/python3.10/threading.py\", line 928, in start\r\n _start_new_thread(self._bootstrap, ())\r\nRuntimeError: can't start new thread\r\n\r\n>>> ds = Datasette(memory=True, settings={\"num_sql_threads\": 0})\r\n>>> await ds.client.get(\"/\")\r\n\r\n>>> (await ds.client.get(\"/\")).text\r\n'\\n\\n\\n Datasette: _memory\\n \\n \\n\\n\\n\\n
\r\n\\n
\\n\\n\\n\\n \\n\\n\\n\\n
\\n\\n

Datasette

\\n\\n\\n\\n\\n\\n\r\n

\r\nr detailsClickedWithin = null;\\n while (target && target.tagName != \\'DETAILS\\') {\\n target = target.parentNode;\\\r\nn }\\n if (target && target.tagName == \\'DETAILS\\') {\\n detailsClickedWithin = target;\\n }\\n Array.from(d\r\nocument.getElementsByTagName(\\'details\\')).filter(\\n (details) => details.open && details != detailsClickedWithin\\n \r\n ).forEach(details => details.open = false);\\n});\\n\\n\\n\\n\\n\\n\\n\r\n'\r\n>>> \r\n```\r\n\r\nThat `ValueError: Requested 'h11<0.13,>=0.11', but h11==0.13.0 is already installed` error is annoying. I assume it's a `uvicorn` dependency clash of some sort, because I wasn't getting that when I removed `uvicorn` as a dependency.\r\n\r\nI can avoid it by running this first though:\r\n\r\n await micropip.install(\"h11==0.12\")", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223234932, "label": "Get Datasette compatible with Pyodide"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1733#issuecomment-1115288284", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1733", "id": 1115288284, "node_id": "IC_kwDOBm6k_c5CefLc", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T19:40:33Z", "updated_at": "2022-05-02T19:40:33Z", "author_association": "OWNER", "body": "I'll release this as a `0.62a0` as soon as it's ready, so I can start testing it out in Pyodide for real.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223234932, "label": "Get Datasette compatible with Pyodide"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1733#issuecomment-1115278325", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1733", "id": 1115278325, "node_id": "IC_kwDOBm6k_c5Cecv1", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T19:29:05Z", "updated_at": "2022-05-02T19:29:05Z", "author_association": "OWNER", "body": "I'm going to add a Datasette setting to disable threading entirely, designed for usage in this particular case.\r\n\r\nI thought about adding a new setting, then I noticed this:\r\n\r\n datasette mydatabase.db --setting num_sql_threads 10\r\n\r\nI'm going to let users set that to `0` to disable threaded execution of SQL queries.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223234932, "label": "Get Datasette compatible with Pyodide"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1733#issuecomment-1115268245", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1733", "id": 1115268245, "node_id": "IC_kwDOBm6k_c5CeaSV", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T19:18:11Z", "updated_at": "2022-05-02T19:18:11Z", "author_association": "OWNER", "body": "Maybe I can leave `uvicorn` as a dependency? Installing it works OK, it only generates errors when you try to import it:\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(\"uvicorn\")\r\n>>> import uvicorn\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"/lib/python3.10/site-packages/uvicorn/__init__.py\", line 1, in \r\n from uvicorn.config import Config\r\n File \"/lib/python3.10/site-packages/uvicorn/config.py\", line 8, in \r\n import ssl\r\n File \"/lib/python3.10/ssl.py\", line 98, in \r\n import _ssl # if we can't import it, let the error propagate\r\nModuleNotFoundError: No module named '_ssl'\r\n>>> import ssl\r\n>>> import uvicorn\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"/lib/python3.10/site-packages/uvicorn/__init__.py\", line 2, in \r\n from uvicorn.main import Server, main, run\r\n File \"/lib/python3.10/site-packages/uvicorn/main.py\", line 24, in \r\n from uvicorn.supervisors import ChangeReload, Multiprocess\r\n File \"/lib/python3.10/site-packages/uvicorn/supervisors/__init__.py\", line 3, in \r\n from uvicorn.supervisors.basereload import BaseReload\r\n File \"/lib/python3.10/site-packages/uvicorn/supervisors/basereload.py\", line 12, in \r\n from uvicorn.subprocess import get_subprocess\r\n File \"/lib/python3.10/site-packages/uvicorn/subprocess.py\", line 14, in \r\n multiprocessing.allow_connection_pickling()\r\n File \"/lib/python3.10/multiprocessing/context.py\", line 170, in allow_connection_pickling\r\n from . import connection\r\n File \"/lib/python3.10/multiprocessing/connection.py\", line 21, in \r\n import _multiprocessing\r\nModuleNotFoundError: No module named '_multiprocessing'\r\n>>> import multiprocessing\r\n>>> import uvicorn\r\nTraceback (most recent call last):\r\n File \"\", line 1, in \r\n File \"/lib/python3.10/site-packages/uvicorn/__init__.py\", line 2, in \r\n from uvicorn.main import Server, main, run\r\n File \"/lib/python3.10/site-packages/uvicorn/main.py\", line 24, in \r\n from uvicorn.supervisors import ChangeReload, Multiprocess\r\n File \"/lib/python3.10/site-packages/uvicorn/supervisors/__init__.py\", line 3, in \r\n from uvicorn.supervisors.basereload import BaseReload\r\n File \"/lib/python3.10/site-packages/uvicorn/supervisors/basereload.py\", line 12, in \r\n from uvicorn.subprocess import get_subprocess\r\n File \"/lib/python3.10/site-packages/uvicorn/subprocess.py\", line 14, in \r\n multiprocessing.allow_connection_pickling()\r\n File \"/lib/python3.10/multiprocessing/context.py\", line 170, in allow_connection_pickling\r\n from . import connection\r\n File \"/lib/python3.10/multiprocessing/connection.py\", line 21, in \r\n import _multiprocessing\r\nModuleNotFoundError: No module named '_multiprocessing'\r\n>>> \r\n```\r\nSince the `import ssl` trick fixed the `_ssl` error I was hopeful that `import multiprocessing` could fix the `_multiprocessing` one, but sadly it did not.\r\n\r\nBut it looks like i can address this issue just by making `import uvicorn` in `app.py` an optional import.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223234932, "label": "Get Datasette compatible with Pyodide"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1733#issuecomment-1115262218", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1733", "id": 1115262218, "node_id": "IC_kwDOBm6k_c5CeY0K", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T19:11:51Z", "updated_at": "2022-05-02T19:14:01Z", "author_association": "OWNER", "body": "Here's the full diff I applied to Datasette to get it fully working in Pyodide:\r\n\r\nhttps://github.com/simonw/datasette/compare/94a3171b01fde5c52697aeeff052e3ad4bab5391...8af32bc5b03c30b1f7a4a8cc4bd80eb7e2ee7b81\r\n\r\nAnd as a visible diff:\r\n\r\n```diff\r\ndiff --git a/datasette/app.py b/datasette/app.py\r\nindex d269372..6c0c5fc 100644\r\n--- a/datasette/app.py\r\n+++ b/datasette/app.py\r\n@@ -15,7 +15,6 @@ import pkg_resources\r\n import re\r\n import secrets\r\n import sys\r\n-import threading\r\n import traceback\r\n import urllib.parse\r\n from concurrent import futures\r\n@@ -26,7 +25,6 @@ from itsdangerous import URLSafeSerializer\r\n from jinja2 import ChoiceLoader, Environment, FileSystemLoader, PrefixLoader\r\n from jinja2.environment import Template\r\n from jinja2.exceptions import TemplateNotFound\r\n-import uvicorn\r\n \r\n from .views.base import DatasetteError, ureg\r\n from .views.database import DatabaseDownload, DatabaseView\r\n@@ -813,7 +811,6 @@ class Datasette:\r\n },\r\n \"datasette\": datasette_version,\r\n \"asgi\": \"3.0\",\r\n- \"uvicorn\": uvicorn.__version__,\r\n \"sqlite\": {\r\n \"version\": sqlite_version,\r\n \"fts_versions\": fts_versions,\r\n@@ -854,23 +851,7 @@ class Datasette:\r\n ]\r\n \r\n def _threads(self):\r\n- threads = list(threading.enumerate())\r\n- d = {\r\n- \"num_threads\": len(threads),\r\n- \"threads\": [\r\n- {\"name\": t.name, \"ident\": t.ident, \"daemon\": t.daemon} for t in threads\r\n- ],\r\n- }\r\n- # Only available in Python 3.7+\r\n- if hasattr(asyncio, \"all_tasks\"):\r\n- tasks = asyncio.all_tasks()\r\n- d.update(\r\n- {\r\n- \"num_tasks\": len(tasks),\r\n- \"tasks\": [_cleaner_task_str(t) for t in tasks],\r\n- }\r\n- )\r\n- return d\r\n+ return {\"num_threads\": 0, \"threads\": []}\r\n \r\n def _actor(self, request):\r\n return {\"actor\": request.actor}\r\ndiff --git a/datasette/database.py b/datasette/database.py\r\nindex ba594a8..b50142d 100644\r\n--- a/datasette/database.py\r\n+++ b/datasette/database.py\r\n@@ -4,7 +4,6 @@ from pathlib import Path\r\n import janus\r\n import queue\r\n import sys\r\n-import threading\r\n import uuid\r\n \r\n from .tracer import trace\r\n@@ -21,8 +20,6 @@ from .utils import (\r\n )\r\n from .inspect import inspect_hash\r\n \r\n-connections = threading.local()\r\n-\r\n AttachedDatabase = namedtuple(\"AttachedDatabase\", (\"seq\", \"name\", \"file\"))\r\n \r\n \r\n@@ -43,12 +40,12 @@ class Database:\r\n self.hash = None\r\n self.cached_size = None\r\n self._cached_table_counts = None\r\n- self._write_thread = None\r\n- self._write_queue = None\r\n if not self.is_mutable and not self.is_memory:\r\n p = Path(path)\r\n self.hash = inspect_hash(p)\r\n self.cached_size = p.stat().st_size\r\n+ self._read_connection = None\r\n+ self._write_connection = None\r\n \r\n @property\r\n def cached_table_counts(self):\r\n@@ -134,60 +131,17 @@ class Database:\r\n return results\r\n \r\n async def execute_write_fn(self, fn, block=True):\r\n- task_id = uuid.uuid5(uuid.NAMESPACE_DNS, \"datasette.io\")\r\n- if self._write_queue is None:\r\n- self._write_queue = queue.Queue()\r\n- if self._write_thread is None:\r\n- self._write_thread = threading.Thread(\r\n- target=self._execute_writes, daemon=True\r\n- )\r\n- self._write_thread.start()\r\n- reply_queue = janus.Queue()\r\n- self._write_queue.put(WriteTask(fn, task_id, reply_queue))\r\n- if block:\r\n- result = await reply_queue.async_q.get()\r\n- if isinstance(result, Exception):\r\n- raise result\r\n- else:\r\n- return result\r\n- else:\r\n- return task_id\r\n-\r\n- def _execute_writes(self):\r\n- # Infinite looping thread that protects the single write connection\r\n- # to this database\r\n- conn_exception = None\r\n- conn = None\r\n- try:\r\n- conn = self.connect(write=True)\r\n- self.ds._prepare_connection(conn, self.name)\r\n- except Exception as e:\r\n- conn_exception = e\r\n- while True:\r\n- task = self._write_queue.get()\r\n- if conn_exception is not None:\r\n- result = conn_exception\r\n- else:\r\n- try:\r\n- result = task.fn(conn)\r\n- except Exception as e:\r\n- sys.stderr.write(\"{}\\n\".format(e))\r\n- sys.stderr.flush()\r\n- result = e\r\n- task.reply_queue.sync_q.put(result)\r\n+ # We always treat it as if block=True now\r\n+ if self._write_connection is None:\r\n+ self._write_connection = self.connect(write=True)\r\n+ self.ds._prepare_connection(self._write_connection, self.name)\r\n+ return fn(self._write_connection)\r\n \r\n async def execute_fn(self, fn):\r\n- def in_thread():\r\n- conn = getattr(connections, self.name, None)\r\n- if not conn:\r\n- conn = self.connect()\r\n- self.ds._prepare_connection(conn, self.name)\r\n- setattr(connections, self.name, conn)\r\n- return fn(conn)\r\n-\r\n- return await asyncio.get_event_loop().run_in_executor(\r\n- self.ds.executor, in_thread\r\n- )\r\n+ if self._read_connection is None:\r\n+ self._read_connection = self.connect()\r\n+ self.ds._prepare_connection(self._read_connection, self.name)\r\n+ return fn(self._read_connection)\r\n \r\n async def execute(\r\n self,\r\ndiff --git a/setup.py b/setup.py\r\nindex 7f0562f..c41669c 100644\r\n--- a/setup.py\r\n+++ b/setup.py\r\n@@ -44,20 +44,20 @@ setup(\r\n install_requires=[\r\n \"asgiref>=3.2.10,<3.6.0\",\r\n \"click>=7.1.1,<8.2.0\",\r\n- \"click-default-group~=1.2.2\",\r\n+ # \"click-default-group~=1.2.2\",\r\n \"Jinja2>=2.10.3,<3.1.0\",\r\n \"hupper~=1.9\",\r\n \"httpx>=0.20\",\r\n \"pint~=0.9\",\r\n \"pluggy>=1.0,<1.1\",\r\n- \"uvicorn~=0.11\",\r\n+ # \"uvicorn~=0.11\",\r\n \"aiofiles>=0.4,<0.9\",\r\n \"janus>=0.6.2,<1.1\",\r\n \"asgi-csrf>=0.9\",\r\n \"PyYAML>=5.3,<7.0\",\r\n \"mergedeep>=1.1.1,<1.4.0\",\r\n \"itsdangerous>=1.1,<3.0\",\r\n- \"python-baseconv==1.2.2\",\r\n+ # \"python-baseconv==1.2.2\",\r\n ],\r\n entry_points=\"\"\"\r\n [console_scripts]\r\n```", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223234932, "label": "Get Datasette compatible with Pyodide"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1733#issuecomment-1115258737", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1733", "id": 1115258737, "node_id": "IC_kwDOBm6k_c5CeX9x", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T19:08:17Z", "updated_at": "2022-05-02T19:08:17Z", "author_association": "OWNER", "body": "I was going to vendor `baseconv.py`, but then I reconsidered - what if there are plugins out there that expect `import baseconv` to work because they have dependend on Datasette?\r\n\r\nI used https://cs.github.com/ and as far as I can tell there aren't any!\r\n\r\nSo I'm going to remove that dependency and work out a smarter way to do this - probably by providing a utility function within Datasette itself.", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223234932, "label": "Get Datasette compatible with Pyodide"}, "performed_via_github_app": null} {"html_url": "https://github.com/simonw/datasette/issues/1733#issuecomment-1115256318", "issue_url": "https://api.github.com/repos/simonw/datasette/issues/1733", "id": 1115256318, "node_id": "IC_kwDOBm6k_c5CeXX-", "user": {"value": 9599, "label": "simonw"}, "created_at": "2022-05-02T19:05:55Z", "updated_at": "2022-05-02T19:05:55Z", "author_association": "OWNER", "body": "I released a `click-default-group-wheel` package to solve that dependency issue. I've already upgraded `sqlite-utils` to that, so now you can use that in Pyodide:\r\n\r\n- https://github.com/simonw/sqlite-utils/pull/429\r\n\r\n`python-baseconv` is only used for actor cookie expiration times:\r\n\r\nhttps://github.com/simonw/datasette/blob/0a7621f96f8ad14da17e7172e8a7bce24ef78966/datasette/actor_auth_cookie.py#L16-L20\r\n\r\nDatasette never actually sets that cookie itself - it instead encourages plugins to set it in the authentication documentation here: https://docs.datasette.io/en/0.61.1/authentication.html#including-an-expiry-time", "reactions": "{\"total_count\": 0, \"+1\": 0, \"-1\": 0, \"laugh\": 0, \"hooray\": 0, \"confused\": 0, \"heart\": 0, \"rocket\": 0, \"eyes\": 0}", "issue": {"value": 1223234932, "label": "Get Datasette compatible with Pyodide"}, "performed_via_github_app": null}