csu 2.1.2__tar.gz → 2.1.4__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.1.2 → csu-2.1.4}/.bumpversion.cfg +1 -1
- {csu-2.1.2 → csu-2.1.4}/.cookiecutterrc +1 -1
- {csu-2.1.2 → csu-2.1.4}/PKG-INFO +3 -3
- {csu-2.1.2 → csu-2.1.4}/README.rst +2 -2
- {csu-2.1.2 → csu-2.1.4}/pyproject.toml +1 -1
- {csu-2.1.2 → csu-2.1.4}/src/csu/__init__.py +1 -1
- {csu-2.1.2 → csu-2.1.4}/src/csu/worker/engine.py +6 -6
- {csu-2.1.2 → csu-2.1.4}/src/csu.egg-info/PKG-INFO +3 -3
- {csu-2.1.2 → csu-2.1.4}/src/csu.egg-info/SOURCES.txt +1 -0
- csu-2.1.4/tests/exampleworker.py +126 -0
- csu-2.1.4/tests/test_exampleworker.py +87 -0
- {csu-2.1.2 → csu-2.1.4}/tests/test_worker.py +0 -1
- {csu-2.1.2 → csu-2.1.4}/tox.ini +1 -0
- csu-2.1.2/tests/exampleworker.py +0 -104
- {csu-2.1.2 → csu-2.1.4}/.coveragerc +0 -0
- {csu-2.1.2 → csu-2.1.4}/.editorconfig +0 -0
- {csu-2.1.2 → csu-2.1.4}/.github/workflows/github-actions.yml +0 -0
- {csu-2.1.2 → csu-2.1.4}/.pre-commit-config.yaml +0 -0
- {csu-2.1.2 → csu-2.1.4}/.taplo.toml +0 -0
- {csu-2.1.2 → csu-2.1.4}/AUTHORS.rst +0 -0
- {csu-2.1.2 → csu-2.1.4}/CHANGELOG.rst +0 -0
- {csu-2.1.2 → csu-2.1.4}/CONTRIBUTING.rst +0 -0
- {csu-2.1.2 → csu-2.1.4}/LICENSE +0 -0
- {csu-2.1.2 → csu-2.1.4}/MANIFEST.in +0 -0
- {csu-2.1.2 → csu-2.1.4}/ci/bootstrap.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/ci/requirements.txt +0 -0
- {csu-2.1.2 → csu-2.1.4}/ci/templates/.github/workflows/github-actions.yml +0 -0
- {csu-2.1.2 → csu-2.1.4}/pytest.ini +0 -0
- {csu-2.1.2 → csu-2.1.4}/setup.cfg +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/admin.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/conf.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/consts.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/__init__.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/auth.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/fields.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/forms.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/perms.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/phonenumber.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/serializers.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/test_auth.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/test_fields.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/test_forms.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/test_phonenumber.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/test_serializers.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/drf/views.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/enums.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/env.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/exceptions.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/export.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/fixups.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/forms/__init__.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/forms/crispy.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/forms/fields.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/gettext.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/gettext_lazy.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/locale/ro/LC_MESSAGES/django.mo +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/locale/ro/LC_MESSAGES/django.po +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/logging.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/management.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/models.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/query.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/routers.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/service.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/templates/api_exception.html +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/templates/forms/widgets/opaquewidget.html +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/test_consts.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/test_env.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/test_exceptions.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/test_logging.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/test_management.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/test_service.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/test_timezones.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/test_utils.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/test_xml.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/timezones.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/utils.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/views.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/worker/__init__.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/worker/admin.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/worker/asgi.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/worker/enums.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/worker/job.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/worker/models.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/worker/registry.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/wsgi.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu/xml.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu.egg-info/dependency_links.txt +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu.egg-info/requires.txt +0 -0
- {csu-2.1.2 → csu-2.1.4}/src/csu.egg-info/top_level.txt +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/cassettes/test_service/test_custom_context.yaml +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/cassettes/test_service/test_custom_context_0_retries.yaml +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/cassettes/test_service/test_decoding.yaml +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/cassettes/test_service/test_error.yaml +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/cassettes/test_service/test_logging.yaml +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/cassettes/test_service/test_redirects.yaml +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/cassettes/test_service_auth/test_async.yaml +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/cassettes/test_service_auth/test_sync.yaml +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/conftest.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/test_models.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/test_service.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/test_service_auth.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/test_views.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/testproject/__init__.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/testproject/fixtures/testuser.json +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/testproject/models.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/testproject/settings.py +0 -0
- {csu-2.1.2 → csu-2.1.4}/tests/testproject/urls.py +0 -0
{csu-2.1.2 → csu-2.1.4}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csu
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
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.1.
|
|
80
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.1.4.svg
|
|
81
81
|
:alt: Commits since latest release
|
|
82
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.1.
|
|
82
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.1.4...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.1.
|
|
37
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.1.4.svg
|
|
38
38
|
:alt: Commits since latest release
|
|
39
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.1.
|
|
39
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.1.4...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/
|
|
@@ -293,19 +293,19 @@ class AbstractEngine(ABC):
|
|
|
293
293
|
|
|
294
294
|
async def push(self, job: Job | None) -> None:
|
|
295
295
|
logger.info("%s.push(%s)", self, job)
|
|
296
|
+
self.queue_awaited.clear() # clear it here, it's the shortest path to "consumer wakes up and calls .set()"
|
|
296
297
|
await self.queue.put(job)
|
|
297
298
|
|
|
298
299
|
@asynccontextmanager
|
|
299
300
|
async def consume(self) -> AsyncIterator[Job | None]:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
self.queue.task_done()
|
|
301
|
+
self.queue_awaited.set()
|
|
302
|
+
yield await self.queue.get()
|
|
303
|
+
# if raised then there's no task to mark as done
|
|
304
|
+
self.queue.task_done()
|
|
305
305
|
|
|
306
306
|
async def wait_worker_ready(self):
|
|
307
|
+
# event set after consuming, cleared right before there's a task
|
|
307
308
|
await self.queue_awaited.wait()
|
|
308
|
-
self.queue_awaited.clear()
|
|
309
309
|
|
|
310
310
|
def add_worker(self, worker: AbstractWorker):
|
|
311
311
|
assert self.queue is None, f"{self} is already started"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csu
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
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.1.
|
|
80
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.1.4.svg
|
|
81
81
|
:alt: Commits since latest release
|
|
82
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.1.
|
|
82
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.1.4...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/
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
import sys
|
|
4
|
+
from dataclasses import asdict
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from enum import StrEnum
|
|
7
|
+
from logging import getLogger
|
|
8
|
+
|
|
9
|
+
from csu.worker.engine import AbstractConsumer
|
|
10
|
+
from csu.worker.engine import AbstractEngine
|
|
11
|
+
from csu.worker.engine import AbstractProducer
|
|
12
|
+
from csu.worker.engine import NoPrefetchingProducerMixin
|
|
13
|
+
from csu.worker.engine import SleepingCooldown
|
|
14
|
+
from csu.worker.engine import SleepingCooldownMixin
|
|
15
|
+
from csu.worker.engine import TaskProtocol
|
|
16
|
+
from csu.worker.job import Job
|
|
17
|
+
|
|
18
|
+
logger = getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class NoOpTask:
|
|
23
|
+
"""
|
|
24
|
+
Simple task model that doesn't store anything.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
id: int
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def job_kwargs(self):
|
|
31
|
+
return asdict(self)
|
|
32
|
+
|
|
33
|
+
async def done_watchdog(self, job: Job):
|
|
34
|
+
"""
|
|
35
|
+
Nothing done as there's no storage for this job.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass
|
|
40
|
+
class ExampleJob[RequestStruct](Job):
|
|
41
|
+
id: int
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class ExampleResultType(StrEnum):
|
|
45
|
+
PENDING = "PENDING"
|
|
46
|
+
SUCCESS = "SUCCESS"
|
|
47
|
+
ERROR = "ERROR"
|
|
48
|
+
ABSENT = "ABSENT"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
if sys.argv[1] == "prefetch":
|
|
52
|
+
|
|
53
|
+
class NoPrefetchingProducerMixin:
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class ExampleProducer(
|
|
58
|
+
NoPrefetchingProducerMixin,
|
|
59
|
+
SleepingCooldownMixin,
|
|
60
|
+
AbstractProducer,
|
|
61
|
+
):
|
|
62
|
+
engine: ExampleEngine
|
|
63
|
+
job_class = ExampleJob
|
|
64
|
+
counter = 0
|
|
65
|
+
cooldown_min_seconds = cooldown_max_seconds = 1
|
|
66
|
+
cooldown_on = SleepingCooldown.IDLE
|
|
67
|
+
|
|
68
|
+
async def fetch_task(self) -> TaskProtocol | None:
|
|
69
|
+
self.counter += 1
|
|
70
|
+
if self.counter > self.engine.max_jobs:
|
|
71
|
+
print(f"PRODUCER fetch_task @ {self.counter} EXHAUSTED")
|
|
72
|
+
return
|
|
73
|
+
if not self.engine.producer_skip or self.counter % self.engine.producer_skip:
|
|
74
|
+
print(f"PRODUCER fetch_task @ {self.counter} RETURN")
|
|
75
|
+
return NoOpTask(self.counter)
|
|
76
|
+
else:
|
|
77
|
+
print(f"PRODUCER fetch_task @ {self.counter} SKIP")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class ExampleConsumer(AbstractConsumer):
|
|
81
|
+
result_type_enum = ExampleResultType
|
|
82
|
+
engine: ExampleEngine
|
|
83
|
+
|
|
84
|
+
def __init__(self, engine: ExampleEngine, name):
|
|
85
|
+
super().__init__(engine)
|
|
86
|
+
self.name = name
|
|
87
|
+
|
|
88
|
+
async def perform_work(self, job: ExampleJob):
|
|
89
|
+
print(f"CONSUMER {self.name} perform_work JOB {job.id} START")
|
|
90
|
+
try:
|
|
91
|
+
await asyncio.sleep(self.engine.consumer_sleep)
|
|
92
|
+
return {"foo": "bar"}
|
|
93
|
+
finally:
|
|
94
|
+
print(f"CONSUMER {self.name} perform_work JOB {job.id} DONE")
|
|
95
|
+
|
|
96
|
+
async def save_result(self, job: ExampleJob, /, **kwargs):
|
|
97
|
+
print(f"CONSUMER {self.name} save_result JOB {job.id}")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ExampleEngine(AbstractEngine):
|
|
101
|
+
def __init__(self, consumers, producer_skip, consumer_sleep, max_jobs):
|
|
102
|
+
super().__init__()
|
|
103
|
+
self.consumers = int(consumers)
|
|
104
|
+
self.producer_skip = int(producer_skip)
|
|
105
|
+
self.consumer_sleep = int(consumer_sleep) / 1000
|
|
106
|
+
self.max_jobs = int(max_jobs)
|
|
107
|
+
print(self.__dict__)
|
|
108
|
+
|
|
109
|
+
async def start(self):
|
|
110
|
+
for i in range(self.consumers):
|
|
111
|
+
self.add_worker(ExampleConsumer(engine=self, name=i))
|
|
112
|
+
|
|
113
|
+
self.add_worker(ExampleProducer(engine=self))
|
|
114
|
+
|
|
115
|
+
await super().start()
|
|
116
|
+
|
|
117
|
+
while self.is_work_pending():
|
|
118
|
+
await asyncio.sleep(1)
|
|
119
|
+
print(self.inspect())
|
|
120
|
+
print(f"DONE: {self.inspect()}")
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
if __name__ == "__main__":
|
|
124
|
+
logging.basicConfig(level=logging.DEBUG)
|
|
125
|
+
engine = ExampleEngine(*sys.argv[2:])
|
|
126
|
+
asyncio.run(engine.start())
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
from process_tests import TestProcess
|
|
4
|
+
from process_tests import dump_on_error
|
|
5
|
+
from process_tests import wait_for_strings
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_noprefetch_noskip():
|
|
9
|
+
with TestProcess(sys.executable, "-mexampleworker", "nope", "3", "0", "1000", "4") as target, dump_on_error(target.read):
|
|
10
|
+
wait_for_strings(
|
|
11
|
+
target.read,
|
|
12
|
+
5,
|
|
13
|
+
"PRODUCER fetch_task @ 1 RETURN",
|
|
14
|
+
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=1))",
|
|
15
|
+
"INFO:worker:ExampleProducer().run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=1).done of NoOpTask(id=1).",
|
|
16
|
+
"working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=1)",
|
|
17
|
+
"- performing...",
|
|
18
|
+
"CONSUMER 0 perform_work JOB 1 START",
|
|
19
|
+
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
20
|
+
"CONSUMER 0 save_result JOB 1",
|
|
21
|
+
"PRODUCER fetch_task @ 2 RETURN",
|
|
22
|
+
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=2))",
|
|
23
|
+
"INFO:worker:ExampleProducer().run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=2).done of NoOpTask(id=2).",
|
|
24
|
+
").run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=2)",
|
|
25
|
+
").run() - performing...",
|
|
26
|
+
"CONSUMER 1 perform_work JOB 2 START",
|
|
27
|
+
"CONSUMER 1 perform_work JOB 2 DONE",
|
|
28
|
+
"CONSUMER 1 save_result JOB 2",
|
|
29
|
+
"PRODUCER fetch_task @ 3 RETURN",
|
|
30
|
+
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=3))",
|
|
31
|
+
"INFO:worker:ExampleProducer().run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=3).done of NoOpTask(id=3).",
|
|
32
|
+
").run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=3)",
|
|
33
|
+
").run() - performing...",
|
|
34
|
+
"CONSUMER 2 perform_work JOB 3 START",
|
|
35
|
+
"CONSUMER 2 perform_work JOB 3 DONE",
|
|
36
|
+
"CONSUMER 2 save_result JOB 3",
|
|
37
|
+
"PRODUCER fetch_task @ 4 RETURN",
|
|
38
|
+
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=4))",
|
|
39
|
+
"INFO:worker:ExampleProducer().run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=4).done of NoOpTask(id=4).",
|
|
40
|
+
").run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=4)",
|
|
41
|
+
").run() - performing...",
|
|
42
|
+
"CONSUMER 0 perform_work JOB 4 START",
|
|
43
|
+
"CONSUMER 0 perform_work JOB 4 DONE",
|
|
44
|
+
"CONSUMER 0 save_result JOB 4",
|
|
45
|
+
"EXHAUSTED",
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_prefetch_skip():
|
|
50
|
+
with TestProcess(sys.executable, "-mexampleworker", "nope", "3", "0", "100", "4") as target, dump_on_error(target.read):
|
|
51
|
+
wait_for_strings(
|
|
52
|
+
target.read,
|
|
53
|
+
5,
|
|
54
|
+
"PRODUCER fetch_task @ 1 RETURN",
|
|
55
|
+
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=1))",
|
|
56
|
+
"INFO:worker:ExampleProducer().run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=1).done of NoOpTask(id=1).",
|
|
57
|
+
").run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=1)",
|
|
58
|
+
").run() - performing...",
|
|
59
|
+
"CONSUMER 0 perform_work JOB 1 START",
|
|
60
|
+
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
61
|
+
"CONSUMER 0 save_result JOB 1",
|
|
62
|
+
"PRODUCER fetch_task @ 2 RETURN",
|
|
63
|
+
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=2))",
|
|
64
|
+
"INFO:worker:ExampleProducer().run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=2).done of NoOpTask(id=2).",
|
|
65
|
+
").run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=2)",
|
|
66
|
+
").run() - performing...",
|
|
67
|
+
"CONSUMER 1 perform_work JOB 2 START",
|
|
68
|
+
"CONSUMER 1 perform_work JOB 2 DONE",
|
|
69
|
+
"CONSUMER 1 save_result JOB 2",
|
|
70
|
+
"PRODUCER fetch_task @ 3 RETURN",
|
|
71
|
+
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=3))",
|
|
72
|
+
"INFO:worker:ExampleProducer().run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=3).done of NoOpTask(id=3).",
|
|
73
|
+
").run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=3)",
|
|
74
|
+
").run() - performing...",
|
|
75
|
+
"CONSUMER 2 perform_work JOB 3 START",
|
|
76
|
+
"CONSUMER 2 perform_work JOB 3 DONE",
|
|
77
|
+
"CONSUMER 2 save_result JOB 3",
|
|
78
|
+
"PRODUCER fetch_task @ 4 RETURN",
|
|
79
|
+
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=4))",
|
|
80
|
+
"INFO:worker:ExampleProducer().run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=4).done of NoOpTask(id=4).",
|
|
81
|
+
").run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=4)",
|
|
82
|
+
").run() - performing...",
|
|
83
|
+
"CONSUMER 0 perform_work JOB 4 START",
|
|
84
|
+
"CONSUMER 0 perform_work JOB 4 DONE",
|
|
85
|
+
"CONSUMER 0 save_result JOB 4",
|
|
86
|
+
"PRODUCER fetch_task @ 5 EXHAUSTED",
|
|
87
|
+
)
|
{csu-2.1.2 → csu-2.1.4}/tox.ini
RENAMED
csu-2.1.2/tests/exampleworker.py
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import logging
|
|
3
|
-
from dataclasses import asdict
|
|
4
|
-
from dataclasses import dataclass
|
|
5
|
-
from enum import StrEnum
|
|
6
|
-
from logging import getLogger
|
|
7
|
-
|
|
8
|
-
from csu.worker.engine import AbstractConsumer
|
|
9
|
-
from csu.worker.engine import AbstractEngine
|
|
10
|
-
from csu.worker.engine import AbstractProducer
|
|
11
|
-
from csu.worker.engine import TaskProtocol
|
|
12
|
-
from csu.worker.job import Job
|
|
13
|
-
|
|
14
|
-
logger = getLogger(__name__)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@dataclass
|
|
18
|
-
class NoOpTask:
|
|
19
|
-
"""
|
|
20
|
-
Simple task model that doesn't store anything.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
id: int
|
|
24
|
-
|
|
25
|
-
@property
|
|
26
|
-
def job_kwargs(self):
|
|
27
|
-
return asdict(self)
|
|
28
|
-
|
|
29
|
-
async def done_watchdog(self, job: Job):
|
|
30
|
-
"""
|
|
31
|
-
Nothing done as there's no storage for this job.
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@dataclass
|
|
36
|
-
class ExampleJob[RequestStruct](Job):
|
|
37
|
-
id: int
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class ExampleResultType(StrEnum):
|
|
41
|
-
PENDING = "PENDING"
|
|
42
|
-
SUCCESS = "SUCCESS"
|
|
43
|
-
ERROR = "ERROR"
|
|
44
|
-
ABSENT = "ABSENT"
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
class ExampleProducer(
|
|
48
|
-
# NoPrefetchingProducerMixin,
|
|
49
|
-
AbstractProducer,
|
|
50
|
-
):
|
|
51
|
-
engine: ExampleEngine
|
|
52
|
-
job_class = ExampleJob
|
|
53
|
-
counter = 0
|
|
54
|
-
|
|
55
|
-
async def fetch_task(self) -> TaskProtocol | None:
|
|
56
|
-
self.counter += 1
|
|
57
|
-
logger.info("%s.fetch_task() -> %s", self, self.counter)
|
|
58
|
-
return NoOpTask(self.counter)
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
class ExampleConsumer(AbstractConsumer):
|
|
62
|
-
result_type_enum = ExampleResultType
|
|
63
|
-
engine: ExampleEngine
|
|
64
|
-
|
|
65
|
-
async def perform_work(self, job: ExampleJob):
|
|
66
|
-
logger.info("%s.perform_work(%s)", self, job)
|
|
67
|
-
await asyncio.sleep(1)
|
|
68
|
-
return {"foo": "bar"}
|
|
69
|
-
|
|
70
|
-
async def save_result(self, job: ExampleJob, /, **kwargs):
|
|
71
|
-
logger.info("%s.save_result(%s)", self, job)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
WORKERS = 5
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
class ExampleEngine(AbstractEngine):
|
|
78
|
-
queue_maxsize = WORKERS
|
|
79
|
-
|
|
80
|
-
async def start(self):
|
|
81
|
-
logger.info("Starting workers...")
|
|
82
|
-
|
|
83
|
-
for _ in range(WORKERS):
|
|
84
|
-
consumer = ExampleConsumer(engine=self)
|
|
85
|
-
consumer.start()
|
|
86
|
-
self.add_worker(consumer)
|
|
87
|
-
|
|
88
|
-
producer = ExampleProducer(engine=self)
|
|
89
|
-
producer.start()
|
|
90
|
-
self.add_worker(producer)
|
|
91
|
-
|
|
92
|
-
await super().start()
|
|
93
|
-
|
|
94
|
-
while self.is_work_pending():
|
|
95
|
-
await asyncio.sleep(1)
|
|
96
|
-
logger.info("%s", self.inspect())
|
|
97
|
-
logger.info("DONE: %s", self.inspect())
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
if __name__ == "__main__":
|
|
101
|
-
logging.basicConfig(level=logging.DEBUG)
|
|
102
|
-
engine = ExampleEngine()
|
|
103
|
-
print(engine)
|
|
104
|
-
asyncio.run(engine.start())
|
|
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.1.2 → csu-2.1.4}/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
|