async-timer 1.1.0__tar.gz → 1.1.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: async-timer
3
- Version: 1.1.0
3
+ Version: 1.1.1
4
4
  Summary: The missing Python async timer.
5
5
  License: MIT
6
6
  Keywords: async,timer
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "async-timer"
3
- version = "v1.1.0"
3
+ version = "v1.1.1"
4
4
  description = "The missing Python async timer."
5
5
  authors = ["Ilya O. <vrghost@gmail.com>"]
6
6
  license = "MIT"
@@ -0,0 +1,2 @@
1
+ from . import pacemaker, timer, traget_caller
2
+ from .timer import Timer
@@ -1,6 +1,5 @@
1
1
  """Utility async io functions"""
2
2
  import asyncio
3
- import inspect
4
3
  import logging
5
4
  import time
6
5
  import typing
@@ -14,6 +13,8 @@ TimerMainTaskT = typing.Union[
14
13
  typing.Callable[[], typing.Coroutine[typing.Any, typing.Any, T]],
15
14
  typing.Callable[[], typing.AsyncGenerator[T, typing.Any]],
16
15
  typing.Callable[[], typing.Generator[T, typing.Any, typing.Any]],
16
+ typing.AsyncGenerator[T, typing.Any],
17
+ typing.Generator[T, typing.Any, typing.Any],
17
18
  ]
18
19
  TimerCallbackT = typing.Callable[["Timer[T]", TimerMainTaskT[T]], None]
19
20
 
@@ -95,7 +96,7 @@ class Timer(typing.Generic[T]):
95
96
  one resolving cancels the timer
96
97
  """
97
98
  self.iterator = async_timer.pacemaker.TimerPacemaker(delay)
98
- self.target = target
99
+ self.target_caller = async_timer.traget_caller.Caller(target)
99
100
  self.result_fanout = FanoutRv()
100
101
  self.exception_callback = exc_cb
101
102
  self.cancel_callback = cancel_cb
@@ -189,63 +190,24 @@ class Timer(typing.Generic[T]):
189
190
  except asyncio.CancelledError as err:
190
191
  raise StopAsyncIteration() from err
191
192
 
192
- def _maybe_detect_generator(
193
- self, target_rv
194
- ) -> typing.Tuple[T, typing.Callable[[], T]]:
195
- """Check if the value returned by the `self.target` call is a
196
- kind of generator (sync or async).
197
-
198
- Returns a (this_iter_rv, new_callable) tuple
199
- """
200
- if inspect.isgenerator(target_rv):
201
-
202
- def _lock_sync_gen_ctx(gen_src):
203
- return lambda: next(gen_src)
204
-
205
- get_next_val = _lock_sync_gen_ctx(target_rv)
206
- rv = (get_next_val(), get_next_val)
207
- elif inspect.isasyncgen(target_rv):
208
-
209
- def _lock_async_gen_ctx(gen_src):
210
- return lambda: gen_src.__anext__()
211
-
212
- get_next_val = _lock_async_gen_ctx(target_rv)
213
- rv = (get_next_val(), get_next_val)
214
- else:
215
- rv = (target_rv, None)
216
- return rv
217
-
218
193
  async def _loop_callback_routine(self):
219
- get_next_val = self.target
220
- first_iter = True
221
194
  try:
222
195
  async for _ in self.iterator:
223
196
  try:
224
- next_val = get_next_val()
225
- if first_iter:
226
- (next_val, updated_get_next_val) = self._maybe_detect_generator(
227
- next_val
228
- )
229
- if updated_get_next_val is not None:
230
- get_next_val = updated_get_next_val
231
- if inspect.isawaitable(next_val):
232
- rv = await next_val
233
- else:
234
- rv = next_val
235
- except (StopIteration, StopAsyncIteration):
197
+ rv = await self.target_caller.next()
198
+ except StopAsyncIteration:
236
199
  break
237
200
  except Exception as err:
238
201
  await self.result_fanout.send_exception(err)
239
- self.exception_callback(self, self.target)
202
+ self.exception_callback(self, self.target_caller.target)
240
203
  break
241
204
  else:
242
205
  await self.result_fanout.send_result(rv)
243
- first_iter = False
244
206
  self.hit_count += 1
245
207
  finally:
246
208
  # Main loop finished - cancel all watchers
247
209
  await self.result_fanout.cancel()
248
- self.cancel_callback(self, self.target)
210
+ self.cancel_callback(self, self.target_caller.target)
249
211
 
250
212
  async def cancel(self):
251
213
  """Unshedule the timer"""
@@ -261,7 +223,8 @@ class Timer(typing.Generic[T]):
261
223
 
262
224
  def __repr__(self) -> str:
263
225
  return (
264
- f"<{self.__class__.__name__} target={self.target!r} delay={self.delay!r}"
226
+ f"<{self.__class__.__name__} target={self.target_caller.target!r}"
227
+ f" delay={self.delay!r}"
265
228
  f" hit_count={self.hit_count!r}"
266
229
  f" exception_callback={self.exception_callback!r}"
267
230
  f" cancel_callback={self.cancel_callback!r}"
@@ -0,0 +1,70 @@
1
+ """This module is responsible for the magic behaviour calling the `target` function."""
2
+
3
+ import inspect
4
+ from collections.abc import Iterator
5
+
6
+
7
+ class Caller:
8
+ target = None
9
+ get_next_val = None
10
+ first_call: bool = True
11
+
12
+ def __init__(self, target):
13
+ self.target = target
14
+
15
+ def _wrap_generator(self, maybe_gen):
16
+ if inspect.isgenerator(maybe_gen):
17
+
18
+ def _lock_sync_gen_ctx():
19
+ return lambda: next(maybe_gen)
20
+
21
+ gen_next_val = _lock_sync_gen_ctx()
22
+ elif inspect.isasyncgen(maybe_gen):
23
+
24
+ def _lock_async_gen_ctx():
25
+ return lambda: maybe_gen.__anext__()
26
+
27
+ gen_next_val = _lock_async_gen_ctx()
28
+ elif isinstance(maybe_gen, Iterator):
29
+
30
+ def _lock_iterator_ctx():
31
+ return next(maybe_gen)
32
+
33
+ gen_next_val = _lock_iterator_ctx
34
+ else:
35
+ gen_next_val = None
36
+ return gen_next_val
37
+
38
+ def _setup(self, target):
39
+ """Configure `get_next_val` to return next value.
40
+
41
+ Return the first such next value.
42
+ """
43
+ self.get_next_val = self._wrap_generator(target)
44
+ if self.get_next_val:
45
+ # `target` is a generator and we now have the
46
+ # `get_next_val`
47
+ return self.get_next_val()
48
+ assert callable(target), "Otherwise target must be callable"
49
+ target_rv = target()
50
+ self.get_next_val = self._wrap_generator(target_rv)
51
+ if self.get_next_val:
52
+ # Tartget is a callable that returned a generator.
53
+ return self.get_next_val()
54
+ # Otherwise, target is just a callable that returns values
55
+ self.get_next_val = target
56
+ return target_rv
57
+
58
+ async def next(self):
59
+ """Call `target` one more time."""
60
+ try:
61
+ if self.first_call:
62
+ rv = self._setup(self.target)
63
+ self.first_call = False
64
+ else:
65
+ rv = self.get_next_val()
66
+ except StopIteration as _err:
67
+ raise StopAsyncIteration() from _err
68
+ if inspect.isawaitable(rv):
69
+ rv = await rv
70
+ return rv
@@ -1,2 +0,0 @@
1
- from . import pacemaker, timer
2
- from .timer import Timer
File without changes