vsjetengine 1.6.2__tar.gz → 1.6.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/PKG-INFO +1 -1
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/test_futures.py +72 -0
- vsjetengine-1.6.3/vsengine/_version.py +2 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/futures.py +67 -35
- vsjetengine-1.6.2/vsengine/_version.py +0 -2
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/.gitignore +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/COPYING +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/README.md +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/pyproject.toml +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/__init__.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/_testutils.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/conftest.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/fixtures/heuristic_examples.json +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/fixtures/test.vpy +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/test_helpers.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/test_hospice.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/test_loop_adapters.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/test_loops.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/test_policy.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/test_policy_store.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/test_pytest_plugin.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/test_video.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/tests/test_vpy.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/__init__.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/_helpers.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/_hospice.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/_nodes.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/adapters/__init__.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/adapters/asyncio.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/adapters/trio.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/loops.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/policy.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/py.typed +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/pytest.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/video.py +0 -0
- {vsjetengine-1.6.2 → vsjetengine-1.6.3}/vsengine/vpy.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: vsjetengine
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.3
|
|
4
4
|
Summary: An engine for vapoursynth previewers, renderers and script analyis tools.
|
|
5
5
|
Project-URL: Source Code, https://github.com/Jaded-Encoding-Thaumaturgy/vs-jet-engine
|
|
6
6
|
Project-URL: Contact, https://discord.gg/XTpc6Fa9eB
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
# SPDX-License-Identifier: EUPL-1.2
|
|
6
6
|
"""Tests for the unified future system."""
|
|
7
7
|
|
|
8
|
+
import asyncio
|
|
8
9
|
import contextlib
|
|
9
10
|
import threading
|
|
10
11
|
from collections.abc import AsyncGenerator, Generator, Iterator
|
|
@@ -741,3 +742,74 @@ def test_unified_future_from_future_cancellation() -> None:
|
|
|
741
742
|
assert uni.cancel() is True
|
|
742
743
|
assert uni.cancelled() is True
|
|
743
744
|
assert raw.cancelled() is True
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
@pytest.mark.asyncio
|
|
748
|
+
async def test_unified_future_add_loop_callback_cancellation_propagation() -> None:
|
|
749
|
+
set_loop(AsyncIOLoop())
|
|
750
|
+
raw = UnifiedFuture[int]()
|
|
751
|
+
chained = raw.add_loop_callback(lambda f: None)
|
|
752
|
+
|
|
753
|
+
assert chained.cancel() is True
|
|
754
|
+
assert chained.cancelled() is True
|
|
755
|
+
assert raw.cancelled() is True
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
@pytest.mark.asyncio
|
|
759
|
+
async def test_unified_future_add_loop_callback_custom_on_cancel() -> None:
|
|
760
|
+
set_loop(AsyncIOLoop())
|
|
761
|
+
cancelled_calls = list[threading.Thread]()
|
|
762
|
+
loop_thread = threading.current_thread()
|
|
763
|
+
raw = UnifiedFuture[int]()
|
|
764
|
+
chained = raw.add_loop_callback(
|
|
765
|
+
lambda f: None,
|
|
766
|
+
cancel_cb=lambda: cancelled_calls.append(threading.current_thread()),
|
|
767
|
+
)
|
|
768
|
+
|
|
769
|
+
assert chained.cancel() is True
|
|
770
|
+
assert chained.cancelled() is True
|
|
771
|
+
await asyncio.sleep(0.01)
|
|
772
|
+
assert cancelled_calls == [loop_thread]
|
|
773
|
+
assert raw.cancelled() is False
|
|
774
|
+
|
|
775
|
+
|
|
776
|
+
@pytest.mark.asyncio
|
|
777
|
+
async def test_unified_future_then_custom_on_cancel_on_loop() -> None:
|
|
778
|
+
set_loop(AsyncIOLoop())
|
|
779
|
+
cancelled_calls = list[threading.Thread]()
|
|
780
|
+
loop_thread = threading.current_thread()
|
|
781
|
+
raw = UnifiedFuture[int]()
|
|
782
|
+
chained = raw.then(
|
|
783
|
+
lambda x: x * 2,
|
|
784
|
+
cancel_cb=lambda: cancelled_calls.append(threading.current_thread()),
|
|
785
|
+
on_loop=True,
|
|
786
|
+
)
|
|
787
|
+
|
|
788
|
+
# Cancel from a background thread
|
|
789
|
+
t = threading.Thread(target=chained.cancel)
|
|
790
|
+
t.start()
|
|
791
|
+
t.join()
|
|
792
|
+
|
|
793
|
+
await asyncio.sleep(0.01)
|
|
794
|
+
assert chained.cancelled() is True
|
|
795
|
+
assert cancelled_calls == [loop_thread]
|
|
796
|
+
assert raw.cancelled() is False
|
|
797
|
+
|
|
798
|
+
|
|
799
|
+
@pytest.mark.asyncio
|
|
800
|
+
async def test_unified_future_add_loop_callback_parent_cancelled() -> None:
|
|
801
|
+
set_loop(AsyncIOLoop())
|
|
802
|
+
raw = UnifiedFuture[int]()
|
|
803
|
+
called = list[bool]()
|
|
804
|
+
|
|
805
|
+
def cb(f: Future[int]) -> None:
|
|
806
|
+
called.append(f.cancelled())
|
|
807
|
+
|
|
808
|
+
chained = raw.add_loop_callback(cb)
|
|
809
|
+
raw.cancel()
|
|
810
|
+
|
|
811
|
+
with pytest.raises(asyncio.CancelledError):
|
|
812
|
+
await chained
|
|
813
|
+
|
|
814
|
+
assert chained.cancelled() is True
|
|
815
|
+
assert called == [True]
|
|
@@ -127,7 +127,11 @@ class UnifiedFuture[T](Future[T], AbstractContextManager[Any], AbstractAsyncCont
|
|
|
127
127
|
# The done_callback should inherit the environment of the current call.
|
|
128
128
|
super().add_done_callback(keep_environment(fn))
|
|
129
129
|
|
|
130
|
-
def add_loop_callback(
|
|
130
|
+
def add_loop_callback(
|
|
131
|
+
self,
|
|
132
|
+
func: Callable[[Future[T]], Any],
|
|
133
|
+
cancel_cb: Callable[[], None] | None = None,
|
|
134
|
+
) -> UnifiedFuture[T]:
|
|
131
135
|
"""
|
|
132
136
|
Register a callback that is guaranteed to run on the event-loop thread.
|
|
133
137
|
|
|
@@ -135,25 +139,28 @@ class UnifiedFuture[T](Future[T], AbstractContextManager[Any], AbstractAsyncCont
|
|
|
135
139
|
this method marshals `func` back to the main event loop via `EventLoop.from_thread`.
|
|
136
140
|
|
|
137
141
|
:param func: A callable that receives the completed future.
|
|
142
|
+
:param cancel_cb: Optional callback invoked when the returned future is cancelled.
|
|
143
|
+
Defaults to cancelling the parent future.
|
|
138
144
|
:return: A UnifiedFuture that resolves to the original future's result once the callback completes
|
|
139
145
|
on the event loop.
|
|
140
146
|
"""
|
|
141
147
|
result = UnifiedFuture[T]()
|
|
142
148
|
|
|
143
|
-
def
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
result.set_exception(orig_exc)
|
|
151
|
-
else:
|
|
152
|
-
result.set_result(future.result())
|
|
149
|
+
def forward_parent(_: Future[Any]) -> None:
|
|
150
|
+
if self.cancelled():
|
|
151
|
+
result.cancel()
|
|
152
|
+
elif (orig_exc := self.exception()) is not None:
|
|
153
|
+
result.set_exception(orig_exc)
|
|
154
|
+
else:
|
|
155
|
+
result.set_result(self.result())
|
|
153
156
|
|
|
154
|
-
|
|
157
|
+
def wrapper(future: Future[T]) -> None:
|
|
158
|
+
if result.cancelled():
|
|
159
|
+
return
|
|
160
|
+
self._forward_loop_task(get_loop().from_thread(func, future), result, forward_parent)
|
|
155
161
|
|
|
156
|
-
self.add_done_callback(
|
|
162
|
+
self.add_done_callback(wrapper)
|
|
163
|
+
self._link_cancellation(result, cancel_cb, on_loop=True)
|
|
157
164
|
return result
|
|
158
165
|
|
|
159
166
|
# Manipulating futures
|
|
@@ -227,23 +234,11 @@ class UnifiedFuture[T](Future[T], AbstractContextManager[Any], AbstractAsyncCont
|
|
|
227
234
|
"""
|
|
228
235
|
result = UnifiedFuture[Any]()
|
|
229
236
|
|
|
230
|
-
def
|
|
237
|
+
def run_cb(cb: Callable[[Any], Any], v: T | BaseException) -> None:
|
|
231
238
|
if result.cancelled():
|
|
232
239
|
return
|
|
233
240
|
if on_loop:
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
def _forward(lf: Future[Any]) -> None:
|
|
237
|
-
if result.cancelled():
|
|
238
|
-
return
|
|
239
|
-
if lf.cancelled():
|
|
240
|
-
result.cancel()
|
|
241
|
-
elif (exc := lf.exception()) is not None:
|
|
242
|
-
result.set_exception(exc)
|
|
243
|
-
else:
|
|
244
|
-
result.set_result(lf.result())
|
|
245
|
-
|
|
246
|
-
loop_fut.add_done_callback(_forward)
|
|
241
|
+
self._forward_loop_task(get_loop().from_thread(cb, v), result)
|
|
247
242
|
else:
|
|
248
243
|
try:
|
|
249
244
|
r = cb(v)
|
|
@@ -252,26 +247,23 @@ class UnifiedFuture[T](Future[T], AbstractContextManager[Any], AbstractAsyncCont
|
|
|
252
247
|
else:
|
|
253
248
|
result.set_result(r)
|
|
254
249
|
|
|
255
|
-
def
|
|
250
|
+
def done(_: Future[T]) -> None:
|
|
256
251
|
if self.cancelled():
|
|
257
252
|
result.cancel()
|
|
258
253
|
return
|
|
259
254
|
if (exc := self.exception()) is not None:
|
|
260
255
|
if err_cb is not None:
|
|
261
|
-
|
|
256
|
+
run_cb(err_cb, exc)
|
|
262
257
|
else:
|
|
263
258
|
result.set_exception(exc)
|
|
264
259
|
elif not result.cancelled():
|
|
265
260
|
if success_cb is not None:
|
|
266
|
-
|
|
261
|
+
run_cb(success_cb, self.result())
|
|
267
262
|
else:
|
|
268
263
|
result.set_result(self.result())
|
|
269
264
|
|
|
270
|
-
self.add_done_callback(
|
|
271
|
-
|
|
272
|
-
result.add_done_callback(lambda f: cancel_cb() if f.cancelled() else None)
|
|
273
|
-
else:
|
|
274
|
-
result.add_done_callback(lambda f: self.cancel() if f.cancelled() else None)
|
|
265
|
+
self.add_done_callback(done)
|
|
266
|
+
self._link_cancellation(result, cancel_cb, on_loop=on_loop)
|
|
275
267
|
return result
|
|
276
268
|
|
|
277
269
|
def map[V](
|
|
@@ -369,6 +361,46 @@ class UnifiedFuture[T](Future[T], AbstractContextManager[Any], AbstractAsyncCont
|
|
|
369
361
|
|
|
370
362
|
raise NotImplementedError("(async) with is not implemented for this object")
|
|
371
363
|
|
|
364
|
+
def _link_cancellation(
|
|
365
|
+
self,
|
|
366
|
+
target: Future[Any],
|
|
367
|
+
cancel_cb: Callable[[], None] | None = None,
|
|
368
|
+
*,
|
|
369
|
+
on_loop: bool = False,
|
|
370
|
+
) -> None:
|
|
371
|
+
if cancel_cb is not None:
|
|
372
|
+
|
|
373
|
+
def on_cancel(f: Future[Any]) -> None:
|
|
374
|
+
if f.cancelled():
|
|
375
|
+
if on_loop:
|
|
376
|
+
get_loop().from_thread(cancel_cb)
|
|
377
|
+
else:
|
|
378
|
+
cancel_cb()
|
|
379
|
+
|
|
380
|
+
target.add_done_callback(on_cancel)
|
|
381
|
+
else:
|
|
382
|
+
target.add_done_callback(lambda f: self.cancel() if f.cancelled() else None)
|
|
383
|
+
|
|
384
|
+
@staticmethod
|
|
385
|
+
def _forward_loop_task(
|
|
386
|
+
loop_fut: Future[Any],
|
|
387
|
+
target: UnifiedFuture[Any],
|
|
388
|
+
on_done: Callable[[Future[Any]], None] | None = None,
|
|
389
|
+
) -> None:
|
|
390
|
+
def forward(lf: Future[Any]) -> None:
|
|
391
|
+
if target.cancelled():
|
|
392
|
+
return
|
|
393
|
+
if lf.cancelled():
|
|
394
|
+
target.cancel()
|
|
395
|
+
elif (exc := lf.exception()) is not None:
|
|
396
|
+
target.set_exception(exc)
|
|
397
|
+
elif on_done is not None:
|
|
398
|
+
on_done(lf)
|
|
399
|
+
else:
|
|
400
|
+
target.set_result(lf.result())
|
|
401
|
+
|
|
402
|
+
loop_fut.add_done_callback(forward)
|
|
403
|
+
|
|
372
404
|
|
|
373
405
|
class UnifiedIterator[T](Iterator[T], AsyncIterator[T]):
|
|
374
406
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|