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.
Files changed (105) hide show
  1. {csu-2.0.8 → csu-2.1.0}/.bumpversion.cfg +1 -1
  2. {csu-2.0.8 → csu-2.1.0}/.cookiecutterrc +1 -1
  3. {csu-2.0.8 → csu-2.1.0}/PKG-INFO +3 -3
  4. {csu-2.0.8 → csu-2.1.0}/README.rst +2 -2
  5. {csu-2.0.8 → csu-2.1.0}/pyproject.toml +1 -1
  6. {csu-2.0.8 → csu-2.1.0}/src/csu/__init__.py +1 -1
  7. {csu-2.0.8 → csu-2.1.0}/src/csu/worker/engine.py +72 -64
  8. {csu-2.0.8 → csu-2.1.0}/src/csu.egg-info/PKG-INFO +3 -3
  9. {csu-2.0.8 → csu-2.1.0}/tests/exampleworker.py +9 -6
  10. {csu-2.0.8 → csu-2.1.0}/tests/test_worker.py +4 -3
  11. {csu-2.0.8 → csu-2.1.0}/.coveragerc +0 -0
  12. {csu-2.0.8 → csu-2.1.0}/.editorconfig +0 -0
  13. {csu-2.0.8 → csu-2.1.0}/.github/workflows/github-actions.yml +0 -0
  14. {csu-2.0.8 → csu-2.1.0}/.pre-commit-config.yaml +0 -0
  15. {csu-2.0.8 → csu-2.1.0}/.taplo.toml +0 -0
  16. {csu-2.0.8 → csu-2.1.0}/AUTHORS.rst +0 -0
  17. {csu-2.0.8 → csu-2.1.0}/CHANGELOG.rst +0 -0
  18. {csu-2.0.8 → csu-2.1.0}/CONTRIBUTING.rst +0 -0
  19. {csu-2.0.8 → csu-2.1.0}/LICENSE +0 -0
  20. {csu-2.0.8 → csu-2.1.0}/MANIFEST.in +0 -0
  21. {csu-2.0.8 → csu-2.1.0}/ci/bootstrap.py +0 -0
  22. {csu-2.0.8 → csu-2.1.0}/ci/requirements.txt +0 -0
  23. {csu-2.0.8 → csu-2.1.0}/ci/templates/.github/workflows/github-actions.yml +0 -0
  24. {csu-2.0.8 → csu-2.1.0}/pytest.ini +0 -0
  25. {csu-2.0.8 → csu-2.1.0}/setup.cfg +0 -0
  26. {csu-2.0.8 → csu-2.1.0}/src/csu/admin.py +0 -0
  27. {csu-2.0.8 → csu-2.1.0}/src/csu/conf.py +0 -0
  28. {csu-2.0.8 → csu-2.1.0}/src/csu/consts.py +0 -0
  29. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/__init__.py +0 -0
  30. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/auth.py +0 -0
  31. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/fields.py +0 -0
  32. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/forms.py +0 -0
  33. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/perms.py +0 -0
  34. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/phonenumber.py +0 -0
  35. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/serializers.py +0 -0
  36. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/test_auth.py +0 -0
  37. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/test_fields.py +0 -0
  38. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/test_forms.py +0 -0
  39. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/test_phonenumber.py +0 -0
  40. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/test_serializers.py +0 -0
  41. {csu-2.0.8 → csu-2.1.0}/src/csu/drf/views.py +0 -0
  42. {csu-2.0.8 → csu-2.1.0}/src/csu/enums.py +0 -0
  43. {csu-2.0.8 → csu-2.1.0}/src/csu/env.py +0 -0
  44. {csu-2.0.8 → csu-2.1.0}/src/csu/exceptions.py +0 -0
  45. {csu-2.0.8 → csu-2.1.0}/src/csu/export.py +0 -0
  46. {csu-2.0.8 → csu-2.1.0}/src/csu/fixups.py +0 -0
  47. {csu-2.0.8 → csu-2.1.0}/src/csu/forms/__init__.py +0 -0
  48. {csu-2.0.8 → csu-2.1.0}/src/csu/forms/crispy.py +0 -0
  49. {csu-2.0.8 → csu-2.1.0}/src/csu/forms/fields.py +0 -0
  50. {csu-2.0.8 → csu-2.1.0}/src/csu/gettext.py +0 -0
  51. {csu-2.0.8 → csu-2.1.0}/src/csu/gettext_lazy.py +0 -0
  52. {csu-2.0.8 → csu-2.1.0}/src/csu/locale/ro/LC_MESSAGES/django.mo +0 -0
  53. {csu-2.0.8 → csu-2.1.0}/src/csu/locale/ro/LC_MESSAGES/django.po +0 -0
  54. {csu-2.0.8 → csu-2.1.0}/src/csu/logging.py +0 -0
  55. {csu-2.0.8 → csu-2.1.0}/src/csu/management.py +0 -0
  56. {csu-2.0.8 → csu-2.1.0}/src/csu/models.py +0 -0
  57. {csu-2.0.8 → csu-2.1.0}/src/csu/query.py +0 -0
  58. {csu-2.0.8 → csu-2.1.0}/src/csu/routers.py +0 -0
  59. {csu-2.0.8 → csu-2.1.0}/src/csu/service.py +0 -0
  60. {csu-2.0.8 → csu-2.1.0}/src/csu/templates/api_exception.html +0 -0
  61. {csu-2.0.8 → csu-2.1.0}/src/csu/templates/forms/widgets/opaquewidget.html +0 -0
  62. {csu-2.0.8 → csu-2.1.0}/src/csu/test_consts.py +0 -0
  63. {csu-2.0.8 → csu-2.1.0}/src/csu/test_env.py +0 -0
  64. {csu-2.0.8 → csu-2.1.0}/src/csu/test_exceptions.py +0 -0
  65. {csu-2.0.8 → csu-2.1.0}/src/csu/test_logging.py +0 -0
  66. {csu-2.0.8 → csu-2.1.0}/src/csu/test_management.py +0 -0
  67. {csu-2.0.8 → csu-2.1.0}/src/csu/test_service.py +0 -0
  68. {csu-2.0.8 → csu-2.1.0}/src/csu/test_timezones.py +0 -0
  69. {csu-2.0.8 → csu-2.1.0}/src/csu/test_utils.py +0 -0
  70. {csu-2.0.8 → csu-2.1.0}/src/csu/test_xml.py +0 -0
  71. {csu-2.0.8 → csu-2.1.0}/src/csu/timezones.py +0 -0
  72. {csu-2.0.8 → csu-2.1.0}/src/csu/utils.py +0 -0
  73. {csu-2.0.8 → csu-2.1.0}/src/csu/views.py +0 -0
  74. {csu-2.0.8 → csu-2.1.0}/src/csu/worker/__init__.py +0 -0
  75. {csu-2.0.8 → csu-2.1.0}/src/csu/worker/admin.py +0 -0
  76. {csu-2.0.8 → csu-2.1.0}/src/csu/worker/asgi.py +0 -0
  77. {csu-2.0.8 → csu-2.1.0}/src/csu/worker/enums.py +0 -0
  78. {csu-2.0.8 → csu-2.1.0}/src/csu/worker/job.py +0 -0
  79. {csu-2.0.8 → csu-2.1.0}/src/csu/worker/models.py +0 -0
  80. {csu-2.0.8 → csu-2.1.0}/src/csu/worker/registry.py +0 -0
  81. {csu-2.0.8 → csu-2.1.0}/src/csu/wsgi.py +0 -0
  82. {csu-2.0.8 → csu-2.1.0}/src/csu/xml.py +0 -0
  83. {csu-2.0.8 → csu-2.1.0}/src/csu.egg-info/SOURCES.txt +0 -0
  84. {csu-2.0.8 → csu-2.1.0}/src/csu.egg-info/dependency_links.txt +0 -0
  85. {csu-2.0.8 → csu-2.1.0}/src/csu.egg-info/requires.txt +0 -0
  86. {csu-2.0.8 → csu-2.1.0}/src/csu.egg-info/top_level.txt +0 -0
  87. {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_custom_context.yaml +0 -0
  88. {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_custom_context_0_retries.yaml +0 -0
  89. {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_decoding.yaml +0 -0
  90. {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_error.yaml +0 -0
  91. {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_logging.yaml +0 -0
  92. {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service/test_redirects.yaml +0 -0
  93. {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service_auth/test_async.yaml +0 -0
  94. {csu-2.0.8 → csu-2.1.0}/tests/cassettes/test_service_auth/test_sync.yaml +0 -0
  95. {csu-2.0.8 → csu-2.1.0}/tests/conftest.py +0 -0
  96. {csu-2.0.8 → csu-2.1.0}/tests/test_models.py +0 -0
  97. {csu-2.0.8 → csu-2.1.0}/tests/test_service.py +0 -0
  98. {csu-2.0.8 → csu-2.1.0}/tests/test_service_auth.py +0 -0
  99. {csu-2.0.8 → csu-2.1.0}/tests/test_views.py +0 -0
  100. {csu-2.0.8 → csu-2.1.0}/tests/testproject/__init__.py +0 -0
  101. {csu-2.0.8 → csu-2.1.0}/tests/testproject/fixtures/testuser.json +0 -0
  102. {csu-2.0.8 → csu-2.1.0}/tests/testproject/models.py +0 -0
  103. {csu-2.0.8 → csu-2.1.0}/tests/testproject/settings.py +0 -0
  104. {csu-2.0.8 → csu-2.1.0}/tests/testproject/urls.py +0 -0
  105. {csu-2.0.8 → csu-2.1.0}/tox.ini +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 2.0.8
2
+ current_version = 2.1.0
3
3
  commit = True
4
4
  tag = True
5
5
 
@@ -40,7 +40,7 @@ default_context:
40
40
  sphinx_theme: furo
41
41
  test_matrix_separate_coverage: 'no'
42
42
  tests_inside_package: 'yes'
43
- version: 2.0.8
43
+ version: 2.1.0
44
44
  version_manager: bump2version
45
45
  website: https://blog.ionelmc.ro
46
46
  year_from: '2024'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csu
3
- Version: 2.0.8
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.8.svg
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.8...main
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.8.svg
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.8...main
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/
@@ -12,7 +12,7 @@ dynamic = [
12
12
  "readme",
13
13
  ]
14
14
  name = "csu"
15
- version = "2.0.8"
15
+ version = "2.1.0"
16
16
  license = "BSD-2-Clause"
17
17
  license-files = ["LICENSE"]
18
18
  description = "Clean Slate Utils - bunch of utility code, mostly Django/DRF specific."
@@ -2,4 +2,4 @@
2
2
  a.k.a Clean Slate Utils
3
3
  """
4
4
 
5
- __version__ = "2.0.8"
5
+ __version__ = "2.1.0"
@@ -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 dataclasses import dataclass
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.wait_pending()
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 = await self.engine.pull()
224
- if job is None:
225
- break
226
- job.started.set_result(True)
227
- logger.info("%s.run() working on: %s", self, job)
228
- self.state = WorkerState.WORKING
229
- logger.info("%s.run() - performing...", self)
230
- try:
231
- fields = await self.perform_work(job)
232
- except Exception as exc:
233
- self.state = WorkerState.SAVING
234
- logger.error("%s.perform_work(%s) failed: %r", self, job, exc)
235
- result = await self.save_result(
236
- job,
237
- result_type=self.result_type_enum.ERROR,
238
- result_details=f"{exc!r}\n{traceback.format_exc()}",
239
- )
240
- idle = True
241
- else:
242
- self.state = WorkerState.SAVING
243
- result = await self.save_result(
244
- job,
245
- result_type=self.result_type_enum.SUCCESS if fields else self.result_type_enum.ABSENT,
246
- result_details=None if fields else repr(fields),
247
- **fields or {},
248
- )
249
- idle = False
250
- if job.done.cancelled():
251
- logger.warning("%s.run() - %s is cancelled.", self, job)
252
- else:
253
- job.done.set_result(result)
254
- self.engine.mark_done()
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.queue = Queue()
276
- self.semaphore = Semaphore(value=0)
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 wait_pending(self):
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
- def push_nowait(self, job: Job | None) -> None:
297
- """
298
- None means a shutdown request.
299
- """
300
- logger.info("%s.push_nowait(%s)", self, job)
301
- self.queue.put_nowait(job)
302
-
303
- async def pull(self) -> Job | None:
304
- self.semaphore.release()
305
- return await self.queue.get()
306
-
307
- def mark_done(self):
308
- self.queue.task_done()
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.push_nowait(None)
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, int]):
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.8
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.8.svg
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.8...main
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(NoPrefetchingProducerMixin, AbstractProducer):
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 = 1
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.workers.append(consumer)
86
+ self.add_worker(consumer)
86
87
 
87
88
  producer = ExampleProducer(engine=self)
88
89
  producer.start()
89
- self.workers.append(producer)
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.workers.append(worker)
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.push_nowait(job1)
56
+ await engine.push(job1)
56
57
  job2 = SleepyJob(0)
57
- engine.push_nowait(job2)
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
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