python-iterutils 0.3.0__py3-none-any.whl → 0.3.1__py3-none-any.whl

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.
iterutils/__init__.py CHANGED
@@ -2,7 +2,7 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __author__ = "ChenyangGao <https://chenyanggao.github.io>"
5
- __version__ = (0, 3, 0)
5
+ __version__ = (0, 3, 1)
6
6
 
7
7
  from .dynamic import *
8
8
  from .gen_step import *
iterutils/dynamic.py CHANGED
@@ -3,58 +3,25 @@
3
3
 
4
4
  __all__ = ["dynamic_async", "dynamic_async_iter"]
5
5
 
6
- from asyncio import run as asyncio_run, to_thread
7
- from collections.abc import (
8
- AsyncIterable, AsyncIterator, Awaitable, Callable, Iterable, Iterator,
9
- )
6
+ from asyncio import AbstractEventLoop
7
+ from collections.abc import AsyncIterable, Callable, Iterable
10
8
  from inspect import isawaitable
11
9
 
12
10
  from argtools import has_keyword_arg
13
- from asynctools import ensure_async, in_async
11
+ from asynctools import (
12
+ ensure_async, in_async, iter_async, run_async, to_coroutine,
13
+ to_aiter,
14
+ )
14
15
  from decotools import optional
15
16
 
16
17
 
17
- async def _as_async[T](v: T, /) -> T:
18
- return v
19
-
20
-
21
- async def _as_async_iter[T](
22
- it: Iterable[T],
23
- /,
24
- threaded: bool = False,
25
- ) -> AsyncIterator[T]:
26
- if threaded:
27
- call = iter(it).__next__
28
- try:
29
- while True:
30
- yield await to_thread(call)
31
- except StopIteration:
32
- pass
33
- else:
34
- for e in it:
35
- yield e
36
-
37
-
38
- def _as_iter[T](
39
- it: AsyncIterable[T],
40
- /,
41
- run: Callable = asyncio_run,
42
- ) -> Iterator[T]:
43
- try:
44
- call = aiter(it).__anext__
45
- while True:
46
- yield run(call())
47
- except StopAsyncIteration:
48
- pass
49
-
50
-
51
18
  @optional
52
- def dynamic_async[T](
19
+ def dynamic_async(
53
20
  func: Callable,
54
21
  /,
55
- run: Callable = asyncio_run,
56
22
  threaded: bool = False,
57
23
  async_: None | bool = None,
24
+ loop: None | AbstractEventLoop = None,
58
25
  ):
59
26
  has_async_arg = has_keyword_arg(func, "async_")
60
27
  def wrapper(*args, async_: None | bool = async_, **kwds):
@@ -68,22 +35,22 @@ def dynamic_async[T](
68
35
  else:
69
36
  r = func(*args, **kwds)
70
37
  if not isawaitable(r):
71
- r = _as_async(r)
38
+ r = to_coroutine(r)
72
39
  else:
73
40
  r = func(*args, **kwds)
74
41
  if isawaitable(r):
75
- r = run(r)
42
+ r = run_async(r, loop)
76
43
  return r
77
44
  return wrapper
78
45
 
79
46
 
80
47
  @optional
81
- def dynamic_async_iter[T](
48
+ def dynamic_async_iter(
82
49
  func: Callable[..., Iterable] | Callable[..., AsyncIterable],
83
50
  /,
84
- run: Callable = asyncio_run,
85
51
  threaded: bool = False,
86
52
  async_: None | bool = None,
53
+ loop: None | AbstractEventLoop = None,
87
54
  ):
88
55
  has_async_arg = has_keyword_arg(func, "async_")
89
56
  def wrapper(*args, async_: None | bool = async_, **kwds):
@@ -94,9 +61,9 @@ def dynamic_async_iter[T](
94
61
  it = func(*args, **kwds)
95
62
  if async_:
96
63
  if not isinstance(it, AsyncIterable):
97
- it = _as_async_iter(it, threaded=threaded)
64
+ it = to_aiter(it, threaded=threaded)
98
65
  elif isinstance(it, AsyncIterable):
99
- it = _as_iter(it, run)
66
+ it = iter_async(it, loop)
100
67
  return it
101
68
  return wrapper
102
69
 
iterutils/gen_step.py CHANGED
@@ -3,7 +3,8 @@
3
3
 
4
4
  __all__ = [
5
5
  "iter_gen_step", "run_gen_step", "run_gen_step_iter",
6
- "as_gen_step", "with_iter_next", "Yield", "YieldFrom",
6
+ "as_gen_step", "with_iter_next", "with_lock", "split_cm",
7
+ "Yield", "YieldFrom",
7
8
  ]
8
9
 
9
10
  from asyncio import CancelledError as AsyncCancelledError
@@ -171,7 +172,7 @@ def run_gen_step[T](
171
172
  /,
172
173
  async_: Literal[True],
173
174
  running: SupportsBool = True,
174
- ) -> Awaitable[T]:
175
+ ) -> Coroutine[Any, Any, T]:
175
176
  ...
176
177
  def run_gen_step(
177
178
  gen,
@@ -358,6 +359,38 @@ def with_iter_next[T](
358
359
  pass
359
360
 
360
361
 
362
+ @contextmanager
363
+ def with_lock[T](lock, /):
364
+ """包装锁,以供 `run_gen_step` 和 `run_gen_step_iter` 使用
365
+
366
+ .. code:: python
367
+
368
+ if async_:
369
+ async def process():
370
+ async with lock:
371
+ ...
372
+ return process()
373
+ else:
374
+ with lock:
375
+ ...
376
+
377
+ 大概相当于
378
+
379
+ .. code:: python
380
+
381
+ def gen_step():
382
+ with with_lock(lock) as r:
383
+ yield r
384
+ ...
385
+
386
+ run_gen_step(gen_step, async_)
387
+ """
388
+ try:
389
+ yield lock.acquire()
390
+ finally:
391
+ lock.release()
392
+
393
+
361
394
  @overload
362
395
  def split_cm[T](
363
396
  cm: AbstractContextManager[T],
iterutils/misc.py CHANGED
@@ -2,10 +2,9 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  __all__ = [
5
- "iterable", "async_iterable", "map", "filter", "reduce", "zip",
6
- "chain", "chain_from_iterable", "chunked", "foreach", "async_foreach",
7
- "through", "async_through", "flatten", "async_flatten", "collect",
8
- "async_collect", "group_collect", "async_group_collect", "iter_unique",
5
+ "iterable", "map", "filter", "reduce", "zip",
6
+ "chain", "chain_from_iterable", "chunked", "foreach", "through",
7
+ "flatten", "collect", "async_group_collect", "iter_unique",
9
8
  "async_iter_unique", "wrap_iter", "wrap_aiter", "peek_iter", "peek_aiter",
10
9
  "acc_step", "cut_iter", "context", "backgroud_loop", "gen_startup",
11
10
  "async_gen_startup", "do_iter", "do_aiter", "bfs_iter", "bfs_gen",
@@ -24,7 +23,7 @@ from contextlib import asynccontextmanager, contextmanager, ExitStack, AsyncExit
24
23
  from copy import copy
25
24
  from functools import update_wrapper
26
25
  from itertools import batched, chain as _chain, pairwise
27
- from inspect import iscoroutinefunction
26
+ from inspect import isawaitable, iscoroutinefunction
28
27
  from sys import _getframe
29
28
  from _thread import start_new_thread
30
29
  from time import sleep, time
@@ -35,17 +34,13 @@ from typing import (
35
34
 
36
35
  from asynctools import (
37
36
  async_filter, async_map, async_reduce, async_zip, async_batched,
38
- ensure_async, ensure_aiter, async_chain, collect as async_collect,
37
+ ensure_async, ensure_aiter, async_chain, async_chain_from_iterable,
38
+ async_collect, async_foreach, async_through,
39
39
  )
40
40
  from texttools import format_time
41
41
  from undefined import undefined
42
42
 
43
43
 
44
- iterable = Iterable.__instancecheck__
45
- async_iterable = AsyncIterable.__instancecheck__
46
- isawaitable = Awaitable.__instancecheck__
47
-
48
-
49
44
  def _coalesce(vals, default=None):
50
45
  for val in vals:
51
46
  if val is not None:
@@ -232,11 +227,8 @@ def chain_from_iterable[T](
232
227
  *,
233
228
  async_: Literal[False, True] = False,
234
229
  ) -> Iterator[T] | AsyncIterator[T]:
235
- if async_ or not isinstance(iterables, Iterable):
236
- if isinstance(iterables, Iterable):
237
- return async_chain.from_iterable(iterables, threaded=threaded)
238
- else:
239
- return async_chain.from_iterable(iterables, threaded=threaded)
230
+ if async_ or threaded:
231
+ return async_chain_from_iterable(iterables, threaded=threaded)
240
232
  return _chain.from_iterable(iterables) # type: ignore
241
233
 
242
234
  setattr(chain, "from_iterable", chain_from_iterable)
@@ -291,85 +283,45 @@ def chunked[T](
291
283
 
292
284
 
293
285
  def foreach(
294
- value: Callable,
286
+ function: Callable,
295
287
  iterable: Iterable | AsyncIterable,
296
288
  /,
297
289
  *iterables: Iterable | AsyncIterable,
290
+ default = None,
298
291
  threaded: bool = False,
299
292
  ):
300
- """
301
- """
302
- if (not threaded and
303
- isinstance(iterable, Iterable) and
304
- all(isinstance(it, Iterable) for it in iterables)
293
+ if (threaded or
294
+ isinstance(iterable, AsyncIterable) or
295
+ any(isinstance(it, AsyncIterable) for it in iterables)
305
296
  ):
306
- if iterables:
307
- for args in _zip(iterable, *iterables):
308
- value(*args)
309
- else:
310
- for arg in iterable:
311
- value(arg)
312
- else:
313
- return async_foreach(value, iterable, *iterables, threaded=threaded)
314
-
315
-
316
- async def async_foreach(
317
- value: Callable,
318
- iterable: Iterable | AsyncIterable,
319
- /,
320
- *iterables: Iterable | AsyncIterable,
321
- threaded: bool = False,
322
- ):
323
- """
324
- """
325
- value = ensure_async(value, threaded=threaded)
297
+ return async_foreach(
298
+ function,
299
+ iterable,
300
+ *iterables,
301
+ default=default,
302
+ threaded=threaded,
303
+ )
304
+ r = default
326
305
  if iterables:
327
- async for args in async_zip(iterable, *iterables, threaded=threaded):
328
- await value(*args)
306
+ for args in _zip(iterable, *iterables):
307
+ r = function(*args)
329
308
  else:
330
- async for arg in ensure_aiter(iterable, threaded=threaded):
331
- await value(arg)
309
+ for arg in iterable:
310
+ r = function(arg)
311
+ return r
332
312
 
333
313
 
334
314
  def through(
335
315
  iterable: Iterable | AsyncIterable,
336
316
  /,
337
- take_while: None | Callable = None,
338
317
  threaded: bool = False,
339
318
  ):
340
319
  """
341
320
  """
342
- if threaded or not isinstance(iterable, Iterable):
343
- return async_through(iterable, take_while, threaded=threaded)
344
- elif take_while is None:
345
- for _ in iterable:
346
- pass
347
- else:
348
- for v in _map(take_while, iterable):
349
- if not v:
350
- break
351
-
352
-
353
- async def async_through(
354
- iterable: Iterable | AsyncIterable,
355
- /,
356
- take_while: None | Callable = None,
357
- threaded: bool = False,
358
- ):
359
- """
360
- """
361
- iterable = ensure_aiter(iterable, threaded=threaded)
362
- if take_while is None:
363
- async for _ in iterable:
364
- pass
365
- elif take_while is bool:
366
- async for v in iterable:
367
- if not v:
368
- break
369
- else:
370
- async for v in async_map(take_while, iterable):
371
- if not v:
372
- break
321
+ if threaded or isinstance(iterable, AsyncIterable):
322
+ return async_through(iterable, threaded=threaded)
323
+ for _ in iterable:
324
+ pass
373
325
 
374
326
 
375
327
  @overload
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-iterutils
3
- Version: 0.3.0
3
+ Version: 0.3.1
4
4
  Summary: Python another itertools.
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -21,7 +21,8 @@ Classifier: Programming Language :: Python :: 3 :: Only
21
21
  Classifier: Topic :: Software Development
22
22
  Classifier: Topic :: Software Development :: Libraries
23
23
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
- Requires-Dist: python-asynctools (>=0.1.4)
24
+ Requires-Dist: python-argtools (>=0.0.4)
25
+ Requires-Dist: python-asynctools (>=0.2.1)
25
26
  Requires-Dist: python-texttools (>=0.0.6)
26
27
  Requires-Dist: python-undefined (>=0.0.4)
27
28
  Project-URL: Homepage, https://github.com/ChenyangGao/python-modules/tree/main/python-iterutils
@@ -0,0 +1,9 @@
1
+ iterutils/__init__.py,sha256=e3TnEvFCdzg6jotU3JB95qHwcA2DrkRJjrTMLF1rYWs,193
2
+ iterutils/dynamic.py,sha256=AxvY1H61BUxMWkTLK6lQDzfKeFQXmRbPadQSf3LJ3gs,1946
3
+ iterutils/gen_step.py,sha256=66QInXz4VxANWHNgmSQ3x6U5I6_pUfCtTRgjftdgeQE,11078
4
+ iterutils/misc.py,sha256=43ViCCJLc2MJGiisTCTbVhOHH5Akfc3ZW7kQlI7pm5c,27823
5
+ iterutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ python_iterutils-0.3.1.dist-info/METADATA,sha256=0cMSV_WHhomz5qnES44vCKzPYQWApzE_oVw_4zDkat8,1521
7
+ python_iterutils-0.3.1.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
8
+ python_iterutils-0.3.1.dist-info/licenses/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
9
+ python_iterutils-0.3.1.dist-info/RECORD,,
@@ -1,9 +0,0 @@
1
- iterutils/__init__.py,sha256=Dxp6LiS0vgtgwo1QZJa1Q4Krxa50dFOai3SJK67tXnY,193
2
- iterutils/dynamic.py,sha256=qYDPlg5JaF637kxo8e2V3OWtzAn3JKLrMGXjH5z4fvw,2555
3
- iterutils/gen_step.py,sha256=Bi5KQO1FkcpTJ9gf1m5AZzsPoHUVQ0pLUiqnFdmKgBQ,10430
4
- iterutils/misc.py,sha256=TTVOs6qSNBN5sQHpCzfmv3LaY8k5H7PFp6RdcsXRdtM,29222
5
- iterutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- python_iterutils-0.3.0.dist-info/METADATA,sha256=NnIt3TLSSAM4ss3bwEx3EU8Ss87Va3TIAI4GU4yWcrc,1480
7
- python_iterutils-0.3.0.dist-info/WHEEL,sha256=EGEvSphFYqXKs23-kQBeyNoJP1nrT8ZJKQoi5p5DYL8,88
8
- python_iterutils-0.3.0.dist-info/licenses/LICENSE,sha256=o5242_N2TgDsWwFhPn7yr8YJNF7XsJM5NxUMtcT97bc,1100
9
- python_iterutils-0.3.0.dist-info/RECORD,,