Clean up compatibility with Pyodide (#1736)

* Optional uvicorn import for Pyodide, refs #1733
* --setting num_sql_threads 0 to disable threading, refs #1735
This commit is contained in:
Simon Willison 2022-05-02 13:15:27 -07:00 committed by GitHub
commit 3f00a29141
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 42 additions and 4 deletions

View file

@ -288,9 +288,12 @@ class Datasette:
self._settings = dict(DEFAULT_SETTINGS, **(settings or {}))
self.renderers = {} # File extension -> (renderer, can_render) functions
self.version_note = version_note
self.executor = futures.ThreadPoolExecutor(
max_workers=self.setting("num_sql_threads")
)
if self.setting("num_sql_threads") == 0:
self.executor = None
else:
self.executor = futures.ThreadPoolExecutor(
max_workers=self.setting("num_sql_threads")
)
self.max_returned_rows = self.setting("max_returned_rows")
self.sql_time_limit_ms = self.setting("sql_time_limit_ms")
self.page_size = self.setting("default_page_size")
@ -862,6 +865,8 @@ class Datasette:
]
def _threads(self):
if self.setting("num_sql_threads") == 0:
return {"num_threads": 0, "threads": []}
threads = list(threading.enumerate())
d = {
"num_threads": len(threads),

View file

@ -45,6 +45,9 @@ class Database:
self._cached_table_counts = None
self._write_thread = None
self._write_queue = None
# These are used when in non-threaded mode:
self._read_connection = None
self._write_connection = None
if not self.is_mutable and not self.is_memory:
p = Path(path)
self.hash = inspect_hash(p)
@ -134,6 +137,14 @@ class Database:
return results
async def execute_write_fn(self, fn, block=True):
if self.ds.executor is None:
# non-threaded mode
if self._write_connection is None:
self._write_connection = self.connect(write=True)
self.ds._prepare_connection(self._write_connection, self.name)
return fn(self._write_connection)
# threaded mode
task_id = uuid.uuid5(uuid.NAMESPACE_DNS, "datasette.io")
if self._write_queue is None:
self._write_queue = queue.Queue()
@ -177,6 +188,14 @@ class Database:
task.reply_queue.sync_q.put(result)
async def execute_fn(self, fn):
if self.ds.executor is None:
# non-threaded mode
if self._read_connection is None:
self._read_connection = self.connect()
self.ds._prepare_connection(self._read_connection, self.name)
return fn(self._read_connection)
# threaded mode
def in_thread():
conn = getattr(connections, self.name, None)
if not conn:

View file

@ -107,6 +107,8 @@ Maximum number of threads in the thread pool Datasette uses to execute SQLite qu
datasette mydatabase.db --setting num_sql_threads 10
Setting this to 0 turns off threaded SQL queries entirely - useful for environments that do not support threading such as `Pyodide <https://pyodide.org/>`__.
.. _setting_allow_facet:
allow_facet

View file

@ -1,7 +1,7 @@
"""
Tests for the datasette.app.Datasette class
"""
from datasette.app import Datasette
from datasette.app import Datasette, Database
from itsdangerous import BadSignature
from .fixtures import app_client
import pytest
@ -63,3 +63,15 @@ async def test_datasette_constructor():
"hash": None,
}
]
@pytest.mark.asyncio
async def test_num_sql_threads_zero():
ds = Datasette([], memory=True, settings={"num_sql_threads": 0})
db = ds.add_database(Database(ds, memory_name="test_num_sql_threads_zero"))
await db.execute_write("create table t(id integer primary key)")
await db.execute_write("insert into t (id) values (1)")
response = await ds.client.get("/-/threads.json")
assert response.json() == {"num_threads": 0, "threads": []}
response2 = await ds.client.get("/test_num_sql_threads_zero/t.json?_shape=array")
assert response2.json() == [{"id": 1}]