reykit 1.1.12__py3-none-any.whl → 1.1.13__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/rmultitask.py CHANGED
@@ -10,363 +10,51 @@
10
10
 
11
11
 
12
12
  from __future__ import annotations
13
- from typing import Any, Literal, Self
14
- from collections.abc import Callable, Iterable, Iterator, Generator, Coroutine, AsyncIterator
13
+ from typing import Any, Literal, overload
14
+ from collections.abc import Callable, Iterable, Generator, Coroutine
15
15
  from threading import RLock as TRLock, get_ident as threading_get_ident
16
16
  from concurrent.futures import ThreadPoolExecutor, Future as CFuture, as_completed as concurrent_as_completed
17
+ from queue import Queue as QQueue
17
18
  from asyncio import (
18
19
  Future as AFuture,
19
- Queue as AQueue,
20
20
  Lock as ALock,
21
+ Task as ATask,
22
+ Queue as AQueue,
23
+ sleep as asyncio_sleep,
21
24
  run as asyncio_run,
22
25
  gather as asyncio_gather,
23
- iscoroutine
26
+ iscoroutine as asyncio_iscoroutine,
27
+ iscoroutinefunction as asyncio_iscoroutinefunction,
28
+ run_coroutine_threadsafe as asyncio_run_coroutine_threadsafe,
29
+ new_event_loop as asyncio_new_event_loop,
30
+ set_event_loop as asyncio_set_event_loop
24
31
  )
25
- from asyncio.queues import QueueEmpty
26
32
  from aiohttp import ClientSession, ClientResponse
27
33
 
28
34
  from .rexception import throw, check_most_one, check_response_code
29
- from .rtime import sleep, RTimeMark
35
+ from .rtime import randn, RTimeMark
36
+ from .rtype import T
30
37
  from .rwrap import wrap_thread
31
38
 
32
39
 
33
40
  __all__ = (
41
+ 'RThreadPool',
34
42
  'async_run',
43
+ 'async_sleep',
44
+ 'async_wait',
35
45
  'async_request',
36
- 'RThreadLock',
37
- 'RAsyncLock',
38
- 'RThreadPool',
39
- 'RAsyncPool',
40
- 'RAsyncIterator'
46
+ 'RAsyncPool'
41
47
  )
42
48
 
43
49
 
44
- def async_run(*coroutines: Coroutine) -> list:
45
- """
46
- Asynchronous run `Coroutine` instances.
47
-
48
- Parameters
49
- ----------
50
- coroutines : `Coroutine` instances.
51
-
52
- Returns
53
- -------
54
- Run result list.
55
- """
56
-
57
-
58
- # Define.
59
- async def gather_coroutine() -> AFuture:
60
- """
61
- Get `Future` instance.
62
-
63
- Returns
64
- -------
65
- Future instance.
66
- """
67
-
68
- # Gather.
69
- future = await asyncio_gather(*coroutines)
70
-
71
- return future
72
-
73
-
74
- # Run.
75
- result = asyncio_run(gather_coroutine())
76
-
77
- return result
78
-
79
-
80
- async def async_request(
81
- url: str,
82
- params: dict | None = None,
83
- data: dict | str | bytes | None = None,
84
- json: dict | None = None,
85
- headers: dict[str, str] = {},
86
- timeout: float | None = None,
87
- proxy: str | None = None,
88
- method: Literal['get', 'post', 'put', 'patch', 'delete', 'options', 'head'] | None = None,
89
- check: bool | int | Iterable[int] = False,
90
- handler: str | tuple[str] | Callable[[ClientResponse], Coroutine | Any] | None = None
91
- ) -> Any:
92
- """
93
- Get asynchronous `Coroutine` instance of send request.
94
-
95
- Parameters
96
- ----------
97
- url : Request URL.
98
- params : Request URL add parameters.
99
- data : Request body data. Conflict with parameter `json`.
100
- - `dict`, Convert to `key=value&...`: format bytes.
101
- Automatic set `Content-Type` to `application/x-www-form-urlencoded`.
102
- - `dict and a certain value is 'bytes' type`: Key is parameter name and file name, value is file data.
103
- Automatic set `Content-Type` to `multipart/form-data`.
104
- - `str`: File path to read file bytes data.
105
- Automatic set `Content-Type` to file media type, and `filename` to file name.
106
- - `bytes`: File bytes data.
107
- Automatic set `Content-Type` to file media type.
108
- json : Request body data, convert to `JSON` format. Conflict with parameter `data`.
109
- Automatic set `Content-Type` to `application/json`.
110
- headers : Request header data.
111
- timeout : Request maximun waiting time.
112
- proxy : Proxy URL.
113
- method : Request method.
114
- - `None`: Automatic judge.
115
- When parameter `data` or `json` not has value, then request method is `get`.
116
- When parameter `data` or `json` has value, then request method is `post`.
117
- - `Literal['get', 'post', 'put', 'patch', 'delete', 'options', 'head']`: Use this request method.
118
- check : Check response code, and throw exception.
119
- - `Literal[False]`: Not check.
120
- - `Literal[True]`: Check if is between 200 and 299.
121
- - `int`: Check if is this value.
122
- - `Iterable`: Check if is in sequence.
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: int | None = 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
50
  class RThreadPool(object):
366
51
  """
367
52
  Rey's `thread pool` type.
368
53
  """
369
54
 
55
+ Queue = QQueue
56
+ Lock = TRLock
57
+
370
58
 
371
59
  def __init__(
372
60
  self,
@@ -381,18 +69,18 @@ class RThreadPool(object):
381
69
  Parameters
382
70
  ----------
383
71
  task : Thread task.
384
- args : Task default position arguments.
72
+ args : ATask default position arguments.
385
73
  _max_workers : Maximum number of threads.
386
74
  - `None`: Number of CPU + 4, 32 maximum.
387
75
  - `int`: Use this value, no maximum limit.
388
- kwargs : Task default keyword arguments.
76
+ kwargs : ATask default keyword arguments.
389
77
  """
390
78
 
391
79
  # Set attribute.
392
80
  self.task = task
393
81
  self.args = args
394
82
  self.kwargs = kwargs
395
- self.thread_pool = ThreadPoolExecutor(
83
+ self.pool = ThreadPoolExecutor(
396
84
  _max_workers,
397
85
  task.__name__
398
86
  )
@@ -405,16 +93,16 @@ class RThreadPool(object):
405
93
  **kwargs: Any
406
94
  ) -> CFuture:
407
95
  """
408
- Add and start a task to the thread pool.
96
+ Start a task.
409
97
 
410
98
  Parameters
411
99
  ----------
412
- args : Task position arguments, after default position arguments.
413
- kwargs : Task keyword arguments, after default keyword arguments.
100
+ args : ATask position arguments, after default position arguments.
101
+ kwargs : ATask keyword arguments, after default keyword arguments.
414
102
 
415
103
  Returns
416
104
  -------
417
- Task instance.
105
+ ATask instance.
418
106
  """
419
107
 
420
108
  # Set parameter.
@@ -427,8 +115,8 @@ class RThreadPool(object):
427
115
  **kwargs
428
116
  }
429
117
 
430
- # Submit.
431
- future = self.thread_pool.submit(
118
+ # Add.
119
+ future = self.pool.submit(
432
120
  self.task,
433
121
  *func_args,
434
122
  **func_kwargs
@@ -446,7 +134,7 @@ class RThreadPool(object):
446
134
  **kwargs: tuple
447
135
  ) -> list[CFuture]:
448
136
  """
449
- Add and start a batch of tasks to the thread pool.
137
+ Batch start tasks.
450
138
  parameters sequence will combine one by one, and discard excess parameters.
451
139
 
452
140
  Parameters
@@ -456,7 +144,7 @@ class RThreadPool(object):
456
144
 
457
145
  Returns
458
146
  -------
459
- Task instance list.
147
+ ATask instance list.
460
148
 
461
149
  Examples
462
150
  --------
@@ -484,7 +172,7 @@ class RThreadPool(object):
484
172
  )
485
173
  params_zip = zip(args_zip, kwargs_zip)
486
174
 
487
- # Batch submit.
175
+ # Batch add.
488
176
  futures = [
489
177
  self.one(*args_, **dict(kwargs_))
490
178
  for args_, kwargs_ in params_zip
@@ -496,6 +184,34 @@ class RThreadPool(object):
496
184
  return futures
497
185
 
498
186
 
187
+ def repeat(
188
+ self,
189
+ number: int
190
+ ) -> list[CFuture]:
191
+ """
192
+ Batch start tasks, and only with default parameters.
193
+
194
+ Parameters
195
+ ----------
196
+ number : Number of add.
197
+
198
+ Returns
199
+ -------
200
+ ATask instance list.
201
+ """
202
+
203
+ # Batch add.
204
+ futures = [
205
+ self.one()
206
+ for _ in range(number)
207
+ ]
208
+
209
+ # Save.
210
+ self.futures.extend(futures)
211
+
212
+ return futures
213
+
214
+
499
215
  def generate(
500
216
  self,
501
217
  timeout: float | None = None
@@ -514,80 +230,439 @@ class RThreadPool(object):
514
230
  Generator of added task instance.
515
231
  """
516
232
 
517
- # Get parameter.
518
- self.futures, futures = [], self.futures
519
-
520
233
  # Build.
521
234
  generator = concurrent_as_completed(
522
- futures,
235
+ self.futures,
523
236
  timeout
524
237
  )
525
238
 
526
- return generator
239
+ return generator
240
+
241
+
242
+ def join(
243
+ self,
244
+ timeout: float | None = None
245
+ ) -> None:
246
+ """
247
+ Block until all tasks are done.
248
+
249
+ Parameters
250
+ ----------
251
+ timeout : Call generator maximum waiting seconds, timeout throw exception.
252
+ - `None`: Infinite.
253
+ - `float`: Set this seconds.
254
+ """
255
+
256
+ # Generator.
257
+ generator = self.generate(timeout)
258
+
259
+ # Wait.
260
+ for _ in generator:
261
+ pass
262
+
263
+
264
+ def __iter__(self) -> Generator:
265
+ """
266
+ Return the generator of task result.
267
+
268
+ Returns
269
+ -------
270
+ Generator of task result.
271
+ """
272
+
273
+ # Generator.
274
+ generator = self.generate()
275
+ self.futures.clear()
276
+
277
+ # Generate.
278
+ for future in generator:
279
+ yield future.result()
280
+
281
+
282
+ @property
283
+ def thread_id(self) -> int:
284
+ """
285
+ Get current thread ID.
286
+
287
+ Returns
288
+ -------
289
+ Current thread ID.
290
+ """
291
+
292
+ # Get.
293
+ thread_id = threading_get_ident()
294
+
295
+ return thread_id
296
+
297
+
298
+ __call__ = one
299
+
300
+
301
+ __mul__ = repeat
302
+
303
+
304
+ @overload
305
+ def async_run(
306
+ coroutine: Coroutine[Any, Any, T] | ATask[Any, Any, T] | Callable[[], Coroutine[Any, Any, T]],
307
+ *,
308
+ return_exceptions: bool = False
309
+ ) -> T: ...
310
+
311
+ @overload
312
+ def async_run(
313
+ *coroutines: Coroutine[Any, Any, T] | ATask[Any, Any, T] | Callable[[], Coroutine[Any, Any, T]],
314
+ return_exceptions: bool = False
315
+ ) -> list[T]: ...
316
+
317
+ def async_run(
318
+ *coroutines: Coroutine[Any, Any, T] | ATask[Any, Any, T] | Callable[[], Coroutine[Any, Any, T]],
319
+ return_exceptions: bool = False
320
+ ) -> T | list[T]:
321
+ """
322
+ Asynchronous run coroutines.
323
+
324
+ Parameters
325
+ ----------
326
+ coroutines : `Coroutine` instances or `ATask` instances or `Coroutine` function.
327
+ return_exceptions : Whether return exception instances, otherwise throw first exception.
328
+
329
+ Returns
330
+ -------
331
+ run results.
332
+ """
333
+
334
+ # Handle parameter.
335
+ coroutines = [
336
+ coroutine()
337
+ if asyncio_iscoroutinefunction(coroutine)
338
+ else coroutine
339
+ for coroutine in coroutines
340
+ ]
341
+
342
+ # Define.
343
+ async def async_run_coroutine() -> list:
344
+ """
345
+ Asynchronous run coroutines.
346
+
347
+ Returns
348
+ -------
349
+ Run result list.
350
+ """
351
+
352
+ # Gather.
353
+ results = await asyncio_gather(*coroutines, return_exceptions=return_exceptions)
354
+
355
+ return results
356
+
357
+
358
+ # Run.
359
+ coroutine = async_run_coroutine()
360
+ results = asyncio_run(coroutine)
361
+
362
+ # One.
363
+ if len(results) == 1:
364
+ results = results[0]
365
+
366
+ return results
367
+
368
+
369
+ @overload
370
+ async def async_sleep(
371
+ *,
372
+ precision: None = None
373
+ ) -> int: ...
374
+
375
+ @overload
376
+ async def async_sleep(
377
+ second: int,
378
+ *,
379
+ precision: None = None
380
+ ) -> int: ...
381
+
382
+ @overload
383
+ async def async_sleep(
384
+ low: int = 0,
385
+ high: int = 10,
386
+ *,
387
+ precision: None = None
388
+ ) -> int: ...
389
+
390
+ @overload
391
+ async def async_sleep(
392
+ *thresholds: float,
393
+ precision: None = None
394
+ ) -> float: ...
395
+
396
+ @overload
397
+ async def async_sleep(
398
+ *thresholds: float,
399
+ precision: Literal[0] = None
400
+ ) -> int: ...
401
+
402
+ @overload
403
+ async def async_sleep(
404
+ *thresholds: float,
405
+ precision: int = None
406
+ ) -> float: ...
407
+
408
+ async def async_sleep(
409
+ *thresholds: float,
410
+ precision: int | None = None
411
+ ) -> float:
412
+ """
413
+ Sleep random seconds, in the coroutine.
414
+
415
+ Parameters
416
+ ----------
417
+ thresholds : Low and high thresholds of random range, range contains thresholds.
418
+ - When `length is 0`, then low and high thresholds is `0` and `10`.
419
+ - When `length is 1`, then sleep this value.
420
+ - When `length is 2`, then low and high thresholds is `thresholds[0]` and `thresholds[1]`.
421
+
422
+ precision : Precision of random range, that is maximum decimal digits of sleep seconds.
423
+ - `None`: Set to Maximum decimal digits of element of parameter `thresholds`.
424
+ - `int`: Set to this value.
425
+
426
+ Returns
427
+ -------
428
+ Random seconds.
429
+ - When parameters `precision` is `0`, then return int.
430
+ - When parameters `precision` is `greater than 0`, then return float.
431
+ """
432
+
433
+ # Handle parameter.
434
+ if len(thresholds) == 1:
435
+ second = thresholds[0]
436
+ else:
437
+ second = randn(*thresholds, precision=precision)
438
+
439
+ # Sleep.
440
+ await asyncio_sleep(second)
441
+
442
+ return second
443
+
444
+
445
+ async def async_wait(
446
+ func: Callable[..., bool],
447
+ *args: Any,
448
+ _interval: float = 1,
449
+ _timeout: float | None = None,
450
+ _raising: bool = True,
451
+ **kwargs: Any
452
+ ) -> float | None:
453
+ """
454
+ Wait success.
455
+
456
+ Parameters
457
+ ----------
458
+ func : Function to be decorated, must return `bool` value.
459
+ args : Position arguments of decorated function.
460
+ _interval : Interval seconds.
461
+ _timeout : Timeout seconds, timeout throw exception.
462
+ - `None`: Infinite time.
463
+ - `float`: Use this time.
464
+ _raising : When timeout, whether throw exception, otherwise return None.
465
+ kwargs : Keyword arguments of decorated function.
466
+
467
+ Returns
468
+ -------
469
+ Total spend seconds or None.
470
+ """
471
+
472
+ # Set parameter.
473
+ rtm = RTimeMark()
474
+ rtm()
475
+
476
+ # Not set timeout.
477
+ if _timeout is None:
478
+
479
+ ## Wait.
480
+ while True:
481
+ success = func(*args, **kwargs)
482
+ if success: break
483
+ await async_sleep(_interval)
484
+
485
+ # Set timeout.
486
+ else:
487
+
488
+ ## Wait.
489
+ while True:
490
+ success = func(*args, **kwargs)
491
+ if success: break
492
+
493
+ ## Timeout.
494
+ rtm()
495
+ if rtm.total_spend > _timeout:
496
+
497
+ ### Throw exception.
498
+ if _raising:
499
+ throw(TimeoutError, _timeout)
500
+
501
+ return
502
+
503
+ ## Sleep.
504
+ await async_sleep(_interval)
505
+
506
+ ## Return.
507
+ rtm()
508
+ return rtm.total_spend
509
+
510
+
511
+ async def async_request(
512
+ url: str,
513
+ params: dict | None = None,
514
+ data: dict | str | bytes | None = None,
515
+ json: dict | None = None,
516
+ headers: dict[str, str] = {},
517
+ timeout: float | None = None,
518
+ proxy: str | None = None,
519
+ method: Literal['get', 'post', 'put', 'patch', 'delete', 'options', 'head'] | None = None,
520
+ check: bool | int | Iterable[int] = False,
521
+ handler: str | tuple[str] | Callable[[ClientResponse], Coroutine | Any] | None = None
522
+ ) -> Any:
523
+ """
524
+ Get asynchronous `Coroutine` instance of send request.
525
+
526
+ Parameters
527
+ ----------
528
+ url : Request URL.
529
+ params : Request URL add parameters.
530
+ data : Request body data. Conflict with parameter `json`.
531
+ - `dict`, Convert to `key=value&...`: format bytes.
532
+ Automatic set `Content-Type` to `application/x-www-form-urlencoded`.
533
+ - `dict and a certain value is 'bytes' type`: Key is parameter name and file name, value is file data.
534
+ Automatic set `Content-Type` to `multipart/form-data`.
535
+ - `str`: File path to read file bytes data.
536
+ Automatic set `Content-Type` to file media type, and `filename` to file name.
537
+ - `bytes`: File bytes data.
538
+ Automatic set `Content-Type` to file media type.
539
+ json : Request body data, convert to `JSON` format. Conflict with parameter `data`.
540
+ Automatic set `Content-Type` to `application/json`.
541
+ headers : Request header data.
542
+ timeout : Request maximun waiting time.
543
+ proxy : Proxy URL.
544
+ method : Request method.
545
+ - `None`: Automatic judge.
546
+ When parameter `data` or `json` not has value, then request method is `get`.
547
+ When parameter `data` or `json` has value, then request method is `post`.
548
+ - `Literal['get', 'post', 'put', 'patch', 'delete', 'options', 'head']`: Use this request method.
549
+ check : Check response code, and throw exception.
550
+ - `Literal[False]`: Not check.
551
+ - `Literal[True]`: Check if is between 200 and 299.
552
+ - `int`: Check if is this value.
553
+ - `Iterable`: Check if is in sequence.
554
+ handler : Response handler.
555
+ - `None`: Automatic handle.
556
+ `Response 'Content-Type' is 'application/json'`: Use `ClientResponse.json` method.
557
+ `Response 'Content-Type' is 'text/plain; charset=utf-8'`: Use `ClientResponse.text` method.
558
+ `Other`: Use `ClientResponse.read` method.
559
+ - `str`: Get this attribute.
560
+ `Callable`: Execute this method. When return `Coroutine`, then use `await` syntax execute `Coroutine`.
561
+ `Any`: Return this value.
562
+ - `tuple[str]`: Get these attribute.
563
+ `Callable`: Execute this method. When return `Coroutine`, then use `await` syntax execute `Coroutine`.
564
+ `Any`: Return this value.
565
+ - `Callable`, Execute this method. When return `Coroutine`, then use `await`: syntax execute `Coroutine`.
566
+
567
+ Returns
568
+ -------
569
+ Response handler result.
570
+ """
527
571
 
572
+ # Check.
573
+ check_most_one(data, json)
528
574
 
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.
575
+ # Handle parameter.
576
+ if method is None:
577
+ if data is None and json is None:
578
+ method = 'get'
579
+ else:
580
+ method = 'post'
535
581
 
536
- Parameters
537
- ----------
538
- number : Number of add.
582
+ # Session.
583
+ async with ClientSession() as session:
539
584
 
540
- Returns
541
- -------
542
- Task instance list.
543
- """
585
+ # Request.
586
+ async with session.request(
587
+ method,
588
+ url,
589
+ params=params,
590
+ data=data,
591
+ json=json,
592
+ headers=headers,
593
+ timeout=timeout,
594
+ proxy=proxy
595
+ ) as response:
544
596
 
545
- # Batch submit.
546
- futures = [
547
- self.one()
548
- for _ in range(number)
549
- ]
597
+ # Check code.
598
+ if check is not False:
599
+ if check is True:
600
+ range_ = None
601
+ else:
602
+ range_ = check
603
+ check_response_code(response.status, range_)
550
604
 
551
- # Save.
552
- self.futures.extend(futures)
605
+ # Receive.
606
+ match handler:
553
607
 
554
- return futures
608
+ ## Auto.
609
+ case None:
610
+ match response.content_type:
611
+ case 'application/json':
612
+ result = await response.json()
613
+ case 'text/plain; charset=utf-8':
555
614
 
615
+ # Set encode type.
616
+ if response.get_encoding() == 'ISO-8859-1':
617
+ encoding = 'utf-8'
618
+ else:
619
+ encoding = None
556
620
 
557
- def join(self) -> None:
558
- """
559
- Block until all tasks are done.
560
- """
621
+ result = await response.text(encoding=encoding)
622
+ case _:
623
+ result = await response.read()
561
624
 
562
- # Generator.
563
- generator = self.generate()
625
+ ## Attribute.
626
+ case str():
627
+ result = getattr(response, handler)
564
628
 
565
- # Wait.
566
- for _ in generator:
567
- pass
629
+ ### Method.
630
+ if callable(result):
631
+ result = result()
568
632
 
633
+ #### Coroutine.
634
+ if asyncio_iscoroutine(result):
635
+ result = await result
569
636
 
570
- def __iter__(self) -> Generator:
571
- """
572
- Return the generator of task result.
637
+ ## Attributes.
638
+ case tuple():
639
+ result = []
640
+ for key in handler:
641
+ result_element = getattr(response, key)
573
642
 
574
- Returns
575
- -------
576
- Generator of task result.
577
- """
643
+ ### Method.
644
+ if callable(result_element):
645
+ result_element = result_element()
578
646
 
579
- # Generator.
580
- generator = self.generate()
647
+ #### Coroutine.
648
+ if asyncio_iscoroutine(result_element):
649
+ result_element = await result_element
581
650
 
582
- # Generate.
583
- for future in generator:
584
- yield future.result()
651
+ result.append(result_element)
585
652
 
653
+ ## Method.
654
+ case _ if callable(handler):
655
+ result = handler(response)
586
656
 
587
- __call__ = one
657
+ ### Coroutine.
658
+ if asyncio_iscoroutine(result):
659
+ result = await result
588
660
 
661
+ ## Throw exception.
662
+ case _:
663
+ throw(TypeError, handler)
589
664
 
590
- __mul__ = repeat
665
+ return result
591
666
 
592
667
 
593
668
  class RAsyncPool(object):
@@ -595,13 +670,14 @@ class RAsyncPool(object):
595
670
  Rey's `asynchronous pool` type.
596
671
  """
597
672
 
673
+ Queue = AQueue
674
+ Lock = ALock
675
+
598
676
 
599
677
  def __init__(
600
678
  self,
601
- async_func: Callable[..., Coroutine],
679
+ task: Callable[..., Coroutine],
602
680
  *args: Any,
603
- _max_async: int = 10,
604
- _exc_handler: Callable | None = None,
605
681
  **kwargs: Any
606
682
  ) -> None:
607
683
  """
@@ -611,76 +687,31 @@ class RAsyncPool(object):
611
687
  ----------
612
688
  async_func : Function of create asynchronous `Coroutine`.
613
689
  args : Function default position arguments.
614
- _max_async : Maximum number of asynchronous.
615
- _exc_handler : `Coroutine` execution exception handler, will return value.
616
690
  kwargs : Function default keyword arguments.
617
691
  """
618
692
 
619
693
  # Set attribute.
620
- self.async_func = async_func
694
+ self.task = task
621
695
  self.args = args
622
696
  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
697
+ self.loop = asyncio_new_event_loop()
698
+ self.futures: list[AFuture] = []
627
699
 
628
700
  # Start.
629
- self._start_workers(_max_async)
701
+ self._start_loop()
630
702
 
631
703
 
632
704
  @wrap_thread
633
- def _start_workers(
634
- self,
635
- worker_n: int
636
- ) -> None:
705
+ def _start_loop(self) -> None:
637
706
  """
638
- Start workers of execute asynchronous `Coroutine`.
639
-
640
- Parameters
641
- ----------
642
- worker_n : Number of execute asynchronous `Coroutine` workers.
707
+ Start event loop.
643
708
  """
644
709
 
710
+ # Set.
711
+ asyncio_set_event_loop(self.loop)
645
712
 
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)
713
+ ## Start and block.
714
+ self.loop.run_forever()
684
715
 
685
716
 
686
717
  def one(
@@ -689,7 +720,7 @@ class RAsyncPool(object):
689
720
  **kwargs: Any
690
721
  ) -> None:
691
722
  """
692
- Add and start a task to the pool.
723
+ Start a task.
693
724
 
694
725
  Parameters
695
726
  ----------
@@ -706,16 +737,15 @@ class RAsyncPool(object):
706
737
  **self.kwargs,
707
738
  **kwargs
708
739
  }
709
- item = (
710
- func_args,
711
- func_kwargs
712
- )
713
740
 
714
- # Count.
715
- self.queue_count += 1
741
+ # Create.
742
+ coroutine = self.task(*func_args, **func_kwargs)
743
+
744
+ # Add.
745
+ future = asyncio_run_coroutine_threadsafe(coroutine, self.loop)
716
746
 
717
- # Put.
718
- self.queue_input.put_nowait(item)
747
+ # Save.
748
+ self.futures.append(future)
719
749
 
720
750
 
721
751
  def batch(
@@ -724,7 +754,7 @@ class RAsyncPool(object):
724
754
  **kwargs: tuple
725
755
  ) -> None:
726
756
  """
727
- Add and start a batch of tasks to the pool.
757
+ Batch start tasks.
728
758
  parameters sequence will combine one by one, and discard excess parameters.
729
759
 
730
760
  Parameters
@@ -759,7 +789,7 @@ class RAsyncPool(object):
759
789
  )
760
790
  params_zip = zip(args_zip, kwargs_zip)
761
791
 
762
- # Batch submit.
792
+ # Batch add.
763
793
  for args_, kwargs_ in params_zip:
764
794
  self.one(*args_, **dict(kwargs_))
765
795
 
@@ -769,167 +799,95 @@ class RAsyncPool(object):
769
799
  number: int
770
800
  ) -> list[CFuture]:
771
801
  """
772
- Add and start a batch of tasks to the pool, and only with default parameters.
802
+ Batch start tasks, and only with default parameters.
773
803
 
774
804
  Parameters
775
805
  ----------
776
806
  number : Number of add.
777
807
  """
778
808
 
779
- # Batch submit.
809
+ # Batch add.
780
810
  for _ in range(number):
781
811
  self.one()
782
812
 
783
813
 
784
- def get(
814
+ def generate(
785
815
  self,
786
816
  timeout: float | None = None
787
- ) -> Any:
817
+ ) -> Generator[CFuture]:
788
818
  """
789
- Get one execution result of asynchronous `Coroutine`, will block.
819
+ Return the generator of added task instance.
790
820
 
791
821
  Parameters
792
822
  ----------
793
- timeout : Maximum seconds of block.
823
+ timeout : Call generator maximum waiting seconds, timeout throw exception.
824
+ - `None`: Infinite.
825
+ - `float`: Set this seconds.
794
826
 
795
827
  Returns
796
828
  -------
797
- One execution result.
829
+ Generator of added task instance.
798
830
  """
799
831
 
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)
832
+ # Build.
833
+ generator = concurrent_as_completed(
834
+ self.futures,
835
+ timeout
836
+ )
828
837
 
829
- # Sleep.
830
- sleep(0.01)
838
+ return generator
831
839
 
832
840
 
833
- def join(self) -> None:
834
- """
835
- Block until all asynchronous `Coroutine` are done.
841
+ def join(
842
+ self,
843
+ timeout: float | None = None
844
+ ) -> None:
836
845
  """
846
+ Block until all tasks are done.
837
847
 
838
- # Generate.
839
- while True:
848
+ Parameters
849
+ ----------
850
+ timeout : Call generator maximum waiting seconds, timeout throw exception.
851
+ - `None`: Infinite.
852
+ - `float`: Set this seconds.
853
+ """
840
854
 
841
- # Break.
842
- if self.queue_count == 0:
843
- break
855
+ # Generator.
856
+ generator = self.generate(timeout)
844
857
 
845
- self.get()
858
+ # Wait.
859
+ for _ in generator:
860
+ pass
846
861
 
847
862
 
848
863
  def __iter__(self) -> Generator:
849
864
  """
850
- Return the generator of result of asynchronous `Coroutine`.
865
+ Return the generator of task result.
851
866
 
852
867
  Returns
853
868
  -------
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
872
-
873
-
874
- class RAsyncIterator(AsyncIterator):
875
- """
876
- Rey's `asynchronous iterator` type.
877
- """
878
-
879
-
880
- def __init__(
881
- self,
882
- task: Callable,
883
- args_iter: Iterable[Iterable] | None = None,
884
- kwargs_iter: Iterable[dict] | None = None
885
- ) -> None:
869
+ Generator of task result.
886
870
  """
887
- Build `asynchronous iterator` attributes.
888
871
 
889
- Parameters
890
- ----------
891
- task : Asynchronous task.
892
- args_iter : Iterable of task position arguments.
893
- kwargs_iter : Iterable of task keyword arguments.
894
- """
872
+ # Generator.
873
+ generator = self.generate()
874
+ self.futures.clear()
895
875
 
896
- # Set attribute.
897
- self.task = task
898
- if args_iter is not None:
899
- args_iter: Iterator[Iterable] = iter(args_iter)
900
- self.args_iter = args_iter
901
- if kwargs_iter is not None:
902
- kwargs_iter: Iterator[dict] = iter(kwargs_iter)
903
- self.kwargs_iter = kwargs_iter
876
+ # Generate.
877
+ for future in generator:
878
+ yield future.result()
904
879
 
905
880
 
906
- def __aiter__(self) -> Self:
881
+ def __del__(self) -> None:
907
882
  """
908
- Get asynchronous iterator.
883
+ End loop.
909
884
  """
910
885
 
911
- return self
912
-
913
-
914
- async def __anext__(self):
915
- """
916
- Get next value from asynchronous iterator.
917
- """
886
+ # Stop.
887
+ self.loop.stop()
918
888
 
919
- # Get parameter.
920
- args = ()
921
- kwargs = {}
922
889
 
923
- ## Next.
924
- try:
925
- if self.args_iter is not None:
926
- args = next(self.args_iter)
927
- if self.kwargs_iter is not None:
928
- kwargs = next(self.kwargs_iter)
929
- except StopIteration:
930
- raise StopAsyncIteration
890
+ __call__ = one
931
891
 
932
- # Execute.
933
- result = self.task(*args, **kwargs)
934
892
 
935
- return result
893
+ __mul__ = repeat