csu 2.0.8__tar.gz → 2.1.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.
- {csu-2.0.8 → csu-2.1.0}/.bumpversion.cfg +1 -1
- {csu-2.0.8 → csu-2.1.0}/.cookiecutterrc +1 -1
- {csu-2.0.8 → csu-2.1.0}/PKG-INFO +3 -3
- {csu-2.0.8 → csu-2.1.0}/README.rst +2 -2
- {csu-2.0.8 → csu-2.1.0}/pyproject.toml +1 -1
- {csu-2.0.8 → csu-2.1.0}/src/csu/__init__.py +1 -1
- {csu-2.0.8 → csu-2.1.0}/src/csu/worker/engine.py +72 -64
- {csu-2.0.8 → csu-2.1.0}/src/csu.egg-info/PKG-INFO +3 -3
- {csu-2.0.8 → csu-2.1.0}/tests/exampleworker.py +9 -6
- {csu-2.0.8 → csu-2.1.0}/tests/test_worker.py +4 -3
- {csu-2.0.8 → csu-2.1.0}/.coveragerc +0 -0
- {csu-2.0.8 → csu-2.1.0}/.editorconfig +0 -0
- {csu-2.0.8 → csu-2.1.0}/.github/workflows/github-actions.yml +0 -0
- {csu-2.0.8 → csu-2.1.0}/.pre-commit-config.yaml +0 -0
- {csu-2.0.8 → csu-2.1.0}/.taplo.toml +0 -0
- {csu-2.0.8 → csu-2.1.0}/AUTHORS.rst +0 -0
- {csu-2.0.8 → csu-2.1.0}/CHANGELOG.rst +0 -0
- {csu-2.0.8 → csu-2.1.0}/CONTRIBUTING.rst +0 -0
- {csu-2.0.8 → csu-2.1.0}/LICENSE +0 -0
- {csu-2.0.8 → csu-2.1.0}/MANIFEST.in +0 -0
- {csu-2.0.8 → csu-2.1.0}/ci/bootstrap.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/ci/requirements.txt +0 -0
- {csu-2.0.8 → csu-2.1.0}/ci/templates/.github/workflows/github-actions.yml +0 -0
- {csu-2.0.8 → csu-2.1.0}/pytest.ini +0 -0
- {csu-2.0.8 → csu-2.1.0}/setup.cfg +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/admin.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/conf.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/consts.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/__init__.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/auth.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/fields.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/forms.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/perms.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/phonenumber.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/serializers.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/test_auth.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/test_fields.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/test_forms.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/test_phonenumber.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/test_serializers.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/drf/views.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/enums.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/env.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/exceptions.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/export.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/fixups.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/forms/__init__.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/forms/crispy.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/forms/fields.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/gettext.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/gettext_lazy.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/locale/ro/LC_MESSAGES/django.mo +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/locale/ro/LC_MESSAGES/django.po +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/logging.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/management.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/models.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/query.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/routers.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/service.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/templates/api_exception.html +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/templates/forms/widgets/opaquewidget.html +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/test_consts.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/test_env.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/test_exceptions.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/test_logging.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/test_management.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/test_service.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/test_timezones.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/test_utils.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/test_xml.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/timezones.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/utils.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/views.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/worker/__init__.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/worker/admin.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/worker/asgi.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/worker/enums.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/worker/job.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/worker/models.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/worker/registry.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/wsgi.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu/xml.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu.egg-info/SOURCES.txt +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu.egg-info/dependency_links.txt +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu.egg-info/requires.txt +0 -0
- {csu-2.0.8 → csu-2.1.0}/src/csu.egg-info/top_level.txt +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_custom_context.yaml +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_custom_context_0_retries.yaml +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_decoding.yaml +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_error.yaml +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_logging.yaml +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_redirects.yaml +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service_auth/test_async.yaml +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service_auth/test_sync.yaml +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/conftest.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/test_models.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/test_service.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/test_service_auth.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/test_views.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/testproject/__init__.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/testproject/fixtures/testuser.json +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/testproject/models.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/testproject/settings.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/tests/testproject/urls.py +0 -0
- {csu-2.0.8 → csu-2.1.0}/tox.ini +0 -0
{csu-2.0.8 → csu-2.1.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csu
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Clean Slate Utils - bunch of utility code, mostly Django/DRF specific.
|
|
5
5
|
Author-email: Ionel Cristian Mărieș <contact@ionelmc.ro>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
@@ -77,9 +77,9 @@ Overview
|
|
|
77
77
|
.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/csu.svg
|
|
78
78
|
:alt: Supported implementations
|
|
79
79
|
:target: https://pypi.org/project/csu
|
|
80
|
-
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.0.
|
|
80
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.1.0.svg
|
|
81
81
|
:alt: Commits since latest release
|
|
82
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.0
|
|
82
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.1.0...main
|
|
83
83
|
.. |scrutinizer| image:: https://img.shields.io/scrutinizer/quality/g/ionelmc/python-csu/main.svg
|
|
84
84
|
:alt: Scrutinizer Status
|
|
85
85
|
:target: https://scrutinizer-ci.com/g/ionelmc/python-csu/
|
|
@@ -34,9 +34,9 @@ Overview
|
|
|
34
34
|
.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/csu.svg
|
|
35
35
|
:alt: Supported implementations
|
|
36
36
|
:target: https://pypi.org/project/csu
|
|
37
|
-
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.0.
|
|
37
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.1.0.svg
|
|
38
38
|
:alt: Commits since latest release
|
|
39
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.0
|
|
39
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.1.0...main
|
|
40
40
|
.. |scrutinizer| image:: https://img.shields.io/scrutinizer/quality/g/ionelmc/python-csu/main.svg
|
|
41
41
|
:alt: Scrutinizer Status
|
|
42
42
|
:target: https://scrutinizer-ci.com/g/ionelmc/python-csu/
|
|
@@ -4,11 +4,12 @@ import traceback
|
|
|
4
4
|
from abc import ABC
|
|
5
5
|
from abc import abstractmethod
|
|
6
6
|
from asyncio import CancelledError
|
|
7
|
+
from asyncio import Event
|
|
7
8
|
from asyncio import Future
|
|
8
9
|
from asyncio import Queue
|
|
9
|
-
from asyncio import Semaphore
|
|
10
10
|
from collections import defaultdict
|
|
11
|
-
from
|
|
11
|
+
from collections.abc import AsyncIterator
|
|
12
|
+
from contextlib import asynccontextmanager
|
|
12
13
|
from datetime import datetime
|
|
13
14
|
from datetime import time
|
|
14
15
|
from datetime import timedelta
|
|
@@ -121,7 +122,7 @@ class NoPrefetchingProducerMixin:
|
|
|
121
122
|
# noinspection PyUnresolvedReferences
|
|
122
123
|
await super().before()
|
|
123
124
|
self.state = WorkerState.WAITING
|
|
124
|
-
await self.engine.
|
|
125
|
+
await self.engine.wait_worker_ready()
|
|
125
126
|
|
|
126
127
|
|
|
127
128
|
class SleepingCooldown(Flag):
|
|
@@ -153,7 +154,6 @@ class SleepingCooldownMixin:
|
|
|
153
154
|
await asyncio.sleep(sleep_seconds)
|
|
154
155
|
|
|
155
156
|
|
|
156
|
-
@dataclass(repr=False)
|
|
157
157
|
class AbstractProducer(AbstractWorker):
|
|
158
158
|
state = WorkerState.UNKNOWN
|
|
159
159
|
engine: AbstractEngine
|
|
@@ -169,6 +169,9 @@ class AbstractProducer(AbstractWorker):
|
|
|
169
169
|
Should raise error if there would be a spin-loop condition.
|
|
170
170
|
"""
|
|
171
171
|
|
|
172
|
+
def __init__(self, engine: AbstractEngine):
|
|
173
|
+
self.engine = engine
|
|
174
|
+
|
|
172
175
|
def __str__(self):
|
|
173
176
|
return f"{type(self).__name__}()"
|
|
174
177
|
|
|
@@ -198,7 +201,6 @@ class AbstractProducer(AbstractWorker):
|
|
|
198
201
|
await self.after(idle=not task)
|
|
199
202
|
|
|
200
203
|
|
|
201
|
-
@dataclass(repr=False)
|
|
202
204
|
class AbstractConsumer(AbstractWorker):
|
|
203
205
|
engine: AbstractEngine
|
|
204
206
|
|
|
@@ -211,6 +213,9 @@ class AbstractConsumer(AbstractWorker):
|
|
|
211
213
|
async def save_result(self, job: Job, /, **kwargs):
|
|
212
214
|
pass
|
|
213
215
|
|
|
216
|
+
def __init__(self, engine: AbstractEngine):
|
|
217
|
+
self.engine = engine
|
|
218
|
+
|
|
214
219
|
def __str__(self):
|
|
215
220
|
return f"{type(self).__name__}({id(self)})"
|
|
216
221
|
|
|
@@ -220,38 +225,39 @@ class AbstractConsumer(AbstractWorker):
|
|
|
220
225
|
self.state = WorkerState.BEFORE
|
|
221
226
|
await self.before()
|
|
222
227
|
self.state = WorkerState.READY
|
|
223
|
-
job: Job
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
228
|
+
job: Job | None
|
|
229
|
+
async with self.engine.consume() as job:
|
|
230
|
+
if job is None:
|
|
231
|
+
break
|
|
232
|
+
job.started.set_result(True)
|
|
233
|
+
logger.info("%s.run() working on: %s", self, job)
|
|
234
|
+
self.state = WorkerState.WORKING
|
|
235
|
+
logger.info("%s.run() - performing...", self)
|
|
236
|
+
try:
|
|
237
|
+
fields = await self.perform_work(job)
|
|
238
|
+
except Exception as exc:
|
|
239
|
+
self.state = WorkerState.SAVING
|
|
240
|
+
logger.error("%s.perform_work(%s) failed: %r", self, job, exc)
|
|
241
|
+
result = await self.save_result(
|
|
242
|
+
job,
|
|
243
|
+
result_type=self.result_type_enum.ERROR,
|
|
244
|
+
result_details=f"{exc!r}\n{traceback.format_exc()}",
|
|
245
|
+
)
|
|
246
|
+
idle = True
|
|
247
|
+
else:
|
|
248
|
+
self.state = WorkerState.SAVING
|
|
249
|
+
result = await self.save_result(
|
|
250
|
+
job,
|
|
251
|
+
result_type=self.result_type_enum.SUCCESS if fields else self.result_type_enum.ABSENT,
|
|
252
|
+
result_details=None if fields else repr(fields),
|
|
253
|
+
**fields or {},
|
|
254
|
+
)
|
|
255
|
+
idle = False
|
|
256
|
+
if job.done.cancelled():
|
|
257
|
+
logger.warning("%s.run() - %s is cancelled.", self, job)
|
|
258
|
+
else:
|
|
259
|
+
job.done.set_result(result)
|
|
260
|
+
|
|
255
261
|
if self.shutdown_requested:
|
|
256
262
|
continue
|
|
257
263
|
self.state = WorkerState.AFTER
|
|
@@ -262,21 +268,21 @@ class AbstractConsumer(AbstractWorker):
|
|
|
262
268
|
pass
|
|
263
269
|
|
|
264
270
|
|
|
265
|
-
@dataclass
|
|
266
271
|
class AbstractEngine(ABC):
|
|
267
272
|
workers: list[AbstractWorker]
|
|
268
|
-
queue: Queue
|
|
273
|
+
queue: Queue = None
|
|
274
|
+
queue_maxsize: int
|
|
269
275
|
shutdown_signal = signal.SIGINT
|
|
270
276
|
shutdown_requested = False
|
|
271
277
|
shutdown_task = None
|
|
272
278
|
|
|
273
279
|
def __init__(self):
|
|
274
280
|
self.workers = []
|
|
275
|
-
self.
|
|
276
|
-
self.
|
|
281
|
+
self.queue_awaited = Event()
|
|
282
|
+
self.queue_maxsize = 0
|
|
277
283
|
|
|
278
284
|
def __str__(self):
|
|
279
|
-
return f"{type(self).__name__}({len(self.workers)}w/{self.queue.qsize()}q)"
|
|
285
|
+
return f"{type(self).__name__}({len(self.workers)}w/{self.queue.qsize() if self.queue else '-'}q)"
|
|
280
286
|
|
|
281
287
|
def is_work_pending(self):
|
|
282
288
|
if any(worker for worker in self.workers if worker.state == WorkerState.WORKING):
|
|
@@ -285,29 +291,31 @@ class AbstractEngine(ABC):
|
|
|
285
291
|
return True
|
|
286
292
|
return not self.shutdown_requested
|
|
287
293
|
|
|
288
|
-
async def
|
|
289
|
-
await self.queue.join()
|
|
290
|
-
|
|
291
|
-
async def push(self, job: Job) -> None:
|
|
294
|
+
async def push(self, job: Job | None) -> None:
|
|
292
295
|
logger.info("%s.push(%s)", self, job)
|
|
293
|
-
await self.semaphore.acquire()
|
|
294
296
|
await self.queue.put(job)
|
|
295
297
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
298
|
+
@asynccontextmanager
|
|
299
|
+
async def consume(self) -> AsyncIterator[Job | None]:
|
|
300
|
+
try:
|
|
301
|
+
self.queue_awaited.set()
|
|
302
|
+
yield await self.queue.get()
|
|
303
|
+
finally:
|
|
304
|
+
self.queue.task_done()
|
|
305
|
+
|
|
306
|
+
async def wait_worker_ready(self):
|
|
307
|
+
await self.queue_awaited.wait()
|
|
308
|
+
self.queue_awaited.clear()
|
|
309
|
+
|
|
310
|
+
def add_worker(self, worker: AbstractWorker):
|
|
311
|
+
if self.queue is not None:
|
|
312
|
+
raise RuntimeError(f"Engine {self} is already started!")
|
|
313
|
+
if isinstance(worker, AbstractConsumer):
|
|
314
|
+
self.queue_maxsize += 1
|
|
315
|
+
self.workers.append(worker)
|
|
309
316
|
|
|
310
317
|
async def start(self):
|
|
318
|
+
self.queue = Queue(maxsize=self.queue_maxsize)
|
|
311
319
|
signal.signal(self.shutdown_signal, self.shutdown_handler)
|
|
312
320
|
|
|
313
321
|
def shutdown_handler(self, sig, frame):
|
|
@@ -329,20 +337,20 @@ class AbstractEngine(ABC):
|
|
|
329
337
|
for worker in self.workers:
|
|
330
338
|
worker.shutdown_requested = True
|
|
331
339
|
if isinstance(worker, AbstractConsumer):
|
|
332
|
-
self.
|
|
340
|
+
await self.push(None)
|
|
333
341
|
else:
|
|
334
342
|
while self.queue.qsize() > 0:
|
|
335
343
|
item = self.queue.get_nowait()
|
|
336
344
|
if item is not None:
|
|
337
|
-
logger.critical("%s unprocessed queue item: %r", item)
|
|
345
|
+
logger.critical("%s unprocessed queue item: %r", self, item)
|
|
338
346
|
for worker in self.workers:
|
|
339
347
|
worker.runner.cancel()
|
|
340
348
|
await asyncio.gather(*[worker.runner for worker in self.workers], return_exceptions=True)
|
|
341
349
|
self.workers.clear()
|
|
342
350
|
logger.info("%s shutdown complete.", self)
|
|
343
351
|
|
|
344
|
-
def report(self, inspect: defaultdict[str,
|
|
345
|
-
inspect["QSIZE"] = self.queue.qsize()
|
|
352
|
+
def report(self, inspect: defaultdict[str, object]):
|
|
353
|
+
inspect["QSIZE"] = "UNSTARTED" if self.queue is None else self.queue.qsize()
|
|
346
354
|
|
|
347
355
|
def inspect(self) -> dict[str, dict[str, int]]:
|
|
348
356
|
result = defaultdict(partial(defaultdict, int))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csu
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
4
4
|
Summary: Clean Slate Utils - bunch of utility code, mostly Django/DRF specific.
|
|
5
5
|
Author-email: Ionel Cristian Mărieș <contact@ionelmc.ro>
|
|
6
6
|
License-Expression: BSD-2-Clause
|
|
@@ -77,9 +77,9 @@ Overview
|
|
|
77
77
|
.. |supported-implementations| image:: https://img.shields.io/pypi/implementation/csu.svg
|
|
78
78
|
:alt: Supported implementations
|
|
79
79
|
:target: https://pypi.org/project/csu
|
|
80
|
-
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.0.
|
|
80
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.1.0.svg
|
|
81
81
|
:alt: Commits since latest release
|
|
82
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.0
|
|
82
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.1.0...main
|
|
83
83
|
.. |scrutinizer| image:: https://img.shields.io/scrutinizer/quality/g/ionelmc/python-csu/main.svg
|
|
84
84
|
:alt: Scrutinizer Status
|
|
85
85
|
:target: https://scrutinizer-ci.com/g/ionelmc/python-csu/
|
|
@@ -8,7 +8,6 @@ from logging import getLogger
|
|
|
8
8
|
from csu.worker.engine import AbstractConsumer
|
|
9
9
|
from csu.worker.engine import AbstractEngine
|
|
10
10
|
from csu.worker.engine import AbstractProducer
|
|
11
|
-
from csu.worker.engine import NoPrefetchingProducerMixin
|
|
12
11
|
from csu.worker.engine import TaskProtocol
|
|
13
12
|
from csu.worker.job import Job
|
|
14
13
|
|
|
@@ -45,7 +44,10 @@ class ExampleResultType(StrEnum):
|
|
|
45
44
|
ABSENT = "ABSENT"
|
|
46
45
|
|
|
47
46
|
|
|
48
|
-
class ExampleProducer(
|
|
47
|
+
class ExampleProducer(
|
|
48
|
+
# NoPrefetchingProducerMixin,
|
|
49
|
+
AbstractProducer,
|
|
50
|
+
):
|
|
49
51
|
engine: ExampleEngine
|
|
50
52
|
job_class = ExampleJob
|
|
51
53
|
counter = 0
|
|
@@ -69,24 +71,25 @@ class ExampleConsumer(AbstractConsumer):
|
|
|
69
71
|
logger.info("%s.save_result(%s)", self, job)
|
|
70
72
|
|
|
71
73
|
|
|
72
|
-
WORKERS =
|
|
74
|
+
WORKERS = 5
|
|
73
75
|
|
|
74
76
|
|
|
75
77
|
class ExampleEngine(AbstractEngine):
|
|
76
78
|
queue_maxsize = WORKERS
|
|
77
79
|
|
|
78
80
|
async def start(self):
|
|
79
|
-
await super().start()
|
|
80
81
|
logger.info("Starting workers...")
|
|
81
82
|
|
|
82
83
|
for _ in range(WORKERS):
|
|
83
84
|
consumer = ExampleConsumer(engine=self)
|
|
84
85
|
consumer.start()
|
|
85
|
-
self.
|
|
86
|
+
self.add_worker(consumer)
|
|
86
87
|
|
|
87
88
|
producer = ExampleProducer(engine=self)
|
|
88
89
|
producer.start()
|
|
89
|
-
self.
|
|
90
|
+
self.add_worker(producer)
|
|
91
|
+
|
|
92
|
+
await super().start()
|
|
90
93
|
|
|
91
94
|
while self.is_work_pending():
|
|
92
95
|
await asyncio.sleep(1)
|
|
@@ -43,7 +43,8 @@ class Engine(AbstractEngine):
|
|
|
43
43
|
async def start(self):
|
|
44
44
|
worker = Consumer(engine=self)
|
|
45
45
|
worker.start()
|
|
46
|
-
self.
|
|
46
|
+
self.add_worker(worker)
|
|
47
|
+
await super().start()
|
|
47
48
|
logging.info("engine started")
|
|
48
49
|
|
|
49
50
|
|
|
@@ -52,9 +53,9 @@ async def test_cancel():
|
|
|
52
53
|
engine = Engine()
|
|
53
54
|
await engine.start()
|
|
54
55
|
job1 = SleepyJob(0.2)
|
|
55
|
-
engine.
|
|
56
|
+
await engine.push(job1)
|
|
56
57
|
job2 = SleepyJob(0)
|
|
57
|
-
engine.
|
|
58
|
+
await engine.push(job2)
|
|
58
59
|
await asyncio.sleep(0.1)
|
|
59
60
|
with pytest.raises(TimeoutError):
|
|
60
61
|
await asyncio.wait_for(job1.done, 0.1)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csu-2.0.8 → csu-2.1.0}/LICENSE
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{csu-2.0.8 → csu-2.1.0}/tox.ini
RENAMED
|
File without changes
|