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