reykit 1.0.0__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.
- reykit/__init__.py +41 -0
- reykit/rall.py +33 -0
- reykit/rcomm.py +431 -0
- reykit/rdata.py +395 -0
- reykit/rdll/__init__.py +17 -0
- reykit/rdll/rdll_inject.py +41 -0
- reykit/rdll/rdll_inject_core.py +202 -0
- reykit/remail.py +276 -0
- reykit/rexception.py +339 -0
- reykit/rimage.py +261 -0
- reykit/rlog.py +1061 -0
- reykit/rmonkey.py +341 -0
- reykit/rmultitask.py +871 -0
- reykit/rnumber.py +161 -0
- reykit/ros.py +1917 -0
- reykit/rrandom.py +351 -0
- reykit/rregex.py +293 -0
- reykit/rschedule.py +272 -0
- reykit/rstdout.py +356 -0
- reykit/rsystem.py +1180 -0
- reykit/rtable.py +511 -0
- reykit/rtext.py +458 -0
- reykit/rtime.py +678 -0
- reykit/rtype.py +106 -0
- reykit/rwrap.py +613 -0
- reykit/rzip.py +137 -0
- reykit-1.0.0.dist-info/METADATA +29 -0
- reykit-1.0.0.dist-info/RECORD +30 -0
- reykit-1.0.0.dist-info/WHEEL +5 -0
- reykit-1.0.0.dist-info/top_level.txt +1 -0
reykit/rmultitask.py
ADDED
@@ -0,0 +1,871 @@
|
|
1
|
+
# !/usr/bin/env python
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
"""
|
5
|
+
@Time : 2022-12-19 20:06:20
|
6
|
+
@Author : Rey
|
7
|
+
@Contact : reyxbo@163.com
|
8
|
+
@Explain : Multi task methods.
|
9
|
+
"""
|
10
|
+
|
11
|
+
|
12
|
+
from __future__ import annotations
|
13
|
+
from typing import Any, Optional, Literal, Union
|
14
|
+
from collections.abc import Callable, Iterable, Generator, Coroutine
|
15
|
+
from threading import RLock as TRLock, get_ident as threading_get_ident
|
16
|
+
from concurrent.futures import ThreadPoolExecutor, Future as CFuture, as_completed as concurrent_as_completed
|
17
|
+
from asyncio import (
|
18
|
+
Future as AFuture,
|
19
|
+
Queue as AQueue,
|
20
|
+
Lock as ALock,
|
21
|
+
run as asyncio_run,
|
22
|
+
gather as asyncio_gather,
|
23
|
+
iscoroutine
|
24
|
+
)
|
25
|
+
from asyncio.queues import QueueEmpty
|
26
|
+
from aiohttp import ClientSession, ClientResponse
|
27
|
+
|
28
|
+
from .rexception import throw, check_most_one, check_response_code
|
29
|
+
from .rtime import sleep, RTimeMark
|
30
|
+
from .rwrap import wrap_thread
|
31
|
+
|
32
|
+
|
33
|
+
__all__ = (
|
34
|
+
'async_run',
|
35
|
+
'async_request',
|
36
|
+
'RThreadLock',
|
37
|
+
'RAsyncLock',
|
38
|
+
'RThreadPool',
|
39
|
+
'RAsyncPool'
|
40
|
+
)
|
41
|
+
|
42
|
+
|
43
|
+
def async_run(*coroutines: Coroutine) -> list:
|
44
|
+
"""
|
45
|
+
Asynchronous run `Coroutine` instances.
|
46
|
+
|
47
|
+
Parameters
|
48
|
+
----------
|
49
|
+
coroutines : `Coroutine` instances.
|
50
|
+
|
51
|
+
Returns
|
52
|
+
-------
|
53
|
+
Run result list.
|
54
|
+
"""
|
55
|
+
|
56
|
+
|
57
|
+
# Define.
|
58
|
+
async def gather_coroutine() -> AFuture:
|
59
|
+
"""
|
60
|
+
Get `Future` instance.
|
61
|
+
|
62
|
+
Returns
|
63
|
+
-------
|
64
|
+
Future instance.
|
65
|
+
"""
|
66
|
+
|
67
|
+
# Gather.
|
68
|
+
future = await asyncio_gather(*coroutines)
|
69
|
+
|
70
|
+
return future
|
71
|
+
|
72
|
+
|
73
|
+
# Run.
|
74
|
+
result = asyncio_run(gather_coroutine())
|
75
|
+
|
76
|
+
return result
|
77
|
+
|
78
|
+
|
79
|
+
async def async_request(
|
80
|
+
url: str,
|
81
|
+
params: Optional[dict] = None,
|
82
|
+
data: Optional[Union[dict, str, bytes]] = None,
|
83
|
+
json: Optional[dict] = None,
|
84
|
+
headers: dict[str, str] = {},
|
85
|
+
timeout: Optional[float] = None,
|
86
|
+
proxy: Optional[str] = None,
|
87
|
+
method: Optional[Literal['get', 'post', 'put', 'patch', 'delete', 'options', 'head']] = None,
|
88
|
+
check: Union[bool, int, Iterable[int]] = False,
|
89
|
+
handler: Optional[Union[str, tuple[str], Callable[[ClientResponse], Union[Coroutine, Any]]]] = None
|
90
|
+
) -> Any:
|
91
|
+
"""
|
92
|
+
Get asynchronous `Coroutine` instance of send request.
|
93
|
+
|
94
|
+
Parameters
|
95
|
+
----------
|
96
|
+
url : Request URL.
|
97
|
+
params : Request URL add parameters.
|
98
|
+
data : Request body data. Conflict with parameter `json`.
|
99
|
+
- `dict`, Convert to `key=value&...`: format bytes.
|
100
|
+
Automatic set `Content-Type` to `application/x-www-form-urlencoded`.
|
101
|
+
- `dict and a certain value is 'bytes' type`: Key is parameter name and file name, value is file data.
|
102
|
+
Automatic set `Content-Type` to `multipart/form-data`.
|
103
|
+
- `str`: File path to read file bytes data.
|
104
|
+
Automatic set `Content-Type` to file media type, and `filename` to file name.
|
105
|
+
- `bytes`: File bytes data.
|
106
|
+
Automatic set `Content-Type` to file media type.
|
107
|
+
json : Request body data, convert to `JSON` format. Conflict with parameter `data`.
|
108
|
+
Automatic set `Content-Type` to `application/json`.
|
109
|
+
headers : Request header data.
|
110
|
+
timeout : Request maximun waiting time.
|
111
|
+
proxy : Proxy URL.
|
112
|
+
method : Request method.
|
113
|
+
- `None`: Automatic judge.
|
114
|
+
When parameter `data` or `json` not has value, then request method is `get`.
|
115
|
+
When parameter `data` or `json` has value, then request method is `post`.
|
116
|
+
- `Literal['get', 'post', 'put', 'patch', 'delete', 'options', 'head']`: Use this request method.
|
117
|
+
check : Check response code, and throw exception.
|
118
|
+
- `Literal[False]`: Not check.
|
119
|
+
- `Literal[True]`: Check if is between 200 and 299.
|
120
|
+
- `int`: Check if is this value.
|
121
|
+
- `Iterable`: Check if is in sequence.
|
122
|
+
|
123
|
+
handler : Response handler.
|
124
|
+
- `None`: Automatic handle.
|
125
|
+
`Response 'Content-Type' is 'application/json'`: Use `ClientResponse.json` method.
|
126
|
+
`Response 'Content-Type' is 'text/plain; charset=utf-8'`: Use `ClientResponse.text` method.
|
127
|
+
`Other`: Use `ClientResponse.read` method.
|
128
|
+
- `str`: Get this attribute.
|
129
|
+
`Callable`: Execute this method. When return `Coroutine`, then use `await` syntax execute `Coroutine`.
|
130
|
+
`Any`: Return this value.
|
131
|
+
- `tuple[str]`: Get these attribute.
|
132
|
+
`Callable`: Execute this method. When return `Coroutine`, then use `await` syntax execute `Coroutine`.
|
133
|
+
`Any`: Return this value.
|
134
|
+
- `Callable`, Execute this method. When return `Coroutine`, then use `await`: syntax execute `Coroutine`.
|
135
|
+
|
136
|
+
Returns
|
137
|
+
-------
|
138
|
+
Response handler result.
|
139
|
+
"""
|
140
|
+
|
141
|
+
# Check.
|
142
|
+
check_most_one(data, json)
|
143
|
+
|
144
|
+
# Handle parameter.
|
145
|
+
if method is None:
|
146
|
+
if data is None and json is None:
|
147
|
+
method = 'get'
|
148
|
+
else:
|
149
|
+
method = 'post'
|
150
|
+
|
151
|
+
# Session.
|
152
|
+
async with ClientSession() as session:
|
153
|
+
|
154
|
+
# Request.
|
155
|
+
async with session.request(
|
156
|
+
method,
|
157
|
+
url,
|
158
|
+
params=params,
|
159
|
+
data=data,
|
160
|
+
json=json,
|
161
|
+
headers=headers,
|
162
|
+
timeout=timeout,
|
163
|
+
proxy=proxy
|
164
|
+
) as response:
|
165
|
+
|
166
|
+
# Check code.
|
167
|
+
if check is not False:
|
168
|
+
if check is True:
|
169
|
+
range_ = None
|
170
|
+
else:
|
171
|
+
range_ = check
|
172
|
+
check_response_code(response.status, range_)
|
173
|
+
|
174
|
+
# Receive.
|
175
|
+
match handler:
|
176
|
+
|
177
|
+
## Auto.
|
178
|
+
case None:
|
179
|
+
match response.content_type:
|
180
|
+
case 'application/json':
|
181
|
+
result = await response.json()
|
182
|
+
case 'text/plain; charset=utf-8':
|
183
|
+
|
184
|
+
# Set encode type.
|
185
|
+
if response.get_encoding() == 'ISO-8859-1':
|
186
|
+
encoding = 'utf-8'
|
187
|
+
else:
|
188
|
+
encoding = None
|
189
|
+
|
190
|
+
result = await response.text(encoding=encoding)
|
191
|
+
case _:
|
192
|
+
result = await response.read()
|
193
|
+
|
194
|
+
## Attribute.
|
195
|
+
case str():
|
196
|
+
result = getattr(response, handler)
|
197
|
+
|
198
|
+
### Method.
|
199
|
+
if callable(result):
|
200
|
+
result = result()
|
201
|
+
|
202
|
+
#### Coroutine.
|
203
|
+
if iscoroutine(result):
|
204
|
+
result = await result
|
205
|
+
|
206
|
+
## Attributes.
|
207
|
+
case tuple():
|
208
|
+
result = []
|
209
|
+
for key in handler:
|
210
|
+
result_element = getattr(response, key)
|
211
|
+
|
212
|
+
### Method.
|
213
|
+
if callable(result_element):
|
214
|
+
result_element = result_element()
|
215
|
+
|
216
|
+
#### Coroutine.
|
217
|
+
if iscoroutine(result_element):
|
218
|
+
result_element = await result_element
|
219
|
+
|
220
|
+
result.append(result_element)
|
221
|
+
|
222
|
+
## Method.
|
223
|
+
case _ if callable(handler):
|
224
|
+
result = handler(response)
|
225
|
+
|
226
|
+
### Coroutine.
|
227
|
+
if iscoroutine(result):
|
228
|
+
result = await result
|
229
|
+
|
230
|
+
## Throw exception.
|
231
|
+
case _:
|
232
|
+
throw(TypeError, handler)
|
233
|
+
|
234
|
+
return result
|
235
|
+
|
236
|
+
|
237
|
+
class RThreadLock():
|
238
|
+
"""
|
239
|
+
Rey's `thread lock` type.
|
240
|
+
"""
|
241
|
+
|
242
|
+
|
243
|
+
def __init__(self) -> None:
|
244
|
+
"""
|
245
|
+
Build `thread lock` attributes.
|
246
|
+
"""
|
247
|
+
|
248
|
+
# Set attribute.
|
249
|
+
self.lock = TRLock()
|
250
|
+
self.acquire_thread_id: Optional[int] = None
|
251
|
+
|
252
|
+
|
253
|
+
def acquire(
|
254
|
+
self,
|
255
|
+
timeout: float = None
|
256
|
+
) -> bool:
|
257
|
+
"""
|
258
|
+
Wait and acquire thread lock.
|
259
|
+
|
260
|
+
Parameters
|
261
|
+
----------
|
262
|
+
timeout : Maximum wait seconds.
|
263
|
+
- `None`: Not limit.
|
264
|
+
- `float`: Use this value.
|
265
|
+
|
266
|
+
Returns
|
267
|
+
-------
|
268
|
+
Whether acquire success.
|
269
|
+
"""
|
270
|
+
|
271
|
+
# Handle parameter.
|
272
|
+
if timeout is None:
|
273
|
+
timeout = -1
|
274
|
+
|
275
|
+
# Acquire.
|
276
|
+
result = self.lock.acquire(timeout=timeout)
|
277
|
+
|
278
|
+
# Update attribute.
|
279
|
+
if result:
|
280
|
+
thread_id = threading_get_ident()
|
281
|
+
self.acquire_thread_id = thread_id
|
282
|
+
|
283
|
+
return result
|
284
|
+
|
285
|
+
|
286
|
+
def release(self) -> None:
|
287
|
+
"""
|
288
|
+
Release thread lock.
|
289
|
+
"""
|
290
|
+
|
291
|
+
# Release.
|
292
|
+
self.lock.release()
|
293
|
+
|
294
|
+
# Update attribute.
|
295
|
+
self.acquire_thread_id = None
|
296
|
+
|
297
|
+
|
298
|
+
def __call__(self) -> None:
|
299
|
+
"""
|
300
|
+
Automatic judge, wait and acquire thread lock, or release thread lock.
|
301
|
+
"""
|
302
|
+
|
303
|
+
# Release.
|
304
|
+
thread_id = threading_get_ident()
|
305
|
+
if thread_id == self.acquire_thread_id:
|
306
|
+
self.release()
|
307
|
+
|
308
|
+
# Acquire.
|
309
|
+
else:
|
310
|
+
self.acquire()
|
311
|
+
|
312
|
+
|
313
|
+
class RAsyncLock():
|
314
|
+
"""
|
315
|
+
Rey's `asynchronous lock` type.
|
316
|
+
"""
|
317
|
+
|
318
|
+
|
319
|
+
def __init__(self) -> None:
|
320
|
+
"""
|
321
|
+
Build `asynchronous lock` attributes.
|
322
|
+
"""
|
323
|
+
|
324
|
+
# Set attribute.
|
325
|
+
self.lock = ALock()
|
326
|
+
|
327
|
+
|
328
|
+
def acquire(
|
329
|
+
self,
|
330
|
+
timeout: float = None
|
331
|
+
) -> bool:
|
332
|
+
"""
|
333
|
+
Wait and acquire thread lock.
|
334
|
+
|
335
|
+
Parameters
|
336
|
+
----------
|
337
|
+
timeout : Maximum wait seconds.
|
338
|
+
- `None`: Not limit.
|
339
|
+
- `float`: Use this value.
|
340
|
+
|
341
|
+
Returns
|
342
|
+
-------
|
343
|
+
Whether acquire success.
|
344
|
+
"""
|
345
|
+
|
346
|
+
# Handle parameter.
|
347
|
+
if timeout is None:
|
348
|
+
timeout = -1
|
349
|
+
|
350
|
+
# Acquire.
|
351
|
+
result = self.lock.acquire()
|
352
|
+
|
353
|
+
return result
|
354
|
+
|
355
|
+
|
356
|
+
def release(self) -> None:
|
357
|
+
"""
|
358
|
+
Release thread lock.
|
359
|
+
"""
|
360
|
+
|
361
|
+
# Release.
|
362
|
+
self.lock.release()
|
363
|
+
|
364
|
+
|
365
|
+
class RThreadPool(object):
|
366
|
+
"""
|
367
|
+
Rey's `thread pool` type.
|
368
|
+
"""
|
369
|
+
|
370
|
+
|
371
|
+
def __init__(
|
372
|
+
self,
|
373
|
+
task: Callable,
|
374
|
+
*args: Any,
|
375
|
+
_max_workers: Optional[int] = None,
|
376
|
+
**kwargs: Any
|
377
|
+
) -> None:
|
378
|
+
"""
|
379
|
+
Build `thread pool` attributes.
|
380
|
+
|
381
|
+
Parameters
|
382
|
+
----------
|
383
|
+
task : Thread task.
|
384
|
+
args : Task default position arguments.
|
385
|
+
_max_workers : Maximum number of threads.
|
386
|
+
- `None`: Number of CPU + 4, 32 maximum.
|
387
|
+
- `int`: Use this value, no maximum limit.
|
388
|
+
kwargs : Task default keyword arguments.
|
389
|
+
"""
|
390
|
+
|
391
|
+
# Set attribute.
|
392
|
+
self.task = task
|
393
|
+
self.args = args
|
394
|
+
self.kwargs = kwargs
|
395
|
+
self.thread_pool = ThreadPoolExecutor(
|
396
|
+
_max_workers,
|
397
|
+
task.__name__
|
398
|
+
)
|
399
|
+
self.futures: list[CFuture] = []
|
400
|
+
|
401
|
+
|
402
|
+
def one(
|
403
|
+
self,
|
404
|
+
*args: Any,
|
405
|
+
**kwargs: Any
|
406
|
+
) -> CFuture:
|
407
|
+
"""
|
408
|
+
Add and start a task to the thread pool.
|
409
|
+
|
410
|
+
Parameters
|
411
|
+
----------
|
412
|
+
args : Task position arguments, after default position arguments.
|
413
|
+
kwargs : Task keyword arguments, after default keyword arguments.
|
414
|
+
|
415
|
+
Returns
|
416
|
+
-------
|
417
|
+
Task instance.
|
418
|
+
"""
|
419
|
+
|
420
|
+
# Set parameter.
|
421
|
+
func_args = (
|
422
|
+
*self.args,
|
423
|
+
*args
|
424
|
+
)
|
425
|
+
func_kwargs = {
|
426
|
+
**self.kwargs,
|
427
|
+
**kwargs
|
428
|
+
}
|
429
|
+
|
430
|
+
# Submit.
|
431
|
+
future = self.thread_pool.submit(
|
432
|
+
self.task,
|
433
|
+
*func_args,
|
434
|
+
**func_kwargs
|
435
|
+
)
|
436
|
+
|
437
|
+
# Save.
|
438
|
+
self.futures.append(future)
|
439
|
+
|
440
|
+
return future
|
441
|
+
|
442
|
+
|
443
|
+
def batch(
|
444
|
+
self,
|
445
|
+
*args: tuple,
|
446
|
+
**kwargs: tuple
|
447
|
+
) -> list[CFuture]:
|
448
|
+
"""
|
449
|
+
Add and start a batch of tasks to the thread pool.
|
450
|
+
parameters sequence will combine one by one, and discard excess parameters.
|
451
|
+
|
452
|
+
Parameters
|
453
|
+
----------
|
454
|
+
args : Sequence of task position arguments, after default position arguments.
|
455
|
+
kwargs : Sequence of task keyword arguments, after default keyword arguments.
|
456
|
+
|
457
|
+
Returns
|
458
|
+
-------
|
459
|
+
Task instance list.
|
460
|
+
|
461
|
+
Examples
|
462
|
+
--------
|
463
|
+
>>> func = lambda *args, **kwargs: print(args, kwargs)
|
464
|
+
>>> a = (1, 2)
|
465
|
+
>>> b = (3, 4, 5)
|
466
|
+
>>> c = (11, 12)
|
467
|
+
>>> d = (13, 14, 15)
|
468
|
+
>>> thread_pool = RThreadPool(func, 0, z=0)
|
469
|
+
>>> thread_pool.batch(a, b, c=c, d=d)
|
470
|
+
(0, 1, 3) {'z': 0, 'c': 11, 'd': 13}
|
471
|
+
(0, 2, 4) {'z': 0, 'c': 12, 'd': 14}
|
472
|
+
"""
|
473
|
+
|
474
|
+
# Combine.
|
475
|
+
args_zip = zip(*args)
|
476
|
+
kwargs_zip = zip(
|
477
|
+
*[
|
478
|
+
[
|
479
|
+
(key, value)
|
480
|
+
for value in values
|
481
|
+
]
|
482
|
+
for key, values in kwargs.items()
|
483
|
+
]
|
484
|
+
)
|
485
|
+
params_zip = zip(args_zip, kwargs_zip)
|
486
|
+
|
487
|
+
# Batch submit.
|
488
|
+
futures = [
|
489
|
+
self.one(*args_, **dict(kwargs_))
|
490
|
+
for args_, kwargs_ in params_zip
|
491
|
+
]
|
492
|
+
|
493
|
+
# Save.
|
494
|
+
self.futures.extend(futures)
|
495
|
+
|
496
|
+
return futures
|
497
|
+
|
498
|
+
|
499
|
+
def generate(
|
500
|
+
self,
|
501
|
+
timeout: Optional[float] = None
|
502
|
+
) -> Generator[CFuture]:
|
503
|
+
"""
|
504
|
+
Return the generator of added task instance.
|
505
|
+
|
506
|
+
Parameters
|
507
|
+
----------
|
508
|
+
timeout : Call generator maximum waiting seconds, timeout throw exception.
|
509
|
+
- `None`: Infinite.
|
510
|
+
- `float`: Set this seconds.
|
511
|
+
|
512
|
+
Returns
|
513
|
+
-------
|
514
|
+
Generator of added task instance.
|
515
|
+
"""
|
516
|
+
|
517
|
+
# Get parameter.
|
518
|
+
self.futures, futures = [], self.futures
|
519
|
+
|
520
|
+
# Build.
|
521
|
+
generator = concurrent_as_completed(
|
522
|
+
futures,
|
523
|
+
timeout
|
524
|
+
)
|
525
|
+
|
526
|
+
return generator
|
527
|
+
|
528
|
+
|
529
|
+
def repeat(
|
530
|
+
self,
|
531
|
+
number: int
|
532
|
+
) -> list[CFuture]:
|
533
|
+
"""
|
534
|
+
Add and start a batch of tasks to the thread pool, and only with default parameters.
|
535
|
+
|
536
|
+
Parameters
|
537
|
+
----------
|
538
|
+
number : Number of add.
|
539
|
+
|
540
|
+
Returns
|
541
|
+
-------
|
542
|
+
Task instance list.
|
543
|
+
"""
|
544
|
+
|
545
|
+
# Batch submit.
|
546
|
+
futures = [
|
547
|
+
self.one()
|
548
|
+
for _ in range(number)
|
549
|
+
]
|
550
|
+
|
551
|
+
# Save.
|
552
|
+
self.futures.extend(futures)
|
553
|
+
|
554
|
+
return futures
|
555
|
+
|
556
|
+
|
557
|
+
def join(self) -> None:
|
558
|
+
"""
|
559
|
+
Block until all tasks are done.
|
560
|
+
"""
|
561
|
+
|
562
|
+
# Generator.
|
563
|
+
generator = self.generate()
|
564
|
+
|
565
|
+
# Wait.
|
566
|
+
for _ in generator:
|
567
|
+
pass
|
568
|
+
|
569
|
+
|
570
|
+
def __iter__(self) -> Generator:
|
571
|
+
"""
|
572
|
+
Return the generator of task result.
|
573
|
+
|
574
|
+
Returns
|
575
|
+
-------
|
576
|
+
Generator of task result.
|
577
|
+
"""
|
578
|
+
|
579
|
+
# Generator.
|
580
|
+
generator = self.generate()
|
581
|
+
|
582
|
+
# Generate.
|
583
|
+
for future in generator:
|
584
|
+
yield future.result()
|
585
|
+
|
586
|
+
|
587
|
+
__call__ = one
|
588
|
+
|
589
|
+
|
590
|
+
__mul__ = repeat
|
591
|
+
|
592
|
+
|
593
|
+
class RAsyncPool(object):
|
594
|
+
"""
|
595
|
+
Rey's `asynchronous pool` type.
|
596
|
+
"""
|
597
|
+
|
598
|
+
|
599
|
+
def __init__(
|
600
|
+
self,
|
601
|
+
async_func: Callable[..., Coroutine],
|
602
|
+
*args: Any,
|
603
|
+
_max_async: int = 10,
|
604
|
+
_exc_handler: Optional[Callable] = None,
|
605
|
+
**kwargs: Any
|
606
|
+
) -> None:
|
607
|
+
"""
|
608
|
+
Build `asynchronous pool` attributes.
|
609
|
+
|
610
|
+
Parameters
|
611
|
+
----------
|
612
|
+
async_func : Function of create asynchronous `Coroutine`.
|
613
|
+
args : Function default position arguments.
|
614
|
+
_max_async : Maximum number of asynchronous.
|
615
|
+
_exc_handler : `Coroutine` execution exception handler, will return value.
|
616
|
+
kwargs : Function default keyword arguments.
|
617
|
+
"""
|
618
|
+
|
619
|
+
# Set attribute.
|
620
|
+
self.async_func = async_func
|
621
|
+
self.args = args
|
622
|
+
self.kwargs = kwargs
|
623
|
+
self.exc_handler = _exc_handler
|
624
|
+
self.queue_input: AQueue[tuple[tuple, dict]] = AQueue()
|
625
|
+
self.queue_output = AQueue()
|
626
|
+
self.queue_count = 0
|
627
|
+
|
628
|
+
# Start.
|
629
|
+
self._start_workers(_max_async)
|
630
|
+
|
631
|
+
|
632
|
+
@wrap_thread
|
633
|
+
def _start_workers(
|
634
|
+
self,
|
635
|
+
worker_n: int
|
636
|
+
) -> None:
|
637
|
+
"""
|
638
|
+
Start workers of execute asynchronous `Coroutine`.
|
639
|
+
|
640
|
+
Parameters
|
641
|
+
----------
|
642
|
+
worker_n : Number of execute asynchronous `Coroutine` workers.
|
643
|
+
"""
|
644
|
+
|
645
|
+
|
646
|
+
# Define.
|
647
|
+
async def async_worker() -> None:
|
648
|
+
"""
|
649
|
+
Worker of execute asynchronous `Coroutine`.
|
650
|
+
"""
|
651
|
+
|
652
|
+
# Loop.
|
653
|
+
while True:
|
654
|
+
|
655
|
+
# Get parameter.
|
656
|
+
args, kwargs = await self.queue_input.get()
|
657
|
+
|
658
|
+
# Execute.
|
659
|
+
try:
|
660
|
+
result = await self.async_func(*args, **kwargs)
|
661
|
+
|
662
|
+
# Handle exception.
|
663
|
+
except:
|
664
|
+
if self.exc_handler is not None:
|
665
|
+
result = self.exc_handler()
|
666
|
+
await self.queue_output.put(result)
|
667
|
+
|
668
|
+
## Count.
|
669
|
+
else:
|
670
|
+
self.queue_count -= 1
|
671
|
+
|
672
|
+
else:
|
673
|
+
await self.queue_output.put(result)
|
674
|
+
|
675
|
+
|
676
|
+
# Create.
|
677
|
+
coroutines = [
|
678
|
+
async_worker()
|
679
|
+
for _ in range(worker_n)
|
680
|
+
]
|
681
|
+
|
682
|
+
# Start.
|
683
|
+
async_run(*coroutines)
|
684
|
+
|
685
|
+
|
686
|
+
def one(
|
687
|
+
self,
|
688
|
+
*args: Any,
|
689
|
+
**kwargs: Any
|
690
|
+
) -> None:
|
691
|
+
"""
|
692
|
+
Add and start a task to the pool.
|
693
|
+
|
694
|
+
Parameters
|
695
|
+
----------
|
696
|
+
args : Function position arguments, after default position arguments.
|
697
|
+
kwargs : Function keyword arguments, after default keyword arguments.
|
698
|
+
"""
|
699
|
+
|
700
|
+
# Set parameter.
|
701
|
+
func_args = (
|
702
|
+
*self.args,
|
703
|
+
*args
|
704
|
+
)
|
705
|
+
func_kwargs = {
|
706
|
+
**self.kwargs,
|
707
|
+
**kwargs
|
708
|
+
}
|
709
|
+
item = (
|
710
|
+
func_args,
|
711
|
+
func_kwargs
|
712
|
+
)
|
713
|
+
|
714
|
+
# Count.
|
715
|
+
self.queue_count += 1
|
716
|
+
|
717
|
+
# Put.
|
718
|
+
self.queue_input.put_nowait(item)
|
719
|
+
|
720
|
+
|
721
|
+
def batch(
|
722
|
+
self,
|
723
|
+
*args: tuple,
|
724
|
+
**kwargs: tuple
|
725
|
+
) -> None:
|
726
|
+
"""
|
727
|
+
Add and start a batch of tasks to the pool.
|
728
|
+
parameters sequence will combine one by one, and discard excess parameters.
|
729
|
+
|
730
|
+
Parameters
|
731
|
+
----------
|
732
|
+
args : Sequence of function position arguments, after default position arguments.
|
733
|
+
kwargs : Sequence of function keyword arguments, after default keyword arguments.
|
734
|
+
|
735
|
+
Examples
|
736
|
+
--------
|
737
|
+
>>> async def func(*args, **kwargs):
|
738
|
+
... print(args, kwargs)
|
739
|
+
>>> a = (1, 2)
|
740
|
+
>>> b = (3, 4, 5)
|
741
|
+
>>> c = (11, 12)
|
742
|
+
>>> d = (13, 14, 15)
|
743
|
+
>>> async_pool = RAsyncPool(func, 0, z=0)
|
744
|
+
>>> async_pool.batch(a, b, c=c, d=d)
|
745
|
+
(0, 1, 3) {'z': 0, 'c': 11, 'd': 13}
|
746
|
+
(0, 2, 4) {'z': 0, 'c': 12, 'd': 14}
|
747
|
+
"""
|
748
|
+
|
749
|
+
# Combine.
|
750
|
+
args_zip = zip(*args)
|
751
|
+
kwargs_zip = zip(
|
752
|
+
*[
|
753
|
+
[
|
754
|
+
(key, value)
|
755
|
+
for value in values
|
756
|
+
]
|
757
|
+
for key, values in kwargs.items()
|
758
|
+
]
|
759
|
+
)
|
760
|
+
params_zip = zip(args_zip, kwargs_zip)
|
761
|
+
|
762
|
+
# Batch submit.
|
763
|
+
for args_, kwargs_ in params_zip:
|
764
|
+
self.one(*args_, **dict(kwargs_))
|
765
|
+
|
766
|
+
|
767
|
+
def repeat(
|
768
|
+
self,
|
769
|
+
number: int
|
770
|
+
) -> list[CFuture]:
|
771
|
+
"""
|
772
|
+
Add and start a batch of tasks to the pool, and only with default parameters.
|
773
|
+
|
774
|
+
Parameters
|
775
|
+
----------
|
776
|
+
number : Number of add.
|
777
|
+
"""
|
778
|
+
|
779
|
+
# Batch submit.
|
780
|
+
for _ in range(number):
|
781
|
+
self.one()
|
782
|
+
|
783
|
+
|
784
|
+
def get(
|
785
|
+
self,
|
786
|
+
timeout: Optional[float] = None
|
787
|
+
) -> Any:
|
788
|
+
"""
|
789
|
+
Get one execution result of asynchronous `Coroutine`, will block.
|
790
|
+
|
791
|
+
Parameters
|
792
|
+
----------
|
793
|
+
timeout : Maximum seconds of block.
|
794
|
+
|
795
|
+
Returns
|
796
|
+
-------
|
797
|
+
One execution result.
|
798
|
+
"""
|
799
|
+
|
800
|
+
# Set parameter.
|
801
|
+
if timeout is not None:
|
802
|
+
rtm = RTimeMark()
|
803
|
+
rtm()
|
804
|
+
|
805
|
+
# Loop.
|
806
|
+
while True:
|
807
|
+
|
808
|
+
# Judge.
|
809
|
+
if not self.queue_output.empty():
|
810
|
+
|
811
|
+
# Get.
|
812
|
+
try:
|
813
|
+
result = self.queue_output.get_nowait()
|
814
|
+
except QueueEmpty:
|
815
|
+
pass
|
816
|
+
else:
|
817
|
+
|
818
|
+
# Count.
|
819
|
+
self.queue_count -= 1
|
820
|
+
|
821
|
+
return result
|
822
|
+
|
823
|
+
# Timeout.
|
824
|
+
if timeout is not None:
|
825
|
+
rtm()
|
826
|
+
if rtm.total_spend > timeout:
|
827
|
+
throw(TimeoutError, timeout)
|
828
|
+
|
829
|
+
# Sleep.
|
830
|
+
sleep(0.01)
|
831
|
+
|
832
|
+
|
833
|
+
def join(self) -> None:
|
834
|
+
"""
|
835
|
+
Block until all asynchronous `Coroutine` are done.
|
836
|
+
"""
|
837
|
+
|
838
|
+
# Generate.
|
839
|
+
while True:
|
840
|
+
|
841
|
+
# Break.
|
842
|
+
if self.queue_count == 0:
|
843
|
+
break
|
844
|
+
|
845
|
+
self.get()
|
846
|
+
|
847
|
+
|
848
|
+
def __iter__(self) -> Generator:
|
849
|
+
"""
|
850
|
+
Return the generator of result of asynchronous `Coroutine`.
|
851
|
+
|
852
|
+
Returns
|
853
|
+
-------
|
854
|
+
Generator of result of asynchronous `Coroutine`.
|
855
|
+
"""
|
856
|
+
|
857
|
+
# Generate.
|
858
|
+
while True:
|
859
|
+
|
860
|
+
# Break.
|
861
|
+
if self.queue_count == 0:
|
862
|
+
break
|
863
|
+
|
864
|
+
result = self.get()
|
865
|
+
yield result
|
866
|
+
|
867
|
+
|
868
|
+
__call__ = one
|
869
|
+
|
870
|
+
|
871
|
+
__mul__ = repeat
|