Run CLI coroutines on explicit event loops

With the help of Codex CLI: https://gist.github.com/simonw/d2de93bfdf85a014a29093720c511093
This commit is contained in:
Simon Willison 2025-10-01 12:54:39 -07:00
commit 909448fb7a

View file

@ -42,6 +42,18 @@ from .utils.sqlite import sqlite3
from .utils.testing import TestClient from .utils.testing import TestClient
from .version import __version__ from .version import __version__
def run_sync(coro_func):
"""Run an async callable to completion on a fresh event loop."""
loop = asyncio.new_event_loop()
try:
asyncio.set_event_loop(loop)
return loop.run_until_complete(coro_func())
finally:
asyncio.set_event_loop(None)
loop.close()
# Use Rich for tracebacks if it is installed # Use Rich for tracebacks if it is installed
try: try:
from rich.traceback import install from rich.traceback import install
@ -135,8 +147,7 @@ def inspect(files, inspect_file, sqlite_extensions):
operations against immutable database files. operations against immutable database files.
""" """
app = Datasette([], immutables=files, sqlite_extensions=sqlite_extensions) app = Datasette([], immutables=files, sqlite_extensions=sqlite_extensions)
loop = asyncio.get_event_loop() inspect_data = run_sync(lambda: inspect_(files, sqlite_extensions))
inspect_data = loop.run_until_complete(inspect_(files, sqlite_extensions))
if inspect_file == "-": if inspect_file == "-":
sys.stdout.write(json.dumps(inspect_data, indent=2)) sys.stdout.write(json.dumps(inspect_data, indent=2))
else: else:
@ -612,10 +623,10 @@ def serve(
return ds return ds
# Run the "startup" plugin hooks # Run the "startup" plugin hooks
asyncio.get_event_loop().run_until_complete(ds.invoke_startup()) run_sync(ds.invoke_startup)
# Run async soundness checks - but only if we're not under pytest # Run async soundness checks - but only if we're not under pytest
asyncio.get_event_loop().run_until_complete(check_databases(ds)) run_sync(lambda: check_databases(ds))
if token and not get: if token and not get:
raise click.ClickException("--token can only be used with --get") raise click.ClickException("--token can only be used with --get")
@ -644,9 +655,7 @@ def serve(
if open_browser: if open_browser:
if url is None: if url is None:
# Figure out most convenient URL - to table, database or homepage # Figure out most convenient URL - to table, database or homepage
path = asyncio.get_event_loop().run_until_complete( path = run_sync(lambda: initial_path_for_datasette(ds))
initial_path_for_datasette(ds)
)
url = f"http://{host}:{port}{path}" url = f"http://{host}:{port}{path}"
webbrowser.open(url) webbrowser.open(url)
uvicorn_kwargs = dict( uvicorn_kwargs = dict(
@ -748,8 +757,7 @@ def create_token(
ds = Datasette(secret=secret, plugins_dir=plugins_dir) ds = Datasette(secret=secret, plugins_dir=plugins_dir)
# Run ds.invoke_startup() in an event loop # Run ds.invoke_startup() in an event loop
loop = asyncio.get_event_loop() run_sync(ds.invoke_startup)
loop.run_until_complete(ds.invoke_startup())
# Warn about any unknown actions # Warn about any unknown actions
actions = [] actions = []