77from collections .abc import Callable , Awaitable , AsyncIterator
88from functools import partial
99from dataclasses import dataclass
10- from contextlib import AbstractAsyncContextManager
10+ from contextlib import AbstractAsyncContextManager , asynccontextmanager
1111
12- from asyncgui import Task , move_on_when , _sleep_forever , _current_task
12+ from asyncgui import Task , move_on_when , _sleep_forever , _current_task , ExclusiveEvent , _wait_args_0 , current_task
1313
1414TimeUnit = TypeVar ("TimeUnit" )
1515ClockCallback : TypeAlias = Callable [[TimeUnit ], None ]
@@ -165,6 +165,52 @@ def callback(dt):
165165 finally :
166166 event .cancel ()
167167
168+ @asynccontextmanager
169+ async def sleep_freq (self , duration , * , free_to_await = False ) -> AsyncIterator [Callable [[], Awaitable [TimeUnit ]]]:
170+ '''
171+ An async form of :meth:`schedule_interval`. The following callback-style code:
172+
173+ .. code-block::
174+
175+ def callback(dt):
176+ print(dt)
177+ if some_condition:
178+ event.cancel()
179+
180+ event = clock.schedule_interval(callback, 10)
181+
182+ is equivalent to the following async-style code:
183+
184+ .. code-block::
185+
186+ async with clock.sleep_freq(10) as sleep:
187+ while True:
188+ dt = await sleep()
189+ print(dt)
190+ if some_condition:
191+ break
192+
193+ .. versionadded:: 0.6.1
194+
195+ The ``free_to_await`` parameter:
196+
197+ If set to False (the default), the only permitted async operation within the with-block is ``await xxx()``,
198+ where ``xxx`` is the identifier specified in the as-clause. To lift this restriction, set ``free_to_await`` to
199+ True — at the cost of slightly reduced performance.
200+ '''
201+ clock_event = self .schedule_interval (None , duration )
202+ try :
203+ if free_to_await :
204+ e = ExclusiveEvent ()
205+ clock_event .callback = e .fire
206+ yield e .wait_args_0
207+ else :
208+ task = await current_task ()
209+ clock_event .callback = task ._step
210+ yield _wait_args_0
211+ finally :
212+ clock_event .cancel ()
213+
168214 async def anim_with_dt (self , * , step = 0 ) -> AsyncIterator [TimeUnit ]:
169215 '''
170216 An async form of :meth:`schedule_interval`.
@@ -202,7 +248,7 @@ def callback(dt):
202248
203249 This is also true of other ``anim_with_xxx`` APIs.
204250 '''
205- async with _repeat_sleeping ( self , step ) as sleep :
251+ async with self . sleep_freq ( step ) as sleep :
206252 while True :
207253 yield await sleep ()
208254
@@ -223,7 +269,7 @@ async def anim_with_et(self, *, step=0) -> AsyncIterator[TimeUnit]:
223269 print(et)
224270 '''
225271 et = 0
226- async with _repeat_sleeping ( self , step ) as sleep :
272+ async with self . sleep_freq ( step ) as sleep :
227273 while True :
228274 et += await sleep ()
229275 yield et
@@ -238,7 +284,7 @@ async def anim_with_dt_et(self, *, step=0) -> AsyncIterator[tuple[TimeUnit, Time
238284 ...
239285 '''
240286 et = 0
241- async with _repeat_sleeping ( self , step ) as sleep :
287+ async with self . sleep_freq ( step ) as sleep :
242288 while True :
243289 dt = await sleep ()
244290 et += dt
@@ -275,7 +321,7 @@ async def anim_with_ratio(self, *, base, step=0) -> AsyncIterator[float]:
275321 The loop no longer stops when the progression reaches 1.0.
276322 '''
277323 et = 0
278- async with _repeat_sleeping ( self , step ) as sleep :
324+ async with self . sleep_freq ( step ) as sleep :
279325 while True :
280326 et += await sleep ()
281327 yield et / base
@@ -294,7 +340,7 @@ async def anim_with_dt_et_ratio(self, *, base, step=0) -> AsyncIterator[tuple[Ti
294340 The ``duration`` parameter was replaced with the ``base`` parameter.
295341 The loop no longer stops when the progression reaches 1.0.
296342 '''
297- async with _repeat_sleeping ( self , step ) as sleep :
343+ async with self . sleep_freq ( step ) as sleep :
298344 et = 0.
299345 while True :
300346 dt = await sleep ()
@@ -466,25 +512,3 @@ def anim_attrs_abbr(self, obj, *, d, s=0, t=_linear, **animated_properties) -> A
466512 :meth:`anim_attrs` cannot animate attributes named ``step``, ``duration`` and ``transition`` but this one can.
467513 '''
468514 return self ._anim_attrs (obj , d , s , t , animated_properties )
469-
470-
471- class _repeat_sleeping :
472- __slots__ = ('_timer' , '_interval' , '_event' , )
473-
474- def __init__ (self , clock : Clock , interval ):
475- self ._timer = clock
476- self ._interval = interval
477-
478- @staticmethod
479- @types .coroutine
480- def _sleep (_f = _sleep_forever ):
481- return (yield _f )[0 ][0 ]
482-
483- @types .coroutine
484- def __aenter__ (self , _current_task = _current_task ) -> Awaitable [Callable [[], Awaitable [TimeUnit ]]]:
485- task = (yield _current_task )[0 ][0 ]
486- self ._event = self ._timer .schedule_interval (task ._step , self ._interval )
487- return self ._sleep
488-
489- async def __aexit__ (self , exc_type , exc_val , exc_tb ):
490- self ._event .cancel ()
0 commit comments