priomesh-python 0.1.0__tar.gz → 0.2.0__tar.gz
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.
- {priomesh_python-0.1.0/src/priomesh_python.egg-info → priomesh_python-0.2.0}/PKG-INFO +47 -6
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/README.md +46 -5
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/pyproject.toml +1 -1
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/src/priomesh/queue.py +42 -3
- {priomesh_python-0.1.0 → priomesh_python-0.2.0/src/priomesh_python.egg-info}/PKG-INFO +47 -6
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/tests/test_queue.py +163 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/LICENSE +0 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/setup.cfg +0 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/src/priomesh/__init__.py +0 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/src/priomesh/models.py +0 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/src/priomesh/policies/__init__.py +0 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/src/priomesh/policies/priority.py +0 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/src/priomesh/policies/subqueue.py +0 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/src/priomesh_python.egg-info/SOURCES.txt +0 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/src/priomesh_python.egg-info/dependency_links.txt +0 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/src/priomesh_python.egg-info/top_level.txt +0 -0
- {priomesh_python-0.1.0 → priomesh_python-0.2.0}/tests/test_policy.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: priomesh-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A pluggable priority and fair-queue scheduling library
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -61,14 +61,13 @@ cd priomesh-python
|
|
|
61
61
|
python -m pip install -e .
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
with:
|
|
64
|
+
Install the latest published release from PyPI:
|
|
66
65
|
|
|
67
66
|
```bash
|
|
68
67
|
python -m pip install priomesh-python
|
|
69
68
|
```
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
To install directly from the GitHub repository instead:
|
|
72
71
|
|
|
73
72
|
```bash
|
|
74
73
|
python -m pip install "git+https://github.com/PrioMesh/priomesh-python.git"
|
|
@@ -108,11 +107,53 @@ sub-queue within that level. Jobs within each priority/sub-queue pair are
|
|
|
108
107
|
returned in FIFO order. Calling `get()` on an empty queue waits until a job is
|
|
109
108
|
put; `empty()` checks whether the queue currently has any jobs.
|
|
110
109
|
|
|
110
|
+
By default, the queue is unbounded. Pass a positive `maxsize` to limit the
|
|
111
|
+
number of jobs waiting in the queue across all priorities and sub-queues.
|
|
112
|
+
When it is full, `put()` waits until a `get()` removes a job and frees a slot.
|
|
113
|
+
`maxsize=0` keeps the queue unbounded; negative values raise `ValueError`.
|
|
114
|
+
|
|
111
115
|
Call `shutdown()` to stop accepting jobs. It performs a graceful shutdown:
|
|
112
116
|
`put()` raises `QueueShutDown`, while `get()` continues returning jobs that
|
|
113
117
|
were already queued. Once the queue is drained, waiting and future `get()`
|
|
114
|
-
calls raise `QueueShutDown`, allowing consumers to exit.
|
|
115
|
-
|
|
118
|
+
calls raise `QueueShutDown`, allowing consumers to exit. A `put()` waiting
|
|
119
|
+
because the queue is full is also woken by shutdown and raises
|
|
120
|
+
`QueueShutDown`. The example above shows a consumer draining the queue and
|
|
121
|
+
stopping cleanly.
|
|
122
|
+
|
|
123
|
+
`put()` and `get()` accept an optional `timeout` in seconds. The default
|
|
124
|
+
`None` waits without a deadline; otherwise the timeout must be a finite
|
|
125
|
+
positive number. Zero, negative, `NaN`, and infinite timeout values raise
|
|
126
|
+
`ValueError`. The timeout is implemented with
|
|
127
|
+
`asyncio.wait_for()`, so an expired timeout raises `asyncio.TimeoutError`
|
|
128
|
+
(`TimeoutError` on Python 3.11 and newer). Cancelling the task awaiting either
|
|
129
|
+
operation also cancels the operation. If the operation is cancelled while
|
|
130
|
+
waiting for a job or for queue capacity, it does not remove or add a job.
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import asyncio
|
|
134
|
+
from priomesh import Job, Priority, PriorityFairQueue
|
|
135
|
+
|
|
136
|
+
async def main() -> None:
|
|
137
|
+
queue = PriorityFairQueue()
|
|
138
|
+
try:
|
|
139
|
+
await queue.get(timeout=1.0)
|
|
140
|
+
except asyncio.TimeoutError:
|
|
141
|
+
print("No job arrived before the timeout")
|
|
142
|
+
|
|
143
|
+
bounded_queue = PriorityFairQueue(maxsize=1)
|
|
144
|
+
await bounded_queue.put(Job("queued", Priority.NORMAL))
|
|
145
|
+
try:
|
|
146
|
+
await bounded_queue.put(Job("waiting", Priority.NORMAL), timeout=1.0)
|
|
147
|
+
except asyncio.TimeoutError:
|
|
148
|
+
print("No capacity became available before the timeout")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
asyncio.run(main())
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
For manual cancellation, create a task for `put()` or `get()` and call its
|
|
155
|
+
`cancel()` method. Timeout and cancellation handling use `asyncio`; they do
|
|
156
|
+
not use a separate queue-specific cancellation API.
|
|
116
157
|
|
|
117
158
|
## Scheduling policies
|
|
118
159
|
|
|
@@ -20,14 +20,13 @@ cd priomesh-python
|
|
|
20
20
|
python -m pip install -e .
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
with:
|
|
23
|
+
Install the latest published release from PyPI:
|
|
25
24
|
|
|
26
25
|
```bash
|
|
27
26
|
python -m pip install priomesh-python
|
|
28
27
|
```
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
To install directly from the GitHub repository instead:
|
|
31
30
|
|
|
32
31
|
```bash
|
|
33
32
|
python -m pip install "git+https://github.com/PrioMesh/priomesh-python.git"
|
|
@@ -67,11 +66,53 @@ sub-queue within that level. Jobs within each priority/sub-queue pair are
|
|
|
67
66
|
returned in FIFO order. Calling `get()` on an empty queue waits until a job is
|
|
68
67
|
put; `empty()` checks whether the queue currently has any jobs.
|
|
69
68
|
|
|
69
|
+
By default, the queue is unbounded. Pass a positive `maxsize` to limit the
|
|
70
|
+
number of jobs waiting in the queue across all priorities and sub-queues.
|
|
71
|
+
When it is full, `put()` waits until a `get()` removes a job and frees a slot.
|
|
72
|
+
`maxsize=0` keeps the queue unbounded; negative values raise `ValueError`.
|
|
73
|
+
|
|
70
74
|
Call `shutdown()` to stop accepting jobs. It performs a graceful shutdown:
|
|
71
75
|
`put()` raises `QueueShutDown`, while `get()` continues returning jobs that
|
|
72
76
|
were already queued. Once the queue is drained, waiting and future `get()`
|
|
73
|
-
calls raise `QueueShutDown`, allowing consumers to exit.
|
|
74
|
-
|
|
77
|
+
calls raise `QueueShutDown`, allowing consumers to exit. A `put()` waiting
|
|
78
|
+
because the queue is full is also woken by shutdown and raises
|
|
79
|
+
`QueueShutDown`. The example above shows a consumer draining the queue and
|
|
80
|
+
stopping cleanly.
|
|
81
|
+
|
|
82
|
+
`put()` and `get()` accept an optional `timeout` in seconds. The default
|
|
83
|
+
`None` waits without a deadline; otherwise the timeout must be a finite
|
|
84
|
+
positive number. Zero, negative, `NaN`, and infinite timeout values raise
|
|
85
|
+
`ValueError`. The timeout is implemented with
|
|
86
|
+
`asyncio.wait_for()`, so an expired timeout raises `asyncio.TimeoutError`
|
|
87
|
+
(`TimeoutError` on Python 3.11 and newer). Cancelling the task awaiting either
|
|
88
|
+
operation also cancels the operation. If the operation is cancelled while
|
|
89
|
+
waiting for a job or for queue capacity, it does not remove or add a job.
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
import asyncio
|
|
93
|
+
from priomesh import Job, Priority, PriorityFairQueue
|
|
94
|
+
|
|
95
|
+
async def main() -> None:
|
|
96
|
+
queue = PriorityFairQueue()
|
|
97
|
+
try:
|
|
98
|
+
await queue.get(timeout=1.0)
|
|
99
|
+
except asyncio.TimeoutError:
|
|
100
|
+
print("No job arrived before the timeout")
|
|
101
|
+
|
|
102
|
+
bounded_queue = PriorityFairQueue(maxsize=1)
|
|
103
|
+
await bounded_queue.put(Job("queued", Priority.NORMAL))
|
|
104
|
+
try:
|
|
105
|
+
await bounded_queue.put(Job("waiting", Priority.NORMAL), timeout=1.0)
|
|
106
|
+
except asyncio.TimeoutError:
|
|
107
|
+
print("No capacity became available before the timeout")
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
asyncio.run(main())
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
For manual cancellation, create a task for `put()` or `get()` and call its
|
|
114
|
+
`cancel()` method. Timeout and cancellation handling use `asyncio`; they do
|
|
115
|
+
not use a separate queue-specific cancellation API.
|
|
75
116
|
|
|
76
117
|
## Scheduling policies
|
|
77
118
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import math
|
|
2
3
|
from collections import deque
|
|
3
4
|
from .models import Priority, SubQueue, Job
|
|
4
5
|
|
|
@@ -14,6 +15,11 @@ class QueueShutDown(Exception):
|
|
|
14
15
|
"""Raised when an operation is attempted on a shut-down queue."""
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
def _validate_timeout(timeout: float | None) -> None:
|
|
19
|
+
if timeout is not None and (not math.isfinite(timeout) or timeout <= 0):
|
|
20
|
+
raise ValueError("timeout must be a finite positive number or None")
|
|
21
|
+
|
|
22
|
+
|
|
17
23
|
class PriorityFairQueue:
|
|
18
24
|
"""Schedule jobs by priority and distribute service fairly among subqueues.
|
|
19
25
|
|
|
@@ -26,6 +32,7 @@ class PriorityFairQueue:
|
|
|
26
32
|
self,
|
|
27
33
|
priority_policy: PriorityPolicy | None = None,
|
|
28
34
|
subqueue_policy: SubQueuePolicy | None = None,
|
|
35
|
+
maxsize: int = 0,
|
|
29
36
|
) -> None:
|
|
30
37
|
self._priority_policy = priority_policy or RandomPriorityPolicy()
|
|
31
38
|
self._subqueue_policy = subqueue_policy or AgingWeightedRandomSubQueuePolicy()
|
|
@@ -34,11 +41,28 @@ class PriorityFairQueue:
|
|
|
34
41
|
self._condition = asyncio.Condition()
|
|
35
42
|
self._shutdown = False
|
|
36
43
|
|
|
44
|
+
if maxsize < 0:
|
|
45
|
+
raise ValueError("maxsize must be non-negative")
|
|
46
|
+
self._maxsize = maxsize
|
|
47
|
+
self._size = 0
|
|
48
|
+
|
|
37
49
|
def empty(self) -> bool:
|
|
38
50
|
return not self._rotation
|
|
39
51
|
|
|
40
|
-
async def put(self, job: Job) -> None:
|
|
52
|
+
async def put(self, job: Job, timeout: float | None = None) -> None:
|
|
53
|
+
_validate_timeout(timeout)
|
|
54
|
+
if timeout is None:
|
|
55
|
+
await self._put(job)
|
|
56
|
+
else:
|
|
57
|
+
await asyncio.wait_for(self._put(job), timeout=timeout)
|
|
58
|
+
|
|
59
|
+
async def _put(self, job: Job) -> None:
|
|
41
60
|
async with self._condition:
|
|
61
|
+
while self._maxsize > 0 and self._size >= self._maxsize:
|
|
62
|
+
if self._shutdown:
|
|
63
|
+
raise QueueShutDown("cannot put into a shut-down queue")
|
|
64
|
+
await self._condition.wait()
|
|
65
|
+
|
|
42
66
|
if self._shutdown:
|
|
43
67
|
raise QueueShutDown("cannot put into a shut-down queue")
|
|
44
68
|
level_queues = self._queues.setdefault(job.priority, {})
|
|
@@ -53,9 +77,16 @@ class PriorityFairQueue:
|
|
|
53
77
|
rotation.append(SubQueue(name=job.subqueue, waiting_rounds=0))
|
|
54
78
|
else:
|
|
55
79
|
subqueue.pending_jobs += 1
|
|
80
|
+
self._size += 1
|
|
56
81
|
self._condition.notify()
|
|
57
82
|
|
|
58
|
-
async def get(self) -> Job:
|
|
83
|
+
async def get(self, timeout: float | None = None) -> Job:
|
|
84
|
+
_validate_timeout(timeout)
|
|
85
|
+
if timeout is None:
|
|
86
|
+
return await self._get()
|
|
87
|
+
return await asyncio.wait_for(self._get(), timeout=timeout)
|
|
88
|
+
|
|
89
|
+
async def _get(self) -> Job:
|
|
59
90
|
async with self._condition:
|
|
60
91
|
while True:
|
|
61
92
|
active_priorities = [p for p, r in self._rotation.items() if r]
|
|
@@ -67,6 +98,8 @@ class PriorityFairQueue:
|
|
|
67
98
|
r.remove(subqueue)
|
|
68
99
|
jobs = self._queues[priority][subqueue.name]
|
|
69
100
|
job = jobs.popleft()
|
|
101
|
+
self._size -= 1
|
|
102
|
+
self._condition.notify_all()
|
|
70
103
|
subqueue.pending_jobs -= 1
|
|
71
104
|
if jobs:
|
|
72
105
|
r.append(subqueue)
|
|
@@ -85,4 +118,10 @@ class PriorityFairQueue:
|
|
|
85
118
|
async def shutdown(self) -> None:
|
|
86
119
|
async with self._condition:
|
|
87
120
|
self._shutdown = True
|
|
88
|
-
self._condition.notify_all() # 唤醒所有等待的协程,促使其检查关闭态并退出
|
|
121
|
+
self._condition.notify_all() # 唤醒所有等待的协程,促使其检查关闭态并退出
|
|
122
|
+
|
|
123
|
+
def qsize(self) -> int:
|
|
124
|
+
return self._size
|
|
125
|
+
|
|
126
|
+
def full(self) -> bool:
|
|
127
|
+
return self._maxsize > 0 and self._size >= self._maxsize
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: priomesh-python
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: A pluggable priority and fair-queue scheduling library
|
|
5
5
|
License: MIT License
|
|
6
6
|
|
|
@@ -61,14 +61,13 @@ cd priomesh-python
|
|
|
61
61
|
python -m pip install -e .
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
with:
|
|
64
|
+
Install the latest published release from PyPI:
|
|
66
65
|
|
|
67
66
|
```bash
|
|
68
67
|
python -m pip install priomesh-python
|
|
69
68
|
```
|
|
70
69
|
|
|
71
|
-
|
|
70
|
+
To install directly from the GitHub repository instead:
|
|
72
71
|
|
|
73
72
|
```bash
|
|
74
73
|
python -m pip install "git+https://github.com/PrioMesh/priomesh-python.git"
|
|
@@ -108,11 +107,53 @@ sub-queue within that level. Jobs within each priority/sub-queue pair are
|
|
|
108
107
|
returned in FIFO order. Calling `get()` on an empty queue waits until a job is
|
|
109
108
|
put; `empty()` checks whether the queue currently has any jobs.
|
|
110
109
|
|
|
110
|
+
By default, the queue is unbounded. Pass a positive `maxsize` to limit the
|
|
111
|
+
number of jobs waiting in the queue across all priorities and sub-queues.
|
|
112
|
+
When it is full, `put()` waits until a `get()` removes a job and frees a slot.
|
|
113
|
+
`maxsize=0` keeps the queue unbounded; negative values raise `ValueError`.
|
|
114
|
+
|
|
111
115
|
Call `shutdown()` to stop accepting jobs. It performs a graceful shutdown:
|
|
112
116
|
`put()` raises `QueueShutDown`, while `get()` continues returning jobs that
|
|
113
117
|
were already queued. Once the queue is drained, waiting and future `get()`
|
|
114
|
-
calls raise `QueueShutDown`, allowing consumers to exit.
|
|
115
|
-
|
|
118
|
+
calls raise `QueueShutDown`, allowing consumers to exit. A `put()` waiting
|
|
119
|
+
because the queue is full is also woken by shutdown and raises
|
|
120
|
+
`QueueShutDown`. The example above shows a consumer draining the queue and
|
|
121
|
+
stopping cleanly.
|
|
122
|
+
|
|
123
|
+
`put()` and `get()` accept an optional `timeout` in seconds. The default
|
|
124
|
+
`None` waits without a deadline; otherwise the timeout must be a finite
|
|
125
|
+
positive number. Zero, negative, `NaN`, and infinite timeout values raise
|
|
126
|
+
`ValueError`. The timeout is implemented with
|
|
127
|
+
`asyncio.wait_for()`, so an expired timeout raises `asyncio.TimeoutError`
|
|
128
|
+
(`TimeoutError` on Python 3.11 and newer). Cancelling the task awaiting either
|
|
129
|
+
operation also cancels the operation. If the operation is cancelled while
|
|
130
|
+
waiting for a job or for queue capacity, it does not remove or add a job.
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import asyncio
|
|
134
|
+
from priomesh import Job, Priority, PriorityFairQueue
|
|
135
|
+
|
|
136
|
+
async def main() -> None:
|
|
137
|
+
queue = PriorityFairQueue()
|
|
138
|
+
try:
|
|
139
|
+
await queue.get(timeout=1.0)
|
|
140
|
+
except asyncio.TimeoutError:
|
|
141
|
+
print("No job arrived before the timeout")
|
|
142
|
+
|
|
143
|
+
bounded_queue = PriorityFairQueue(maxsize=1)
|
|
144
|
+
await bounded_queue.put(Job("queued", Priority.NORMAL))
|
|
145
|
+
try:
|
|
146
|
+
await bounded_queue.put(Job("waiting", Priority.NORMAL), timeout=1.0)
|
|
147
|
+
except asyncio.TimeoutError:
|
|
148
|
+
print("No capacity became available before the timeout")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
asyncio.run(main())
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
For manual cancellation, create a task for `put()` or `get()` and call its
|
|
155
|
+
`cancel()` method. Timeout and cancellation handling use `asyncio`; they do
|
|
156
|
+
not use a separate queue-specific cancellation API.
|
|
116
157
|
|
|
117
158
|
## Scheduling policies
|
|
118
159
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import math
|
|
2
3
|
import unittest
|
|
3
4
|
|
|
4
5
|
from priomesh import Job, Priority, PriorityFairQueue, QueueShutDown
|
|
@@ -207,6 +208,168 @@ class PriorityFairQueueTests(unittest.IsolatedAsyncioTestCase):
|
|
|
207
208
|
with self.assertRaises(QueueShutDown):
|
|
208
209
|
await asyncio.wait_for(t, timeout=1)
|
|
209
210
|
|
|
211
|
+
async def test_maxsize_neg(self) -> None:
|
|
212
|
+
with self.assertRaises(ValueError):
|
|
213
|
+
PriorityFairQueue(maxsize=-1)
|
|
214
|
+
|
|
215
|
+
async def test_maxsize_zero(self) -> None:
|
|
216
|
+
queue = PriorityFairQueue(maxsize=0)
|
|
217
|
+
for i in range(10):
|
|
218
|
+
await queue.put(Job(f"job-{i}", Priority.NORMAL))
|
|
219
|
+
self.assertEqual(queue.qsize(), 10)
|
|
220
|
+
self.assertFalse(queue.full())
|
|
221
|
+
while not queue.empty():
|
|
222
|
+
await queue.get()
|
|
223
|
+
self.assertEqual(queue.qsize(), 0)
|
|
224
|
+
|
|
225
|
+
async def test_qsize_and_full_track_jobs(self) -> None:
|
|
226
|
+
queue = PriorityFairQueue(maxsize=2)
|
|
227
|
+
|
|
228
|
+
self.assertEqual(queue.qsize(), 0)
|
|
229
|
+
self.assertFalse(queue.full())
|
|
230
|
+
|
|
231
|
+
await queue.put(Job("job-1", Priority.NORMAL, "tenant-a"))
|
|
232
|
+
self.assertEqual(queue.qsize(), 1)
|
|
233
|
+
self.assertFalse(queue.full())
|
|
234
|
+
|
|
235
|
+
await queue.put(Job("job-2", Priority.HIGH, "tenant-b"))
|
|
236
|
+
self.assertEqual(queue.qsize(), 2)
|
|
237
|
+
self.assertTrue(queue.full())
|
|
238
|
+
|
|
239
|
+
await queue.get()
|
|
240
|
+
self.assertEqual(queue.qsize(), 1)
|
|
241
|
+
self.assertFalse(queue.full())
|
|
242
|
+
|
|
243
|
+
await queue.get()
|
|
244
|
+
self.assertEqual(queue.qsize(), 0)
|
|
245
|
+
self.assertFalse(queue.full())
|
|
246
|
+
|
|
247
|
+
async def test_full_is_false_for_unbounded_queue(self) -> None:
|
|
248
|
+
queue = PriorityFairQueue()
|
|
249
|
+
await queue.put(Job("job-1", Priority.NORMAL))
|
|
250
|
+
|
|
251
|
+
self.assertEqual(queue.qsize(), 1)
|
|
252
|
+
self.assertFalse(queue.full())
|
|
253
|
+
|
|
254
|
+
async def test_maxsize_1(self) -> None:
|
|
255
|
+
queue = PriorityFairQueue(maxsize=1)
|
|
256
|
+
await queue.put(Job("job-1", Priority.NORMAL))
|
|
257
|
+
|
|
258
|
+
t = asyncio.create_task(
|
|
259
|
+
queue.put(Job("job-2", Priority.NORMAL))
|
|
260
|
+
)
|
|
261
|
+
await asyncio.sleep(0)
|
|
262
|
+
self.assertFalse(t.done())
|
|
263
|
+
await queue.get()
|
|
264
|
+
|
|
265
|
+
await asyncio.wait_for(t, timeout=1)
|
|
266
|
+
await queue.get()
|
|
267
|
+
self.assertTrue(t.done())
|
|
268
|
+
self.assertTrue(queue.empty())
|
|
269
|
+
|
|
270
|
+
async def test_shutdown_wakes_put_waiting_on_full_queue(self) -> None:
|
|
271
|
+
queue = PriorityFairQueue(maxsize=1)
|
|
272
|
+
await queue.put(Job("job-1", Priority.NORMAL))
|
|
273
|
+
|
|
274
|
+
put_task = asyncio.create_task(
|
|
275
|
+
queue.put(Job("job-2", Priority.NORMAL))
|
|
276
|
+
)
|
|
277
|
+
await asyncio.sleep(0)
|
|
278
|
+
self.assertFalse(put_task.done())
|
|
279
|
+
|
|
280
|
+
await queue.shutdown()
|
|
281
|
+
|
|
282
|
+
with self.assertRaises(QueueShutDown):
|
|
283
|
+
await asyncio.wait_for(put_task, timeout=1)
|
|
284
|
+
|
|
285
|
+
self.assertEqual((await queue.get()).name, "job-1")
|
|
286
|
+
with self.assertRaises(QueueShutDown):
|
|
287
|
+
await queue.get()
|
|
288
|
+
|
|
289
|
+
async def test_get_times_out_when_queue_is_empty(self) -> None:
|
|
290
|
+
queue = PriorityFairQueue()
|
|
291
|
+
|
|
292
|
+
with self.assertRaises(asyncio.TimeoutError):
|
|
293
|
+
await queue.get(timeout=0.01)
|
|
294
|
+
|
|
295
|
+
self.assertTrue(queue.empty())
|
|
296
|
+
self.assertEqual(queue.qsize(), 0)
|
|
297
|
+
|
|
298
|
+
job = Job("job-1", Priority.NORMAL)
|
|
299
|
+
await queue.put(job)
|
|
300
|
+
self.assertEqual(await queue.get(), job)
|
|
301
|
+
|
|
302
|
+
async def test_put_times_out_when_queue_is_full(self) -> None:
|
|
303
|
+
queue = PriorityFairQueue(maxsize=1)
|
|
304
|
+
first_job = Job("job-1", Priority.NORMAL)
|
|
305
|
+
await queue.put(first_job)
|
|
306
|
+
|
|
307
|
+
with self.assertRaises(asyncio.TimeoutError):
|
|
308
|
+
await queue.put(Job("job-2", Priority.NORMAL), timeout=0.01)
|
|
309
|
+
|
|
310
|
+
self.assertEqual(queue.qsize(), 1)
|
|
311
|
+
self.assertTrue(queue.full())
|
|
312
|
+
self.assertEqual(await queue.get(), first_job)
|
|
313
|
+
|
|
314
|
+
async def test_cancelled_get_does_not_remove_jobs(self) -> None:
|
|
315
|
+
queue = PriorityFairQueue()
|
|
316
|
+
get_task = asyncio.create_task(queue.get())
|
|
317
|
+
await asyncio.sleep(0)
|
|
318
|
+
self.assertFalse(get_task.done())
|
|
319
|
+
|
|
320
|
+
get_task.cancel()
|
|
321
|
+
with self.assertRaises(asyncio.CancelledError):
|
|
322
|
+
await get_task
|
|
323
|
+
|
|
324
|
+
job = Job("job-1", Priority.NORMAL)
|
|
325
|
+
await queue.put(job)
|
|
326
|
+
self.assertEqual(queue.qsize(), 1)
|
|
327
|
+
self.assertEqual(await queue.get(), job)
|
|
328
|
+
|
|
329
|
+
async def test_cancelled_put_does_not_enqueue_job(self) -> None:
|
|
330
|
+
queue = PriorityFairQueue(maxsize=1)
|
|
331
|
+
first_job = Job("job-1", Priority.NORMAL)
|
|
332
|
+
await queue.put(first_job)
|
|
333
|
+
|
|
334
|
+
put_task = asyncio.create_task(
|
|
335
|
+
queue.put(Job("job-2", Priority.NORMAL))
|
|
336
|
+
)
|
|
337
|
+
await asyncio.sleep(0)
|
|
338
|
+
self.assertFalse(put_task.done())
|
|
339
|
+
|
|
340
|
+
put_task.cancel()
|
|
341
|
+
with self.assertRaises(asyncio.CancelledError):
|
|
342
|
+
await put_task
|
|
343
|
+
|
|
344
|
+
self.assertEqual(queue.qsize(), 1)
|
|
345
|
+
self.assertTrue(queue.full())
|
|
346
|
+
self.assertEqual(await queue.get(), first_job)
|
|
347
|
+
|
|
348
|
+
async def test_put_rejects_invalid_timeouts(self) -> None:
|
|
349
|
+
queue = PriorityFairQueue()
|
|
350
|
+
job = Job("job-1", Priority.NORMAL)
|
|
351
|
+
|
|
352
|
+
for timeout in (0, -1, math.inf, -math.inf, math.nan):
|
|
353
|
+
with self.subTest(timeout=timeout):
|
|
354
|
+
with self.assertRaises(ValueError):
|
|
355
|
+
await queue.put(job, timeout=timeout)
|
|
356
|
+
|
|
357
|
+
self.assertTrue(queue.empty())
|
|
358
|
+
self.assertEqual(queue.qsize(), 0)
|
|
359
|
+
|
|
360
|
+
async def test_get_rejects_invalid_timeouts_without_consuming_jobs(self) -> None:
|
|
361
|
+
queue = PriorityFairQueue()
|
|
362
|
+
job = Job("job-1", Priority.NORMAL)
|
|
363
|
+
await queue.put(job)
|
|
364
|
+
|
|
365
|
+
for timeout in (0, -1, math.inf, -math.inf, math.nan):
|
|
366
|
+
with self.subTest(timeout=timeout):
|
|
367
|
+
with self.assertRaises(ValueError):
|
|
368
|
+
await queue.get(timeout=timeout)
|
|
369
|
+
|
|
370
|
+
self.assertEqual(queue.qsize(), 1)
|
|
371
|
+
self.assertEqual(await queue.get(), job)
|
|
372
|
+
|
|
210
373
|
if __name__ == "__main__":
|
|
211
374
|
"""
|
|
212
375
|
python -m pip install .
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{priomesh_python-0.1.0 → priomesh_python-0.2.0}/src/priomesh_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|