mirror of https://github.com/pyodide/pyodide.git
FIX Check if future is done before trying to set a result on it (#4837)
Otherwise, the future raises invalid state error
This commit is contained in:
parent
97eda9d0bf
commit
43cce6e5ff
|
@ -24,9 +24,14 @@ myst:
|
|||
stack switching is disabled.
|
||||
{pr}`4817`
|
||||
|
||||
- {{ Fix }} Resolved an issue where string keys in `PyProxyJsonAdaptor` were unexpectedly cast to numbers.
|
||||
- {{ Fix }} Resolved an issue where string keys in `PyProxyJsonAdaptor` were
|
||||
unexpectedly cast to numbers.
|
||||
{pr}`4825`
|
||||
|
||||
- {{ Fix }} When a `Future` connected to a `Promise` is cancelled, don't raise
|
||||
`InvalidStateError`.
|
||||
{pr}`4837`
|
||||
|
||||
### Packages
|
||||
|
||||
- New Packages: `pytest-asyncio` {pr}`4819`
|
||||
|
|
|
@ -2637,6 +2637,8 @@ wrap_promise(JsVal promise, JsVal done_callback, PyObject* js2py_converter)
|
|||
{
|
||||
bool success = false;
|
||||
PyObject* loop = NULL;
|
||||
PyObject* helpers_mod = NULL;
|
||||
PyObject* helpers = NULL;
|
||||
PyObject* set_result = NULL;
|
||||
PyObject* set_exception = NULL;
|
||||
|
||||
|
@ -2648,9 +2650,15 @@ wrap_promise(JsVal promise, JsVal done_callback, PyObject* js2py_converter)
|
|||
result = _PyObject_CallMethodIdNoArgs(loop, &PyId_create_future);
|
||||
FAIL_IF_NULL(result);
|
||||
|
||||
set_result = _PyObject_GetAttrId(result, &PyId_set_result);
|
||||
helpers_mod = PyImport_ImportModule("_pyodide._future_helper");
|
||||
FAIL_IF_NULL(helpers_mod);
|
||||
_Py_IDENTIFIER(get_future_resolvers);
|
||||
helpers = _PyObject_CallMethodIdOneArg(
|
||||
helpers_mod, &PyId_get_future_resolvers, result);
|
||||
FAIL_IF_NULL(helpers);
|
||||
set_result = Py_XNewRef(PyTuple_GetItem(helpers, 0));
|
||||
FAIL_IF_NULL(set_result);
|
||||
set_exception = _PyObject_GetAttrId(result, &PyId_set_exception);
|
||||
set_exception = Py_XNewRef(PyTuple_GetItem(helpers, 1));
|
||||
FAIL_IF_NULL(set_exception);
|
||||
|
||||
promise = JsvPromise_Resolve(promise);
|
||||
|
@ -2663,6 +2671,8 @@ wrap_promise(JsVal promise, JsVal done_callback, PyObject* js2py_converter)
|
|||
success = true;
|
||||
finally:
|
||||
Py_CLEAR(loop);
|
||||
Py_CLEAR(helpers_mod);
|
||||
Py_CLEAR(helpers);
|
||||
Py_CLEAR(set_result);
|
||||
Py_CLEAR(set_exception);
|
||||
if (!success) {
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
def set_result(fut, val):
|
||||
if fut.done():
|
||||
return
|
||||
fut.set_result(val)
|
||||
|
||||
|
||||
def set_exception(fut, val):
|
||||
if fut.done():
|
||||
return
|
||||
fut.set_exception(val)
|
||||
|
||||
|
||||
def get_future_resolvers(fut):
|
||||
return (set_result.__get__(fut), set_exception.__get__(fut))
|
|
@ -2,6 +2,7 @@ import asyncio
|
|||
import time
|
||||
|
||||
import pytest
|
||||
from pytest_pyodide import run_in_pyodide
|
||||
|
||||
from pyodide.code import eval_code_async
|
||||
|
||||
|
@ -421,3 +422,24 @@ def test_await_pyproxy_async_def(selenium):
|
|||
return (!!packages.packages) && (!!packages.info);
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
@run_in_pyodide
|
||||
async def inner_test_cancellation(selenium):
|
||||
from asyncio import ensure_future, sleep
|
||||
|
||||
from js import fetch
|
||||
|
||||
async def f():
|
||||
while True:
|
||||
await fetch("/")
|
||||
|
||||
fut = ensure_future(f())
|
||||
await sleep(0.01)
|
||||
fut.cancel()
|
||||
await sleep(0.1)
|
||||
|
||||
|
||||
def test_cancellation(selenium):
|
||||
inner_test_cancellation(selenium)
|
||||
assert "InvalidStateError" not in selenium.logs
|
||||
|
|
Loading…
Reference in New Issue