|
4 | 4 |
|
5 | 5 | from asyncio import get_running_loop |
6 | 6 | from concurrent.futures import Future as SyncFuture |
7 | | -from contextlib import contextmanager |
8 | 7 | from operator import index as op_index |
9 | 8 | from typing import TYPE_CHECKING, Any, cast |
10 | 9 |
|
|
20 | 19 | JSUndefinedType, |
21 | 20 | PythonJSConvertedTypes, |
22 | 21 | ) |
| 22 | +from py_mini_racer._wrap_py_function import wrap_py_function_as_js_function |
23 | 23 |
|
24 | 24 | if TYPE_CHECKING: |
25 | 25 | from asyncio import Future |
26 | | - from collections.abc import Callable, Generator, Iterator |
| 26 | + from collections.abc import Generator, Iterator |
27 | 27 |
|
28 | 28 | from py_mini_racer._abstract_context import AbstractContext, AbstractValueHandle |
29 | 29 | from py_mini_racer._exc import JSEvalException |
@@ -171,56 +171,55 @@ def get(self, *, timeout: float | None = None) -> PythonJSConvertedTypes: |
171 | 171 | This is deprecated; use timeout_sec instead. |
172 | 172 | """ |
173 | 173 |
|
174 | | - future: SyncFuture[PythonJSConvertedTypes] = SyncFuture() |
| 174 | + future: SyncFuture[JSArray] = SyncFuture() |
| 175 | + is_rejected = False |
175 | 176 |
|
176 | | - def future_caller(value: PythonJSConvertedTypes) -> None: |
177 | | - future.set_result(value) |
| 177 | + def on_resolved(value: PythonJSConvertedTypes | JSEvalException) -> None: |
| 178 | + future.set_result(cast("JSArray", value)) |
178 | 179 |
|
179 | | - with self._attach_callbacks_to_promise(future_caller): |
180 | | - return self._unpack_promise_results(future.result(timeout=timeout)) |
| 180 | + def on_rejected(value: PythonJSConvertedTypes | JSEvalException) -> None: |
| 181 | + nonlocal is_rejected |
| 182 | + is_rejected = True |
| 183 | + future.set_result(cast("JSArray", value)) |
181 | 184 |
|
182 | | - def __await__(self) -> Generator[Any, None, Any]: |
183 | | - return self._do_await().__await__() |
184 | | - |
185 | | - async def _do_await(self) -> PythonJSConvertedTypes: |
186 | | - loop = get_running_loop() |
187 | | - future: Future[PythonJSConvertedTypes] = loop.create_future() |
188 | | - |
189 | | - def future_caller(value: PythonJSConvertedTypes) -> None: |
190 | | - loop.call_soon_threadsafe(future.set_result, value) |
| 185 | + with ( |
| 186 | + self._ctx.js_to_py_callback(on_resolved) as on_resolved_js_func, |
| 187 | + self._ctx.js_to_py_callback(on_rejected) as on_rejected_js_func, |
| 188 | + ): |
| 189 | + self._ctx.promise_then(self, on_resolved_js_func, on_rejected_js_func) |
191 | 190 |
|
192 | | - with self._attach_callbacks_to_promise(future_caller): |
193 | | - return self._unpack_promise_results(await future) |
| 191 | + result = future.result(timeout=timeout) |
194 | 192 |
|
195 | | - @contextmanager |
196 | | - def _attach_callbacks_to_promise( |
197 | | - self, future_caller: Callable[[Any], None] |
198 | | - ) -> Generator[None, None, None]: |
199 | | - """Attach the given Python callbacks to a JS Promise.""" |
| 193 | + if is_rejected: |
| 194 | + msg = _get_exception_msg(result[0]) |
| 195 | + raise JSPromiseError(msg) |
200 | 196 |
|
201 | | - def on_resolved_and_cleanup( |
202 | | - value: PythonJSConvertedTypes | JSEvalException, |
203 | | - ) -> None: |
204 | | - future_caller([False, cast("JSArray", value)]) |
| 197 | + return result[0] |
205 | 198 |
|
206 | | - def on_rejected_and_cleanup( |
207 | | - value: PythonJSConvertedTypes | JSEvalException, |
208 | | - ) -> None: |
209 | | - future_caller([True, cast("JSArray", value)]) |
| 199 | + def __await__(self) -> Generator[Any, None, Any]: |
| 200 | + return self._do_await().__await__() |
210 | 201 |
|
211 | | - with ( |
212 | | - self._ctx.js_to_py_callback(on_resolved_and_cleanup) as on_resolved_js_func, |
213 | | - self._ctx.js_to_py_callback(on_rejected_and_cleanup) as on_rejected_js_func, |
| 202 | + async def _do_await(self) -> PythonJSConvertedTypes: |
| 203 | + future: Future[PythonJSConvertedTypes] = get_running_loop().create_future() |
| 204 | + |
| 205 | + async def on_resolved(value: PythonJSConvertedTypes | JSEvalException) -> None: |
| 206 | + future.set_result(cast("PythonJSConvertedTypes", value)) |
| 207 | + |
| 208 | + async def on_rejected(value: PythonJSConvertedTypes | JSEvalException) -> None: |
| 209 | + future.set_exception( |
| 210 | + JSPromiseError( |
| 211 | + _get_exception_msg(cast("PythonJSConvertedTypes", value)) |
| 212 | + ) |
| 213 | + ) |
| 214 | + |
| 215 | + async with ( |
| 216 | + wrap_py_function_as_js_function( |
| 217 | + self._ctx, on_resolved |
| 218 | + ) as on_resolved_js_func, |
| 219 | + wrap_py_function_as_js_function( |
| 220 | + self._ctx, on_rejected |
| 221 | + ) as on_rejected_js_func, |
214 | 222 | ): |
215 | 223 | self._ctx.promise_then(self, on_resolved_js_func, on_rejected_js_func) |
216 | | - yield |
217 | 224 |
|
218 | | - def _unpack_promise_results( |
219 | | - self, results: PythonJSConvertedTypes |
220 | | - ) -> PythonJSConvertedTypes: |
221 | | - is_rejected, argv = cast("JSArray", results) |
222 | | - result = cast("JSArray", argv)[0] |
223 | | - if is_rejected: |
224 | | - msg = _get_exception_msg(result) |
225 | | - raise JSPromiseError(msg) |
226 | | - return result |
| 225 | + return await future |
0 commit comments