csu 2.0.7__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.7 → csu-2.1.0}/.bumpversion.cfg +1 -1
  2. {csu-2.0.7 → csu-2.1.0}/.cookiecutterrc +1 -1
  3. {csu-2.0.7 → csu-2.1.0}/PKG-INFO +3 -3
  4. {csu-2.0.7 → csu-2.1.0}/README.rst +2 -2
  5. {csu-2.0.7 → csu-2.1.0}/pyproject.toml +1 -1
  6. {csu-2.0.7 → csu-2.1.0}/src/csu/__init__.py +1 -1
  7. {csu-2.0.7 → csu-2.1.0}/src/csu/worker/engine.py +83 -60
  8. {csu-2.0.7 → csu-2.1.0}/src/csu/worker/enums.py +4 -2
  9. {csu-2.0.7 → csu-2.1.0}/src/csu.egg-info/PKG-INFO +3 -3
  10. {csu-2.0.7 → csu-2.1.0}/tests/exampleworker.py +8 -4
  11. {csu-2.0.7 → csu-2.1.0}/tests/test_worker.py +4 -3
  12. {csu-2.0.7 → csu-2.1.0}/.coveragerc +0 -0
  13. {csu-2.0.7 → csu-2.1.0}/.editorconfig +0 -0
  14. {csu-2.0.7 → csu-2.1.0}/.github/workflows/github-actions.yml +0 -0
  15. {csu-2.0.7 → csu-2.1.0}/.pre-commit-config.yaml +0 -0
  16. {csu-2.0.7 → csu-2.1.0}/.taplo.toml +0 -0
  17. {csu-2.0.7 → csu-2.1.0}/AUTHORS.rst +0 -0
  18. {csu-2.0.7 → csu-2.1.0}/CHANGELOG.rst +0 -0
  19. {csu-2.0.7 → csu-2.1.0}/CONTRIBUTING.rst +0 -0
  20. {csu-2.0.7 → csu-2.1.0}/LICENSE +0 -0
  21. {csu-2.0.7 → csu-2.1.0}/MANIFEST.in +0 -0
  22. {csu-2.0.7 → csu-2.1.0}/ci/bootstrap.py +0 -0
  23. {csu-2.0.7 → csu-2.1.0}/ci/requirements.txt +0 -0
  24. {csu-2.0.7 → csu-2.1.0}/ci/templates/.github/workflows/github-actions.yml +0 -0
  25. {csu-2.0.7 → csu-2.1.0}/pytest.ini +0 -0
  26. {csu-2.0.7 → csu-2.1.0}/setup.cfg +0 -0
  27. {csu-2.0.7 → csu-2.1.0}/src/csu/admin.py +0 -0
  28. {csu-2.0.7 → csu-2.1.0}/src/csu/conf.py +0 -0
  29. {csu-2.0.7 → csu-2.1.0}/src/csu/consts.py +0 -0
  30. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/__init__.py +0 -0
  31. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/auth.py +0 -0
  32. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/fields.py +0 -0
  33. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/forms.py +0 -0
  34. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/perms.py +0 -0
  35. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/phonenumber.py +0 -0
  36. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/serializers.py +0 -0
  37. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/test_auth.py +0 -0
  38. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/test_fields.py +0 -0
  39. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/test_forms.py +0 -0
  40. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/test_phonenumber.py +0 -0
  41. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/test_serializers.py +0 -0
  42. {csu-2.0.7 → csu-2.1.0}/src/csu/drf/views.py +0 -0
  43. {csu-2.0.7 → csu-2.1.0}/src/csu/enums.py +0 -0
  44. {csu-2.0.7 → csu-2.1.0}/src/csu/env.py +0 -0
  45. {csu-2.0.7 → csu-2.1.0}/src/csu/exceptions.py +0 -0
  46. {csu-2.0.7 → csu-2.1.0}/src/csu/export.py +0 -0
  47. {csu-2.0.7 → csu-2.1.0}/src/csu/fixups.py +0 -0
  48. {csu-2.0.7 → csu-2.1.0}/src/csu/forms/__init__.py +0 -0
  49. {csu-2.0.7 → csu-2.1.0}/src/csu/forms/crispy.py +0 -0
  50. {csu-2.0.7 → csu-2.1.0}/src/csu/forms/fields.py +0 -0
  51. {csu-2.0.7 → csu-2.1.0}/src/csu/gettext.py +0 -0
  52. {csu-2.0.7 → csu-2.1.0}/src/csu/gettext_lazy.py +0 -0
  53. {csu-2.0.7 → csu-2.1.0}/src/csu/locale/ro/LC_MESSAGES/django.mo +0 -0
  54. {csu-2.0.7 → csu-2.1.0}/src/csu/locale/ro/LC_MESSAGES/django.po +0 -0
  55. {csu-2.0.7 → csu-2.1.0}/src/csu/logging.py +0 -0
  56. {csu-2.0.7 → csu-2.1.0}/src/csu/management.py +0 -0
  57. {csu-2.0.7 → csu-2.1.0}/src/csu/models.py +0 -0
  58. {csu-2.0.7 → csu-2.1.0}/src/csu/query.py +0 -0
  59. {csu-2.0.7 → csu-2.1.0}/src/csu/routers.py +0 -0
  60. {csu-2.0.7 → csu-2.1.0}/src/csu/service.py +0 -0
  61. {csu-2.0.7 → csu-2.1.0}/src/csu/templates/api_exception.html +0 -0
  62. {csu-2.0.7 → csu-2.1.0}/src/csu/templates/forms/widgets/opaquewidget.html +0 -0
  63. {csu-2.0.7 → csu-2.1.0}/src/csu/test_consts.py +0 -0
  64. {csu-2.0.7 → csu-2.1.0}/src/csu/test_env.py +0 -0
  65. {csu-2.0.7 → csu-2.1.0}/src/csu/test_exceptions.py +0 -0
  66. {csu-2.0.7 → csu-2.1.0}/src/csu/test_logging.py +0 -0
  67. {csu-2.0.7 → csu-2.1.0}/src/csu/test_management.py +0 -0
  68. {csu-2.0.7 → csu-2.1.0}/src/csu/test_service.py +0 -0
  69. {csu-2.0.7 → csu-2.1.0}/src/csu/test_timezones.py +0 -0
  70. {csu-2.0.7 → csu-2.1.0}/src/csu/test_utils.py +0 -0
  71. {csu-2.0.7 → csu-2.1.0}/src/csu/test_xml.py +0 -0
  72. {csu-2.0.7 → csu-2.1.0}/src/csu/timezones.py +0 -0
  73. {csu-2.0.7 → csu-2.1.0}/src/csu/utils.py +0 -0
  74. {csu-2.0.7 → csu-2.1.0}/src/csu/views.py +0 -0
  75. {csu-2.0.7 → csu-2.1.0}/src/csu/worker/__init__.py +0 -0
  76. {csu-2.0.7 → csu-2.1.0}/src/csu/worker/admin.py +0 -0
  77. {csu-2.0.7 → csu-2.1.0}/src/csu/worker/asgi.py +0 -0
  78. {csu-2.0.7 → csu-2.1.0}/src/csu/worker/job.py +0 -0
  79. {csu-2.0.7 → csu-2.1.0}/src/csu/worker/models.py +0 -0
  80. {csu-2.0.7 → csu-2.1.0}/src/csu/worker/registry.py +0 -0
  81. {csu-2.0.7 → csu-2.1.0}/src/csu/wsgi.py +0 -0
  82. {csu-2.0.7 → csu-2.1.0}/src/csu/xml.py +0 -0
  83. {csu-2.0.7 → csu-2.1.0}/src/csu.egg-info/SOURCES.txt +0 -0
  84. {csu-2.0.7 → csu-2.1.0}/src/csu.egg-info/dependency_links.txt +0 -0
  85. {csu-2.0.7 → csu-2.1.0}/src/csu.egg-info/requires.txt +0 -0
  86. {csu-2.0.7 → csu-2.1.0}/src/csu.egg-info/top_level.txt +0 -0
  87. {csu-2.0.7 → csu-2.1.0}/tests/cassettes/test_service/test_custom_context.yaml +0 -0
  88. {csu-2.0.7 → csu-2.1.0}/tests/cassettes/test_service/test_custom_context_0_retries.yaml +0 -0
  89. {csu-2.0.7 → csu-2.1.0}/tests/cassettes/test_service/test_decoding.yaml +0 -0
  90. {csu-2.0.7 → csu-2.1.0}/tests/cassettes/test_service/test_error.yaml +0 -0
  91. {csu-2.0.7 → csu-2.1.0}/tests/cassettes/test_service/test_logging.yaml +0 -0
  92. {csu-2.0.7 → csu-2.1.0}/tests/cassettes/test_service/test_redirects.yaml +0 -0
  93. {csu-2.0.7 → csu-2.1.0}/tests/cassettes/test_service_auth/test_async.yaml +0 -0
  94. {csu-2.0.7 → csu-2.1.0}/tests/cassettes/test_service_auth/test_sync.yaml +0 -0
  95. {csu-2.0.7 → csu-2.1.0}/tests/conftest.py +0 -0
  96. {csu-2.0.7 → csu-2.1.0}/tests/test_models.py +0 -0
  97. {csu-2.0.7 → csu-2.1.0}/tests/test_service.py +0 -0
  98. {csu-2.0.7 → csu-2.1.0}/tests/test_service_auth.py +0 -0
  99. {csu-2.0.7 → csu-2.1.0}/tests/test_views.py +0 -0
  100. {csu-2.0.7 → csu-2.1.0}/tests/testproject/__init__.py +0 -0
  101. {csu-2.0.7 → csu-2.1.0}/tests/testproject/fixtures/testuser.json +0 -0
  102. {csu-2.0.7 → csu-2.1.0}/tests/testproject/models.py +0 -0
  103. {csu-2.0.7 → csu-2.1.0}/tests/testproject/settings.py +0 -0
  104. {csu-2.0.7 → csu-2.1.0}/tests/testproject/urls.py +0 -0
  105. {csu-2.0.7 → csu-2.1.0}/tox.ini +0 -0
@@ -1,5 +1,5 @@
1
1
  [bumpversion]
2
- current_version = 2.0.7
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.7
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.7
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.7.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.7...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.7.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.7...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.7"
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.7"
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
@@ -112,6 +113,18 @@ class SleepingTimeRangeMixin:
112
113
  await asyncio.sleep(delta)
113
114
 
114
115
 
116
+ class NoPrefetchingProducerMixin:
117
+ state: WorkerState
118
+ engine: AbstractEngine
119
+ sleeping_timerange: tuple[time, time]
120
+
121
+ async def before(self):
122
+ # noinspection PyUnresolvedReferences
123
+ await super().before()
124
+ self.state = WorkerState.WAITING
125
+ await self.engine.wait_worker_ready()
126
+
127
+
115
128
  class SleepingCooldown(Flag):
116
129
  IDLE = auto()
117
130
  BUSY = auto()
@@ -141,7 +154,6 @@ class SleepingCooldownMixin:
141
154
  await asyncio.sleep(sleep_seconds)
142
155
 
143
156
 
144
- @dataclass(repr=False)
145
157
  class AbstractProducer(AbstractWorker):
146
158
  state = WorkerState.UNKNOWN
147
159
  engine: AbstractEngine
@@ -157,6 +169,9 @@ class AbstractProducer(AbstractWorker):
157
169
  Should raise error if there would be a spin-loop condition.
158
170
  """
159
171
 
172
+ def __init__(self, engine: AbstractEngine):
173
+ self.engine = engine
174
+
160
175
  def __str__(self):
161
176
  return f"{type(self).__name__}()"
162
177
 
@@ -186,7 +201,6 @@ class AbstractProducer(AbstractWorker):
186
201
  await self.after(idle=not task)
187
202
 
188
203
 
189
- @dataclass(repr=False)
190
204
  class AbstractConsumer(AbstractWorker):
191
205
  engine: AbstractEngine
192
206
 
@@ -199,6 +213,9 @@ class AbstractConsumer(AbstractWorker):
199
213
  async def save_result(self, job: Job, /, **kwargs):
200
214
  pass
201
215
 
216
+ def __init__(self, engine: AbstractEngine):
217
+ self.engine = engine
218
+
202
219
  def __str__(self):
203
220
  return f"{type(self).__name__}({id(self)})"
204
221
 
@@ -208,38 +225,39 @@ class AbstractConsumer(AbstractWorker):
208
225
  self.state = WorkerState.BEFORE
209
226
  await self.before()
210
227
  self.state = WorkerState.READY
211
- job: Job = await self.engine.pull()
212
- if job is None:
213
- break
214
- job.started.set_result(True)
215
- logger.info("%s.run() working on: %s", self, job)
216
- self.state = WorkerState.WORKING
217
- logger.info("%s.run() - performing...", self)
218
- try:
219
- fields = await self.perform_work(job)
220
- except Exception as exc:
221
- self.state = WorkerState.SAVING
222
- logger.error("%s.perform_work(%s) failed: %r", self, job, exc)
223
- result = await self.save_result(
224
- job,
225
- result_type=self.result_type_enum.ERROR,
226
- result_details=f"{exc!r}\n{traceback.format_exc()}",
227
- )
228
- idle = True
229
- else:
230
- self.state = WorkerState.SAVING
231
- result = await self.save_result(
232
- job,
233
- result_type=self.result_type_enum.SUCCESS if fields else self.result_type_enum.ABSENT,
234
- result_details=None if fields else repr(fields),
235
- **fields or {},
236
- )
237
- idle = False
238
- if job.done.cancelled():
239
- logger.warning("%s.run() - %s is cancelled.", self, job)
240
- else:
241
- job.done.set_result(result)
242
- 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
+
243
261
  if self.shutdown_requested:
244
262
  continue
245
263
  self.state = WorkerState.AFTER
@@ -250,21 +268,21 @@ class AbstractConsumer(AbstractWorker):
250
268
  pass
251
269
 
252
270
 
253
- @dataclass
254
271
  class AbstractEngine(ABC):
255
272
  workers: list[AbstractWorker]
256
- queue: Queue
273
+ queue: Queue = None
274
+ queue_maxsize: int
257
275
  shutdown_signal = signal.SIGINT
258
276
  shutdown_requested = False
259
277
  shutdown_task = None
260
278
 
261
279
  def __init__(self):
262
280
  self.workers = []
263
- self.queue = Queue()
264
- self.semaphore = Semaphore(value=0)
281
+ self.queue_awaited = Event()
282
+ self.queue_maxsize = 0
265
283
 
266
284
  def __str__(self):
267
- 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)"
268
286
 
269
287
  def is_work_pending(self):
270
288
  if any(worker for worker in self.workers if worker.state == WorkerState.WORKING):
@@ -273,26 +291,31 @@ class AbstractEngine(ABC):
273
291
  return True
274
292
  return not self.shutdown_requested
275
293
 
276
- async def push(self, job: Job) -> None:
294
+ async def push(self, job: Job | None) -> None:
277
295
  logger.info("%s.push(%s)", self, job)
278
- await self.semaphore.acquire()
279
296
  await self.queue.put(job)
280
297
 
281
- def push_nowait(self, job: Job | None) -> None:
282
- """
283
- None means a shutdown request.
284
- """
285
- logger.info("%s.push_nowait(%s)", self, job)
286
- self.queue.put_nowait(job)
287
-
288
- async def pull(self) -> Job | None:
289
- self.semaphore.release()
290
- return await self.queue.get()
291
-
292
- def mark_done(self):
293
- 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)
294
316
 
295
317
  async def start(self):
318
+ self.queue = Queue(maxsize=self.queue_maxsize)
296
319
  signal.signal(self.shutdown_signal, self.shutdown_handler)
297
320
 
298
321
  def shutdown_handler(self, sig, frame):
@@ -314,20 +337,20 @@ class AbstractEngine(ABC):
314
337
  for worker in self.workers:
315
338
  worker.shutdown_requested = True
316
339
  if isinstance(worker, AbstractConsumer):
317
- self.push_nowait(None)
340
+ await self.push(None)
318
341
  else:
319
342
  while self.queue.qsize() > 0:
320
343
  item = self.queue.get_nowait()
321
344
  if item is not None:
322
- logger.critical("%s unprocessed queue item: %r", item)
345
+ logger.critical("%s unprocessed queue item: %r", self, item)
323
346
  for worker in self.workers:
324
347
  worker.runner.cancel()
325
348
  await asyncio.gather(*[worker.runner for worker in self.workers], return_exceptions=True)
326
349
  self.workers.clear()
327
350
  logger.info("%s shutdown complete.", self)
328
351
 
329
- def report(self, inspect: defaultdict[str, int]):
330
- 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()
331
354
 
332
355
  def inspect(self) -> dict[str, dict[str, int]]:
333
356
  result = defaultdict(partial(defaultdict, int))
@@ -1,9 +1,11 @@
1
- from enum import IntEnum
1
+ from enum import Enum
2
2
  from enum import auto
3
3
 
4
4
 
5
- class WorkerState(IntEnum):
5
+ class WorkerState(Enum):
6
6
  UNKNOWN = auto()
7
+
8
+ WAITING = auto()
7
9
  READY = auto()
8
10
  WORKING = auto()
9
11
  SAVING = auto()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: csu
3
- Version: 2.0.7
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.7.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.7...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/
@@ -44,7 +44,10 @@ class ExampleResultType(StrEnum):
44
44
  ABSENT = "ABSENT"
45
45
 
46
46
 
47
- class ExampleProducer(AbstractProducer):
47
+ class ExampleProducer(
48
+ # NoPrefetchingProducerMixin,
49
+ AbstractProducer,
50
+ ):
48
51
  engine: ExampleEngine
49
52
  job_class = ExampleJob
50
53
  counter = 0
@@ -75,17 +78,18 @@ class ExampleEngine(AbstractEngine):
75
78
  queue_maxsize = WORKERS
76
79
 
77
80
  async def start(self):
78
- await super().start()
79
81
  logger.info("Starting workers...")
80
82
 
81
83
  for _ in range(WORKERS):
82
84
  consumer = ExampleConsumer(engine=self)
83
85
  consumer.start()
84
- self.workers.append(consumer)
86
+ self.add_worker(consumer)
85
87
 
86
88
  producer = ExampleProducer(engine=self)
87
89
  producer.start()
88
- self.workers.append(producer)
90
+ self.add_worker(producer)
91
+
92
+ await super().start()
89
93
 
90
94
  while self.is_work_pending():
91
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