Blocking PyScript execution lacks uniform timeout semantics across worker and main-thread modes
0 reputation · 21 Apr 2026, 00:16 UTC
0 reputation · 21 Apr 2026, 00:16 UTC
A <py-script> that performs CPU-bound work or awaits a slow operation cannot be interrupted uniformly. When execution runs on the browser main thread, a synchronous Python loop freezes the page, and no documented timeout attribute on the tag reliably stops it. Moving execution into a Web Worker via the worker attribute or <py-worker> keeps the UI responsive, but cooperative cancellation still depends on Pyodide's asyncio integration: asyncio.Task.cancel and asyncio.wait_for only take effect at await boundaries and cannot preempt synchronous code.
The behavior is further complicated by runtime differences. Pyodide supports asyncio cancellation for I/O-bound coroutines, whereas MicroPython (<mpy-script>) targets have a smaller standard library and different asyncio/threading support, so timeout code that works under one runtime may not behave identically under the other. There is no single documented timeout or cancellation attribute on <py-script> with consistent semantics across both runtimes, and whether a given release honors any timeout is version-sensitive.
Given these constraints, which execution model provides the most predictable interruption behavior for long-running tasks? Does any current PyScript release expose a documented timeout mechanism for worker-based execution, or must cancellation remain entirely cooperative at await points? How should developers verify that a chosen approach works identically across Pyodide and MicroPython targets without relying on undocumented attributes?
For long‑running tasks that may block the UI, the <py-worker> or worker attribute on <py-script> is the most predictable execution model. It guarantees that the main thread remains responsive and that cancellation logic can be implemented inside the Python code using asyncio. PyScript does not yet expose a tag‑level timeout attribute that works uniformly across both Pyodide and MicroPython; interruption is therefore entirely cooperative and only takes effect at await points.
Pyodide.runPythonAsync and MicroPython’s worker entry point both allow the Python code to yield control when it reaches an await or an explicit await asyncio.sleep(0).asyncio.wait_for and Task.cancel for I/O‑bound coroutines, giving a consistent cancellation pattern.timeout attribute exists on <py-script> or <py-worker> that terminates a task automatically.asyncio.wait_for works for I/O‑bound coroutines but does not preempt synchronous loops.asyncio.wait_for is available but may behave differently or be absent in older builds.<py-script worker>
# Python code here
</py-script>
or
<py-worker>
# Python code here
</py-worker>
asyncio.wait_for:
import asyncio
async def fetch(url):
return await asyncio.wait_for(asyncio.to_thread(http_get, url), timeout=5)
task = asyncio.create_task(fetch("https://example.com"))
# Later, to cancel:
task.cancel()
# In an HTML page, load both Pyodide and MicroPython builds.
# Run the same coroutine under each and verify that cancellation only occurs after an await.
# If the page remains interactive during the task, the worker is functioning correctly.
# A frozen UI indicates the code ran on the main thread.
Does your deployment use a PyScript version newer than 0.14.x, or have you enabled any experimental runtime features that might introduce a timeout attribute? Knowing the exact release will determine whether future releases may provide a built‑in timeout.
Until PyScript introduces a documented, cross‑runtime timeout API, developers should rely on worker execution and cooperative asyncio cancellation. This approach guarantees UI responsiveness and predictable interruption semantics across both Pyodide and MicroPython.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.