mirror of https://github.com/pyodide/pyodide.git
ENH Add then, catch, and finally_ to the Tasks too (#3748)
This commit is contained in:
parent
6213477940
commit
0c458f4469
|
@ -15,6 +15,10 @@ myst:
|
|||
|
||||
## Unreleased
|
||||
|
||||
- {{ Enhancement }} The promise methods `then`, `catch` and `finally_` are now
|
||||
present also on `Task`s as well as `Future`s.
|
||||
{pr}`3748`
|
||||
|
||||
### Deployment
|
||||
|
||||
- {{ Fix }} Export `python_stdlib.zip` in `package.json`.
|
||||
|
|
|
@ -4,7 +4,7 @@ import inspect
|
|||
import sys
|
||||
import time
|
||||
import traceback
|
||||
from asyncio import Future
|
||||
from asyncio import Future, Task
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import Any, TypeVar, overload
|
||||
|
||||
|
@ -18,10 +18,11 @@ S = TypeVar("S")
|
|||
|
||||
|
||||
class PyodideFuture(Future[T]):
|
||||
"""A future with extra ``then``, ``catch``, and ``finally_`` methods based
|
||||
on the Javascript promise API. ``Webloop.create_future`` returns these so in
|
||||
practice all futures encountered in Pyodide should be an instance of
|
||||
``PyodideFuture``.
|
||||
"""A :py:class:`~asyncio.Future` with extra :js:meth:`~Promise.then`,
|
||||
:js:meth:`~Promise.catch`, and :js:meth:`finally_() <Promise.finally>` methods
|
||||
based on the Javascript promise API. :py:meth:`~asyncio.loop.create_future`
|
||||
returns these so in practice all futures encountered in Pyodide should be an
|
||||
instance of :py:class:`~pyodide.webloop.PyodideFuture`.
|
||||
"""
|
||||
|
||||
@overload
|
||||
|
@ -141,7 +142,7 @@ class PyodideFuture(Future[T]):
|
|||
return self.then(None, onrejected)
|
||||
|
||||
def finally_(self, onfinally: Callable[[], None]) -> "PyodideFuture[T]":
|
||||
"""When the future is either resolved or rejected, call onfinally with
|
||||
"""When the future is either resolved or rejected, call ``onfinally`` with
|
||||
no arguments.
|
||||
"""
|
||||
result: PyodideFuture[T] = PyodideFuture()
|
||||
|
@ -167,6 +168,16 @@ class PyodideFuture(Future[T]):
|
|||
return result
|
||||
|
||||
|
||||
class PyodideTask(Task[T], PyodideFuture[T]):
|
||||
"""Inherits from both :py:class:`~asyncio.Task` and
|
||||
:py:class:`~pyodide.webloop.PyodideFuture`
|
||||
|
||||
Instantiation is discouraged unless you are writing your own event loop.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
class WebLoop(asyncio.AbstractEventLoop):
|
||||
"""A custom event loop for use in Pyodide.
|
||||
|
||||
|
@ -400,7 +411,7 @@ class WebLoop(asyncio.AbstractEventLoop):
|
|||
"""
|
||||
self._check_closed()
|
||||
if self._task_factory is None:
|
||||
task = asyncio.tasks.Task(coro, loop=self, name=name)
|
||||
task = PyodideTask(coro, loop=self, name=name)
|
||||
if task._source_traceback: # type: ignore[attr-defined]
|
||||
# Added comment:
|
||||
# this only happens if get_debug() returns True.
|
||||
|
@ -599,4 +610,4 @@ def _initialize_event_loop():
|
|||
policy.get_event_loop()
|
||||
|
||||
|
||||
__all__ = ["WebLoop", "WebLoopPolicy", "PyodideFuture"]
|
||||
__all__ = ["WebLoop", "WebLoopPolicy", "PyodideFuture", "PyodideTask"]
|
||||
|
|
|
@ -355,6 +355,51 @@ async def test_pyodide_future2(selenium):
|
|||
assert name == "pytest"
|
||||
|
||||
|
||||
@run_in_pyodide
|
||||
async def test_pyodide_task(selenium):
|
||||
from asyncio import Future, ensure_future, sleep
|
||||
|
||||
async def taskify(fut):
|
||||
return await fut
|
||||
|
||||
def do_the_thing():
|
||||
d = dict(
|
||||
did_onresolve=None,
|
||||
did_onreject=None,
|
||||
did_onfinally=False,
|
||||
)
|
||||
f: Future[int] = Future()
|
||||
t = ensure_future(taskify(f))
|
||||
t.then(
|
||||
lambda v: d.update(did_onresolve=v), lambda e: d.update(did_onreject=e)
|
||||
).finally_(lambda: d.update(did_onfinally=True))
|
||||
return f, d
|
||||
|
||||
f, d = do_the_thing()
|
||||
f.set_result(7)
|
||||
await sleep(0.1)
|
||||
assert d == dict(
|
||||
did_onresolve=7,
|
||||
did_onreject=None,
|
||||
did_onfinally=True,
|
||||
)
|
||||
|
||||
f, d = do_the_thing()
|
||||
e = Exception("Oops!")
|
||||
f.set_exception(e)
|
||||
assert d == dict(
|
||||
did_onresolve=None,
|
||||
did_onreject=None,
|
||||
did_onfinally=False,
|
||||
)
|
||||
await sleep(0.1)
|
||||
assert d == dict(
|
||||
did_onresolve=None,
|
||||
did_onreject=e,
|
||||
did_onfinally=True,
|
||||
)
|
||||
|
||||
|
||||
@run_in_pyodide
|
||||
async def test_inprogress(selenium):
|
||||
import asyncio
|
||||
|
|
Loading…
Reference in New Issue