queutils 0.8.4__py3-none-any.whl → 0.9.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- queutils/__init__.py +5 -0
- queutils/asyncqueue.py +3 -5
- queutils/categorycounterqueue.py +116 -0
- queutils/iterablequeue.py +14 -13
- {queutils-0.8.4.dist-info → queutils-0.9.1.dist-info}/METADATA +4 -2
- queutils-0.9.1.dist-info/RECORD +11 -0
- {queutils-0.8.4.dist-info → queutils-0.9.1.dist-info}/WHEEL +1 -1
- queutils-0.8.4.dist-info/RECORD +0 -10
- {queutils-0.8.4.dist-info → queutils-0.9.1.dist-info}/licenses/LICENSE +0 -0
queutils/__init__.py
CHANGED
@@ -2,10 +2,15 @@ from .countable import Countable as Countable
|
|
2
2
|
from .asyncqueue import AsyncQueue as AsyncQueue
|
3
3
|
from .iterablequeue import IterableQueue as IterableQueue, QueueDone as QueueDone
|
4
4
|
from .filequeue import FileQueue as FileQueue
|
5
|
+
from .categorycounterqueue import (
|
6
|
+
QCounter as QCounter,
|
7
|
+
CategoryCounterQueue as CategoryCounterQueue,
|
8
|
+
)
|
5
9
|
|
6
10
|
__all__ = [
|
7
11
|
"asyncqueue",
|
8
12
|
"countable",
|
13
|
+
"categorycounterqueue",
|
9
14
|
"filequeue",
|
10
15
|
"iterablequeue",
|
11
16
|
]
|
queutils/asyncqueue.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -----------------------------------------------------------
|
2
2
|
# Class AsyncQueue(asyncio.Queue, Generic[T])
|
3
3
|
#
|
4
|
-
# asyncio wrapper for non-async queue.Queue. Can be used to create
|
4
|
+
# asyncio wrapper for non-async queue.Queue. Can be used to create
|
5
5
|
# an asyncio.Queue out of a (non-async) multiprocessing.Queue.
|
6
6
|
#
|
7
7
|
# -----------------------------------------------------------
|
@@ -10,7 +10,6 @@ __author__ = "Jylpah"
|
|
10
10
|
__copyright__ = "Copyright 2024, Jylpah <Jylpah@gmail.com>"
|
11
11
|
__credits__ = ["Jylpah"]
|
12
12
|
__license__ = "MIT"
|
13
|
-
# __version__ = "1.0"
|
14
13
|
__maintainer__ = "Jylpah"
|
15
14
|
__email__ = "Jylpah@gmail.com"
|
16
15
|
__status__ = "Production"
|
@@ -35,18 +34,17 @@ error = logger.error
|
|
35
34
|
|
36
35
|
class AsyncQueue(asyncio.Queue, Generic[T]):
|
37
36
|
"""
|
38
|
-
Async wrapper for non-async queue.Queue. Can be used to create
|
37
|
+
Async wrapper for non-async queue.Queue. Can be used to create
|
39
38
|
an asyncio.Queue out of a (non-async) multiprocessing.Queue.
|
40
39
|
"""
|
41
40
|
|
42
|
-
def __init__(self, queue: Queue[T], asleep: float = 0.
|
41
|
+
def __init__(self, queue: Queue[T], asleep: float = 0.001):
|
43
42
|
self._Q: Queue[T] = queue
|
44
43
|
# self._maxsize: int = queue.maxsize
|
45
44
|
self._done: int = 0
|
46
45
|
self._items: int = 0
|
47
46
|
self._sleep: float = asleep
|
48
47
|
|
49
|
-
|
50
48
|
@property
|
51
49
|
def maxsize(self) -> int:
|
52
50
|
"""not supported by queue.Queue"""
|
@@ -0,0 +1,116 @@
|
|
1
|
+
from asyncio import Queue
|
2
|
+
from typing import TypeVar
|
3
|
+
from deprecated import deprecated
|
4
|
+
from .countable import Countable
|
5
|
+
from .iterablequeue import IterableQueue, QueueDone
|
6
|
+
from collections import defaultdict
|
7
|
+
import logging
|
8
|
+
|
9
|
+
logger = logging.getLogger()
|
10
|
+
error = logger.error
|
11
|
+
message = logger.warning
|
12
|
+
verbose = logger.info
|
13
|
+
debug = logger.debug
|
14
|
+
|
15
|
+
###########################################
|
16
|
+
#
|
17
|
+
# class CounterQueue
|
18
|
+
#
|
19
|
+
###########################################
|
20
|
+
T = TypeVar("T")
|
21
|
+
|
22
|
+
|
23
|
+
@deprecated(version="0.9.1", reason="Use CategoryCounterQueue instead")
|
24
|
+
class CounterQueue(Queue[T], Countable):
|
25
|
+
"""
|
26
|
+
CounterQueue is a asyncio.Queue for counting items
|
27
|
+
"""
|
28
|
+
|
29
|
+
_counter: int
|
30
|
+
_count_items: bool
|
31
|
+
_batch: int
|
32
|
+
|
33
|
+
def __init__(
|
34
|
+
self, *args, count_items: bool = True, batch: int = 1, **kwargs
|
35
|
+
) -> None:
|
36
|
+
super().__init__(*args, **kwargs)
|
37
|
+
self._counter = 0
|
38
|
+
self._count_items = count_items
|
39
|
+
self._batch = batch
|
40
|
+
|
41
|
+
def task_done(self) -> None:
|
42
|
+
super().task_done()
|
43
|
+
if self._count_items:
|
44
|
+
self._counter += 1
|
45
|
+
return None
|
46
|
+
|
47
|
+
@property
|
48
|
+
def count(self) -> int:
|
49
|
+
"""Return number of completed tasks"""
|
50
|
+
return self._counter * self._batch
|
51
|
+
|
52
|
+
@property
|
53
|
+
def count_items(self) -> bool:
|
54
|
+
"""Whether or not count items"""
|
55
|
+
return self._count_items
|
56
|
+
|
57
|
+
|
58
|
+
class CategoryCounterQueue(IterableQueue[tuple[str, int]]):
|
59
|
+
"""
|
60
|
+
CategorySummerQueue is a asyncio.Queue for summing up values by category
|
61
|
+
"""
|
62
|
+
|
63
|
+
_counter: defaultdict[str, int]
|
64
|
+
_batch: int
|
65
|
+
|
66
|
+
def __init__(self, *args, batch: int = 1, **kwargs) -> None:
|
67
|
+
super().__init__(*args, **kwargs)
|
68
|
+
self._batch = batch
|
69
|
+
self._counter = defaultdict(int)
|
70
|
+
|
71
|
+
async def receive(self) -> tuple[str, int]:
|
72
|
+
"""Receive a category value from the queue and sum it"""
|
73
|
+
category: str
|
74
|
+
value: int
|
75
|
+
category, value = await super().get()
|
76
|
+
self._counter[category] += value
|
77
|
+
super().task_done()
|
78
|
+
return (category, value)
|
79
|
+
|
80
|
+
async def send(self, category: str = "count", value: int = 1) -> None:
|
81
|
+
"""Send count of a category"""
|
82
|
+
await super().put((category, value))
|
83
|
+
return None
|
84
|
+
|
85
|
+
def get_count(self, category: str = "count") -> int:
|
86
|
+
"""Return count of a category"""
|
87
|
+
return self._counter[category]
|
88
|
+
|
89
|
+
def get_counts(self) -> defaultdict[str, int]:
|
90
|
+
"""Return counts of all categories"""
|
91
|
+
return self._counter
|
92
|
+
|
93
|
+
async def listen(self) -> defaultdict[str, int]:
|
94
|
+
"""Listen for category values"""
|
95
|
+
try:
|
96
|
+
while True:
|
97
|
+
await self.receive()
|
98
|
+
except QueueDone:
|
99
|
+
pass
|
100
|
+
return self.get_counts()
|
101
|
+
|
102
|
+
|
103
|
+
class QCounter:
|
104
|
+
def __init__(self, Q: Queue[int]):
|
105
|
+
self._count = 0
|
106
|
+
self._Q: Queue[int] = Q
|
107
|
+
|
108
|
+
@property
|
109
|
+
def count(self) -> int:
|
110
|
+
return self._count
|
111
|
+
|
112
|
+
async def start(self) -> None:
|
113
|
+
"""Read and count items from Q"""
|
114
|
+
while True:
|
115
|
+
self._count += await self._Q.get()
|
116
|
+
self._Q.task_done()
|
queutils/iterablequeue.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# -----------------------------------------------------------
|
2
2
|
# Class IterableQueue(asyncio.Queue[T], AsyncIterable[T], Countable)
|
3
3
|
#
|
4
|
-
# IterableQueue is asyncio.Queue subclass that can be iterated asynchronusly.
|
4
|
+
# IterableQueue is asyncio.Queue subclass that can be iterated asynchronusly.
|
5
5
|
# IterableQueue terminates automatically when the queue has been
|
6
6
|
# filled and emptied.
|
7
7
|
#
|
@@ -23,7 +23,7 @@ from .countable import Countable
|
|
23
23
|
import logging
|
24
24
|
|
25
25
|
# Setup logging
|
26
|
-
logger = logging.getLogger()
|
26
|
+
logger = logging.getLogger(__name__)
|
27
27
|
error = logger.error
|
28
28
|
message = logger.warning
|
29
29
|
verbose = logger.info
|
@@ -31,24 +31,26 @@ debug = logger.debug
|
|
31
31
|
|
32
32
|
T = TypeVar("T")
|
33
33
|
|
34
|
+
|
34
35
|
class QueueDone(Exception):
|
35
36
|
"""
|
36
37
|
Exception to mark an IterableQueue as filled and emptied i.e. done
|
37
38
|
"""
|
39
|
+
|
38
40
|
pass
|
39
41
|
|
40
42
|
|
41
43
|
class IterableQueue(Queue[T], AsyncIterable[T], Countable):
|
42
44
|
"""
|
43
|
-
IterableQueue is asyncio.Queue subclass that can be iterated asynchronusly.
|
44
|
-
|
45
|
+
IterableQueue is asyncio.Queue subclass that can be iterated asynchronusly.
|
46
|
+
|
45
47
|
IterableQueue terminates automatically when the queue has been
|
46
48
|
filled and emptied. Supports:
|
47
49
|
- asyncio.Queue() interface, _nowait() methods are experimental
|
48
50
|
- AsyncIterable(): async for item in queue:
|
49
51
|
- Automatic termination of the consumers when the queue has been emptied with QueueDone exception
|
50
52
|
- Producers must be registered with add_producer() and they must notify the queue
|
51
|
-
with finish() once they have finished adding items
|
53
|
+
with finish() once they have finished adding items
|
52
54
|
- Countable interface to count number of items task_done() through 'count' property
|
53
55
|
- Countable property can be disabled with count_items=False. This is useful when you
|
54
56
|
want to sum the count of multiple IterableQueues
|
@@ -74,16 +76,16 @@ class IterableQueue(Queue[T], AsyncIterable[T], Countable):
|
|
74
76
|
|
75
77
|
@property
|
76
78
|
def is_filled(self) -> bool:
|
77
|
-
""""
|
78
|
-
True if all the producers finished filling the queue.
|
79
|
+
""" "
|
80
|
+
True if all the producers finished filling the queue.
|
79
81
|
New items cannot be added to a filled queue nor can new producers can be added.
|
80
82
|
"""
|
81
83
|
return self._filled.is_set()
|
82
|
-
|
84
|
+
|
83
85
|
@property
|
84
86
|
def is_done(self) -> bool:
|
85
87
|
"""
|
86
|
-
Has the queue been filled, emptied and all the items have been marked with task_done()
|
88
|
+
Has the queue been filled, emptied and all the items have been marked with task_done()
|
87
89
|
"""
|
88
90
|
return self.is_filled and self.empty() and not self.has_wip
|
89
91
|
|
@@ -91,7 +93,6 @@ class IterableQueue(Queue[T], AsyncIterable[T], Countable):
|
|
91
93
|
def maxsize(self) -> int:
|
92
94
|
return self._Q.maxsize
|
93
95
|
|
94
|
-
|
95
96
|
def full(self) -> bool:
|
96
97
|
"""
|
97
98
|
True if the queue is full
|
@@ -122,8 +123,8 @@ class IterableQueue(Queue[T], AsyncIterable[T], Countable):
|
|
122
123
|
@property
|
123
124
|
def wip(self) -> int:
|
124
125
|
"""
|
125
|
-
Number of items in progress i.e. items that have been
|
126
|
-
read from the queue, but not marked with task_done()
|
126
|
+
Number of items in progress i.e. items that have been
|
127
|
+
read from the queue, but not marked with task_done()
|
127
128
|
"""
|
128
129
|
return self._wip
|
129
130
|
|
@@ -236,7 +237,7 @@ class IterableQueue(Queue[T], AsyncIterable[T], Countable):
|
|
236
237
|
def get_nowait(self) -> T:
|
237
238
|
"""
|
238
239
|
Experimental asyncio.Queue.get_nowait() implementation
|
239
|
-
"""
|
240
|
+
"""
|
240
241
|
item: T | None = self._Q.get_nowait()
|
241
242
|
if item is None:
|
242
243
|
self._empty.set()
|
@@ -1,6 +1,6 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: queutils
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.9.1
|
4
4
|
Summary: Handy Python Queue utilies
|
5
5
|
Project-URL: Homepage, https://github.com/Jylpah/queutils
|
6
6
|
Project-URL: Bug Tracker, https://github.com/Jylpah/queutils/issues
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3
|
|
14
14
|
Classifier: Topic :: Software Development :: Libraries
|
15
15
|
Requires-Python: >=3.11
|
16
16
|
Requires-Dist: aioconsole>=0.6
|
17
|
+
Requires-Dist: deprecated>=1.2.18
|
17
18
|
Provides-Extra: dev
|
18
19
|
Requires-Dist: build>=0.10; extra == 'dev'
|
19
20
|
Requires-Dist: hatchling>=1.22.4; extra == 'dev'
|
@@ -25,6 +26,7 @@ Requires-Dist: pytest-datafiles>=3.0; extra == 'dev'
|
|
25
26
|
Requires-Dist: pytest-timeout>=2.2; extra == 'dev'
|
26
27
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
27
28
|
Requires-Dist: ruff>=0.1.9; extra == 'dev'
|
29
|
+
Requires-Dist: types-deprecated>=1.2.15; extra == 'dev'
|
28
30
|
Description-Content-Type: text/markdown
|
29
31
|
|
30
32
|
[](https://github.com/Jylpah/queutils/actions/workflows/python-package.yml) [](https://codecov.io/gh/Jylpah/queutils)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
queutils/__init__.py,sha256=buANyRlxO1x4G_EBE3tYFpPtZGSepJDKRkWl-zKct1M,453
|
2
|
+
queutils/asyncqueue.py,sha256=GZRmlWTBQoKzTf7xr4MI-qhNqvIiaNWswAxFokP91Lg,2789
|
3
|
+
queutils/categorycounterqueue.py,sha256=Ku5pAJXSsoNyUsuWkQe9w7SaPeiPwPHFMdZFqA0LtBw,3097
|
4
|
+
queutils/countable.py,sha256=YSi7ILf9CuB5Tm3T4UUMEFlveqzqcmomfqJAlLGHEz8,172
|
5
|
+
queutils/filequeue.py,sha256=q2ly9H-lSCq6xuOqT1IlWgyCVyLoZiKbc0NuzmkF4aw,5360
|
6
|
+
queutils/iterablequeue.py,sha256=ZXwa9040yTawL1DMMNBXgQKiYr32nmzuSbgi-raAtZc,8664
|
7
|
+
queutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
|
+
queutils-0.9.1.dist-info/METADATA,sha256=zT3br-kHUXjDGp2QhPxW_K-EvP212VUYWZNbjTb-MUA,4281
|
9
|
+
queutils-0.9.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
10
|
+
queutils-0.9.1.dist-info/licenses/LICENSE,sha256=J1zeIKU2JVQmhwO2hHQDK8WR6zjVZ-wX8r7ZlL45AbI,1063
|
11
|
+
queutils-0.9.1.dist-info/RECORD,,
|
queutils-0.8.4.dist-info/RECORD
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
queutils/__init__.py,sha256=MboAom80iQTQURz0liGv_f2ae2Np8YA5ORX1LwsNa50,311
|
2
|
-
queutils/asyncqueue.py,sha256=cZPrXJgRCK0iOtE6k-VTzA9kcDDg818h9v0cwYzm2g0,2813
|
3
|
-
queutils/countable.py,sha256=YSi7ILf9CuB5Tm3T4UUMEFlveqzqcmomfqJAlLGHEz8,172
|
4
|
-
queutils/filequeue.py,sha256=q2ly9H-lSCq6xuOqT1IlWgyCVyLoZiKbc0NuzmkF4aw,5360
|
5
|
-
queutils/iterablequeue.py,sha256=hygOtgRRuqaPebcK27Ixm_KOOW0MnlKmBNcYreQoWOA,8677
|
6
|
-
queutils/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
7
|
-
queutils-0.8.4.dist-info/METADATA,sha256=kIu1OwdfGdiStIcU4DjLjkCfbvtEHMxBirj16Ba98gc,4191
|
8
|
-
queutils-0.8.4.dist-info/WHEEL,sha256=uNdcs2TADwSd5pVaP0Z_kcjcvvTUklh2S7bxZMF8Uj0,87
|
9
|
-
queutils-0.8.4.dist-info/licenses/LICENSE,sha256=J1zeIKU2JVQmhwO2hHQDK8WR6zjVZ-wX8r7ZlL45AbI,1063
|
10
|
-
queutils-0.8.4.dist-info/RECORD,,
|
File without changes
|