csu 2.2.4__tar.gz → 2.3.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.2.4 → csu-2.3.0}/.bumpversion.cfg +1 -1
- {csu-2.2.4 → csu-2.3.0}/.cookiecutterrc +1 -1
- {csu-2.2.4 → csu-2.3.0}/PKG-INFO +3 -3
- {csu-2.2.4 → csu-2.3.0}/README.rst +2 -2
- {csu-2.2.4 → csu-2.3.0}/pyproject.toml +1 -1
- {csu-2.2.4 → csu-2.3.0}/src/csu/__init__.py +1 -1
- {csu-2.2.4 → csu-2.3.0}/src/csu/export.py +3 -4
- {csu-2.2.4 → csu-2.3.0}/src/csu/test_logging.py +1 -1
- {csu-2.2.4 → csu-2.3.0}/src/csu/test_management.py +1 -1
- {csu-2.2.4 → csu-2.3.0}/src/csu/test_service.py +4 -5
- {csu-2.2.4 → csu-2.3.0}/src/csu/worker/__init__.py +0 -4
- {csu-2.2.4 → csu-2.3.0}/src/csu/worker/admin.py +1 -2
- csu-2.3.0/src/csu/worker/asgi.py +74 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/worker/engine.py +5 -4
- {csu-2.2.4 → csu-2.3.0}/src/csu/worker/models.py +3 -1
- {csu-2.2.4 → csu-2.3.0}/src/csu.egg-info/PKG-INFO +3 -3
- csu-2.3.0/tests/test_exampleworker.py +164 -0
- {csu-2.2.4 → csu-2.3.0}/tests/test_worker.py +1 -1
- csu-2.2.4/src/csu/worker/asgi.py +0 -36
- csu-2.2.4/tests/test_exampleworker.py +0 -164
- {csu-2.2.4 → csu-2.3.0}/.coveragerc +0 -0
- {csu-2.2.4 → csu-2.3.0}/.editorconfig +0 -0
- {csu-2.2.4 → csu-2.3.0}/.github/workflows/github-actions.yml +0 -0
- {csu-2.2.4 → csu-2.3.0}/.pre-commit-config.yaml +0 -0
- {csu-2.2.4 → csu-2.3.0}/.taplo.toml +0 -0
- {csu-2.2.4 → csu-2.3.0}/AUTHORS.rst +0 -0
- {csu-2.2.4 → csu-2.3.0}/CHANGELOG.rst +0 -0
- {csu-2.2.4 → csu-2.3.0}/CONTRIBUTING.rst +0 -0
- {csu-2.2.4 → csu-2.3.0}/LICENSE +0 -0
- {csu-2.2.4 → csu-2.3.0}/MANIFEST.in +0 -0
- {csu-2.2.4 → csu-2.3.0}/ci/bootstrap.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/ci/requirements.txt +0 -0
- {csu-2.2.4 → csu-2.3.0}/ci/templates/.github/workflows/github-actions.yml +0 -0
- {csu-2.2.4 → csu-2.3.0}/pytest.ini +0 -0
- {csu-2.2.4 → csu-2.3.0}/setup.cfg +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/admin.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/conf.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/consts.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/__init__.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/auth.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/fields.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/forms.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/perms.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/phonenumber.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/serializers.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/test_auth.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/test_fields.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/test_forms.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/test_phonenumber.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/test_serializers.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/drf/views.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/enums.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/env.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/exceptions.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/fixups.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/forms/__init__.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/forms/crispy.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/forms/fields.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/gettext.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/gettext_lazy.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/locale/ro/LC_MESSAGES/django.mo +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/locale/ro/LC_MESSAGES/django.po +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/logging.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/management.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/models.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/query.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/routers.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/service.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/templates/api_exception.html +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/templates/forms/widgets/opaquewidget.html +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/test_consts.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/test_env.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/test_exceptions.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/test_timezones.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/test_utils.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/test_xml.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/timezones.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/utils.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/views.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/worker/enums.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/worker/job.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/worker/registry.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/wsgi.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu/xml.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu.egg-info/SOURCES.txt +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu.egg-info/dependency_links.txt +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu.egg-info/requires.txt +0 -0
- {csu-2.2.4 → csu-2.3.0}/src/csu.egg-info/top_level.txt +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/cassettes/test_service/test_custom_context.yaml +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/cassettes/test_service/test_custom_context_0_retries.yaml +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/cassettes/test_service/test_decoding.yaml +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/cassettes/test_service/test_error.yaml +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/cassettes/test_service/test_logging.yaml +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/cassettes/test_service/test_redirects.yaml +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/cassettes/test_service_auth/test_async.yaml +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/cassettes/test_service_auth/test_sync.yaml +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/conftest.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/exampleworker.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/test_models.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/test_service.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/test_service_auth.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/test_views.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/testproject/__init__.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/testproject/fixtures/testuser.json +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/testproject/models.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/testproject/settings.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/tests/testproject/urls.py +0 -0
- {csu-2.2.4 → csu-2.3.0}/tox.ini +0 -0
{csu-2.2.4 → csu-2.3.0}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csu
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.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.
|
|
80
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.3.0.svg
|
|
81
81
|
:alt: Commits since latest release
|
|
82
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.
|
|
82
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.3.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.
|
|
37
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.3.0.svg
|
|
38
38
|
:alt: Commits since latest release
|
|
39
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.
|
|
39
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.3.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/
|
|
@@ -26,10 +26,9 @@ from import_export.widgets import Widget
|
|
|
26
26
|
from rest_framework import serializers
|
|
27
27
|
from tablib import InvalidDimensions
|
|
28
28
|
|
|
29
|
-
from
|
|
30
|
-
from
|
|
31
|
-
from
|
|
32
|
-
|
|
29
|
+
from .gettext import _
|
|
30
|
+
from .timezones import adjust_dt
|
|
31
|
+
from .timezones import now
|
|
33
32
|
from .timezones import today
|
|
34
33
|
|
|
35
34
|
EXPORT_FORMATS = {
|
|
@@ -4,13 +4,12 @@ import time_machine
|
|
|
4
4
|
from niquests import PreparedRequest
|
|
5
5
|
from niquests import Response
|
|
6
6
|
|
|
7
|
-
from csu.service import AsyncHTTPService
|
|
8
|
-
from csu.service import AsyncHTTPServiceContext
|
|
9
|
-
from csu.service import HTTPService
|
|
10
|
-
from csu.service import HTTPServiceContext
|
|
11
|
-
|
|
12
7
|
from . import exceptions
|
|
13
8
|
from .exceptions import get_event_id
|
|
9
|
+
from .service import AsyncHTTPService
|
|
10
|
+
from .service import AsyncHTTPServiceContext
|
|
11
|
+
from .service import HTTPService
|
|
12
|
+
from .service import HTTPServiceContext
|
|
14
13
|
|
|
15
14
|
|
|
16
15
|
def test_get_accident_id(monkeypatch):
|
|
@@ -8,8 +8,6 @@ from django.utils.html import format_html
|
|
|
8
8
|
from django.utils.safestring import mark_safe
|
|
9
9
|
from import_export.admin import ImportExportMixin
|
|
10
10
|
|
|
11
|
-
from csu.timezones import now
|
|
12
|
-
|
|
13
11
|
from ..export import ChoiceWidget
|
|
14
12
|
from ..export import FullFilenameExportAdminMixin
|
|
15
13
|
from ..export import SmartModelResource
|
|
@@ -17,6 +15,7 @@ from ..gettext import _
|
|
|
17
15
|
from .models import AbstractTask
|
|
18
16
|
from .models import QueueState
|
|
19
17
|
from .models import ResultType
|
|
18
|
+
from .timezones import now
|
|
20
19
|
|
|
21
20
|
|
|
22
21
|
class AbstractTaskResource(SmartModelResource):
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from logging import getLogger
|
|
2
|
+
|
|
3
|
+
import django
|
|
4
|
+
from asgiref.typing import ASGIReceiveCallable
|
|
5
|
+
from asgiref.typing import ASGIReceiveEvent
|
|
6
|
+
from asgiref.typing import ASGISendCallable
|
|
7
|
+
from asgiref.typing import LifespanShutdownCompleteEvent
|
|
8
|
+
from asgiref.typing import LifespanShutdownFailedEvent
|
|
9
|
+
from asgiref.typing import LifespanStartupCompleteEvent
|
|
10
|
+
from asgiref.typing import LifespanStartupFailedEvent
|
|
11
|
+
from django.core.handlers.asgi import ASGIHandler
|
|
12
|
+
|
|
13
|
+
from ..utils import lazy_import_classproperty
|
|
14
|
+
|
|
15
|
+
logger = getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class WorkerLifespanASGIHandler(ASGIHandler):
|
|
19
|
+
async def __call__(self, scope, receive: ASGIReceiveCallable, send) -> None:
|
|
20
|
+
if scope["type"] == "lifespan":
|
|
21
|
+
await self.handle_lifespan(receive, send)
|
|
22
|
+
else:
|
|
23
|
+
await super().__call__(scope, receive, send)
|
|
24
|
+
|
|
25
|
+
engine_registry = lazy_import_classproperty("csu.worker.registry.REGISTRY")
|
|
26
|
+
|
|
27
|
+
async def handle_start_engines(self):
|
|
28
|
+
registry = self.engine_registry
|
|
29
|
+
logger.info("Found %s registered engines.", len(registry))
|
|
30
|
+
for module_name, engine in registry.items():
|
|
31
|
+
logger.info("Starting engine for %s: %s...", module_name, engine)
|
|
32
|
+
if engine.shutdown_on:
|
|
33
|
+
raise RuntimeError(f"Engine {type(engine)} shutdown_on should be empty, not {engine.shutdown_on}!")
|
|
34
|
+
await engine.start()
|
|
35
|
+
logger.info(
|
|
36
|
+
"Started %s engines: \n %s",
|
|
37
|
+
len(registry),
|
|
38
|
+
"\n ".join(f"{module_name}: {engine}" for module_name, engine in registry.items()),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
async def handle_stop_engines(self):
|
|
42
|
+
registry = self.engine_registry
|
|
43
|
+
logger.info("Stopping %s registered engines.", len(registry))
|
|
44
|
+
for engine in registry.values():
|
|
45
|
+
await engine.stop(graceful=True)
|
|
46
|
+
|
|
47
|
+
async def handle_lifespan(self, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None:
|
|
48
|
+
while True:
|
|
49
|
+
message: ASGIReceiveEvent = await receive()
|
|
50
|
+
match message["type"]:
|
|
51
|
+
case "lifespan.startup":
|
|
52
|
+
try:
|
|
53
|
+
await self.handle_start_engines()
|
|
54
|
+
except Exception as exc:
|
|
55
|
+
await send(LifespanStartupFailedEvent(type="lifespan.startup.failed", message=str(exc)))
|
|
56
|
+
raise
|
|
57
|
+
else:
|
|
58
|
+
await send(LifespanStartupCompleteEvent(type="lifespan.startup.complete"))
|
|
59
|
+
case "lifespan.shutdown":
|
|
60
|
+
try:
|
|
61
|
+
await self.handle_stop_engines()
|
|
62
|
+
except Exception as exc:
|
|
63
|
+
await send(LifespanShutdownFailedEvent(type="lifespan.shutdown.failed", message=str(exc)))
|
|
64
|
+
raise
|
|
65
|
+
else:
|
|
66
|
+
await send(LifespanShutdownCompleteEvent(type="lifespan.shutdown.complete"))
|
|
67
|
+
return
|
|
68
|
+
case _:
|
|
69
|
+
raise ValueError(f"Unexpected message type: {message['type']!r}", message)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_worker_lifespan_application() -> WorkerLifespanASGIHandler:
|
|
73
|
+
django.setup(set_prefix=False)
|
|
74
|
+
return WorkerLifespanASGIHandler()
|
|
@@ -18,6 +18,7 @@ from datetime import timedelta
|
|
|
18
18
|
from enum import Flag
|
|
19
19
|
from enum import auto
|
|
20
20
|
from functools import partial
|
|
21
|
+
from logging import getLogger
|
|
21
22
|
from random import randint
|
|
22
23
|
from typing import ClassVar
|
|
23
24
|
from typing import Protocol
|
|
@@ -26,10 +27,11 @@ from datetimerange import DateTimeRange
|
|
|
26
27
|
|
|
27
28
|
from ..exceptions import InternalServiceError
|
|
28
29
|
from ..timezones import naivenow
|
|
29
|
-
from . import logger
|
|
30
30
|
from .enums import WorkerState
|
|
31
31
|
from .job import Job
|
|
32
32
|
|
|
33
|
+
logger = getLogger(__name__)
|
|
34
|
+
|
|
33
35
|
|
|
34
36
|
class TaskProtocol(Protocol):
|
|
35
37
|
job_kwargs: dict
|
|
@@ -246,7 +248,6 @@ class AbstractConsumer(AbstractWorker):
|
|
|
246
248
|
result = await self.save_result(
|
|
247
249
|
job,
|
|
248
250
|
result_type=self.result_type_enum.SUCCESS if fields else self.result_type_enum.ABSENT,
|
|
249
|
-
result_details=None if fields else repr(fields),
|
|
250
251
|
**fields or {},
|
|
251
252
|
)
|
|
252
253
|
idle = False
|
|
@@ -270,7 +271,7 @@ class AbstractEngine(ABC):
|
|
|
270
271
|
workers: list[AbstractWorker]
|
|
271
272
|
queue: Queue = None
|
|
272
273
|
queue_sem: Semaphore
|
|
273
|
-
shutdown_on: Iterable[
|
|
274
|
+
shutdown_on: Iterable[signal.Signals] = (signal.SIGINT, signal.SIGTERM)
|
|
274
275
|
shutdown_requested = False
|
|
275
276
|
shutdown_tasks: ClassVar[dict[bool, asyncio.Task]] = {}
|
|
276
277
|
if sys.platform == "win32":
|
|
@@ -322,7 +323,7 @@ class AbstractEngine(ABC):
|
|
|
322
323
|
for sig in self.shutdown_on:
|
|
323
324
|
loop.add_signal_handler(sig, self.shutdown_handler, sig)
|
|
324
325
|
|
|
325
|
-
def shutdown_handler(self, sig):
|
|
326
|
+
def shutdown_handler(self, sig: signal.Signals) -> None:
|
|
326
327
|
sig_name = sig.name
|
|
327
328
|
graceful = not self.shutdown_requested
|
|
328
329
|
logger.info("%s.shutdown_handler(%s) %s", self, sig_name, "GRACEFUL" if graceful else "FORCED")
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from asyncio import CancelledError
|
|
3
3
|
from datetime import timedelta
|
|
4
|
+
from logging import getLogger
|
|
4
5
|
from random import SystemRandom
|
|
5
6
|
|
|
6
7
|
from asgiref.sync import sync_to_async
|
|
@@ -11,11 +12,12 @@ from django.db.models import F
|
|
|
11
12
|
from django.db.models import Q
|
|
12
13
|
|
|
13
14
|
from ..timezones import utcnow
|
|
14
|
-
from . import logger
|
|
15
15
|
from .engine import AbstractConsumer
|
|
16
16
|
from .engine import AbstractProducer
|
|
17
17
|
from .job import Job
|
|
18
18
|
|
|
19
|
+
logger = getLogger(__name__)
|
|
20
|
+
|
|
19
21
|
|
|
20
22
|
def get_random_float(system_random=SystemRandom()): # noqa: B008
|
|
21
23
|
return system_random.random()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: csu
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.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.
|
|
80
|
+
.. |commits-since| image:: https://img.shields.io/github/commits-since/ionelmc/python-csu/v2.3.0.svg
|
|
81
81
|
:alt: Commits since latest release
|
|
82
|
-
:target: https://github.com/ionelmc/python-csu/compare/v2.
|
|
82
|
+
:target: https://github.com/ionelmc/python-csu/compare/v2.3.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/
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import signal
|
|
2
|
+
import sys
|
|
3
|
+
import time
|
|
4
|
+
|
|
5
|
+
from process_tests import TestProcess
|
|
6
|
+
from process_tests import dump_on_error
|
|
7
|
+
from process_tests import wait_for_strings
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def test_noskip():
|
|
11
|
+
with TestProcess(sys.executable, "-mexampleworker", "3", "0", "1000", "4") as target, dump_on_error(target.read):
|
|
12
|
+
wait_for_strings(
|
|
13
|
+
target.read,
|
|
14
|
+
5,
|
|
15
|
+
"INFO:csu.worker.engine:ExampleConsumer(1/UNKNOWN) started.",
|
|
16
|
+
"INFO:csu.worker.engine:ExampleConsumer(2/UNKNOWN) started.",
|
|
17
|
+
"INFO:csu.worker.engine:ExampleConsumer(3/UNKNOWN) started.",
|
|
18
|
+
"INFO:csu.worker.engine:ExampleProducer(1/UNKNOWN) started.",
|
|
19
|
+
"INFO:csu.worker.engine:ExampleProducer(1/UNKNOWN).run() started.",
|
|
20
|
+
"PRODUCER fetch_task @ 1 RETURN",
|
|
21
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=1))",
|
|
22
|
+
"INFO:csu.worker.engine:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=1).done of NoOpTask(id=1).",
|
|
23
|
+
"PRODUCER fetch_task @ 2 RETURN",
|
|
24
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/1q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=2))",
|
|
25
|
+
"INFO:csu.worker.engine:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=2).done of NoOpTask(id=2).",
|
|
26
|
+
"PRODUCER fetch_task @ 3 RETURN",
|
|
27
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/2q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=3))",
|
|
28
|
+
"INFO:csu.worker.engine:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=3).done of NoOpTask(id=3).",
|
|
29
|
+
"INFO:csu.worker.engine:ExampleConsumer(1/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=1)",
|
|
30
|
+
"INFO:csu.worker.engine:ExampleConsumer(1/WORKING).run() - performing...",
|
|
31
|
+
"CONSUMER 0 perform_work JOB 1 START",
|
|
32
|
+
"INFO:csu.worker.engine:ExampleConsumer(2/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=2)",
|
|
33
|
+
"INFO:csu.worker.engine:ExampleConsumer(2/WORKING).run() - performing...",
|
|
34
|
+
"CONSUMER 1 perform_work JOB 2 START",
|
|
35
|
+
"INFO:csu.worker.engine:ExampleConsumer(3/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=3)",
|
|
36
|
+
"INFO:csu.worker.engine:ExampleConsumer(3/WORKING).run() - performing...",
|
|
37
|
+
"CONSUMER 2 perform_work JOB 3 START",
|
|
38
|
+
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
39
|
+
"CONSUMER 0 save_result JOB 1",
|
|
40
|
+
"CONSUMER 1 perform_work JOB 2 DONE",
|
|
41
|
+
"CONSUMER 1 save_result JOB 2",
|
|
42
|
+
"CONSUMER 2 perform_work JOB 3 DONE",
|
|
43
|
+
"CONSUMER 2 save_result JOB 3",
|
|
44
|
+
"PRODUCER fetch_task @ 4 RETURN",
|
|
45
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=4))",
|
|
46
|
+
"INFO:csu.worker.engine:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=4).done of NoOpTask(id=4).",
|
|
47
|
+
"PRODUCER fetch_task @ 5 EXHAUSTED",
|
|
48
|
+
"INFO:csu.worker.engine:ExampleConsumer(1/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=4)",
|
|
49
|
+
"INFO:csu.worker.engine:ExampleConsumer(1/WORKING).run() - performing...",
|
|
50
|
+
"CONSUMER 0 perform_work JOB 4 START",
|
|
51
|
+
"PRODUCER fetch_task @ 6 EXHAUSTED",
|
|
52
|
+
"CONSUMER 0 perform_work JOB 4 DONE",
|
|
53
|
+
"CONSUMER 0 save_result JOB 4",
|
|
54
|
+
"PRODUCER fetch_task @ 7 EXHAUSTED",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def test_skip():
|
|
59
|
+
with TestProcess(sys.executable, "-mexampleworker", "3", "2", "100", "4") as target, dump_on_error(target.read):
|
|
60
|
+
wait_for_strings(
|
|
61
|
+
target.read,
|
|
62
|
+
5,
|
|
63
|
+
"INFO:csu.worker.engine:ExampleConsumer(1/UNKNOWN) started.",
|
|
64
|
+
"INFO:csu.worker.engine:ExampleConsumer(2/UNKNOWN) started.",
|
|
65
|
+
"INFO:csu.worker.engine:ExampleConsumer(3/UNKNOWN) started.",
|
|
66
|
+
"INFO:csu.worker.engine:ExampleProducer(1/UNKNOWN) started.",
|
|
67
|
+
"INFO:csu.worker.engine:ExampleProducer(1/UNKNOWN).run() started.",
|
|
68
|
+
"PRODUCER fetch_task @ 1 RETURN",
|
|
69
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=1))",
|
|
70
|
+
"INFO:csu.worker.engine:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=1).done of NoOpTask(id=1).",
|
|
71
|
+
"PRODUCER fetch_task @ 2 SKIP",
|
|
72
|
+
"INFO:csu.worker.engine:ExampleConsumer(1/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=1)",
|
|
73
|
+
"INFO:csu.worker.engine:ExampleConsumer(1/WORKING).run() - performing...",
|
|
74
|
+
"CONSUMER 0 perform_work JOB 1 START",
|
|
75
|
+
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
76
|
+
"CONSUMER 0 save_result JOB 1",
|
|
77
|
+
"PRODUCER fetch_task @ 3 RETURN",
|
|
78
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=3))",
|
|
79
|
+
"INFO:csu.worker.engine:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=3).done of NoOpTask(id=3).",
|
|
80
|
+
"PRODUCER fetch_task @ 4 SKIP",
|
|
81
|
+
"INFO:csu.worker.engine:ExampleConsumer(2/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=3)",
|
|
82
|
+
"INFO:csu.worker.engine:ExampleConsumer(2/WORKING).run() - performing...",
|
|
83
|
+
"CONSUMER 1 perform_work JOB 3 START",
|
|
84
|
+
"CONSUMER 1 perform_work JOB 3 DONE",
|
|
85
|
+
"CONSUMER 1 save_result JOB 3",
|
|
86
|
+
"PRODUCER fetch_task @ 5 EXHAUSTED",
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def test_graceful_shutdown():
|
|
91
|
+
with TestProcess(sys.executable, "-mexampleworker", "3", "0", "1000", "100") as target, dump_on_error(target.read):
|
|
92
|
+
wait_for_strings(
|
|
93
|
+
target.read,
|
|
94
|
+
15,
|
|
95
|
+
"PRODUCER fetch_task @ 1 RETURN",
|
|
96
|
+
)
|
|
97
|
+
target.proc.send_signal(signal.SIGINT)
|
|
98
|
+
wait_for_strings(
|
|
99
|
+
target.read,
|
|
100
|
+
15,
|
|
101
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q).shutdown_handler(SIGINT) GRACEFUL",
|
|
102
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q).stop(graceful=True)",
|
|
103
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q) stats: {'ExampleConsumer': {'WORKING': 3}, 'ExampleProducer': {'WAITING': 1}, 'ExampleEngine': {'JOBS': 3, 'QSIZE': 0, 'SEMVAL': 0}}",
|
|
104
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q) worker 1: <__main__.ExampleConsumer object at ",
|
|
105
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q) worker 2: <__main__.ExampleConsumer object at ",
|
|
106
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q) worker 3: <__main__.ExampleConsumer object at ",
|
|
107
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q) worker 4: <__main__.ExampleProducer object at ",
|
|
108
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q) requesting workers to stop and awaiting 1 producers...",
|
|
109
|
+
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
110
|
+
"CONSUMER 0 save_result JOB 1",
|
|
111
|
+
"INFO:csu.worker.engine:ExampleProducer(1/WAITING).on_exit(",
|
|
112
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q) flushing queue (0 items) and awaiting consumers...",
|
|
113
|
+
"INFO:csu.worker.engine:ExampleConsumer(1/SHUTDOWN).on_exit(",
|
|
114
|
+
"INFO:csu.worker.engine:ExampleConsumer(2/SHUTDOWN).on_exit(",
|
|
115
|
+
"INFO:csu.worker.engine:ExampleConsumer(3/SHUTDOWN).on_exit(",
|
|
116
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q) shutdown complete.",
|
|
117
|
+
"DONE: {'ExampleConsumer': {'EXITED': 3}, 'ExampleProducer': {'EXITED': 1}, 'ExampleEngine': {'JOBS': 3, 'QSIZE': 0, 'SEMVAL': 2}}",
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def test_forced_shutdown():
|
|
122
|
+
with TestProcess(sys.executable, "-mexampleworker", "3", "0", "2000", "100") as target, dump_on_error(target.read):
|
|
123
|
+
wait_for_strings(
|
|
124
|
+
target.read,
|
|
125
|
+
15,
|
|
126
|
+
"PRODUCER fetch_task @ 1 RETURN",
|
|
127
|
+
)
|
|
128
|
+
target.proc.send_signal(signal.SIGINT)
|
|
129
|
+
time.sleep(1)
|
|
130
|
+
target.proc.send_signal(signal.SIGTERM)
|
|
131
|
+
wait_for_strings(
|
|
132
|
+
target.read,
|
|
133
|
+
15,
|
|
134
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q).shutdown_handler(SIGINT) GRACEFUL",
|
|
135
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q).stop(graceful=True)",
|
|
136
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q) requesting workers to stop and awaiting 1 producers...",
|
|
137
|
+
"INFO:csu.worker.engine:ExampleEngine(4w/0q).shutdown_handler(SIGTERM) FORCED",
|
|
138
|
+
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
139
|
+
"CONSUMER 1 perform_work JOB 2 DONE",
|
|
140
|
+
"CONSUMER 2 perform_work JOB 3 DONE",
|
|
141
|
+
"DONE: {'ExampleConsumer': {'CANCELED': 3}, 'ExampleProducer': {'CANCELED': 1}, 'ExampleEngine': {'JOBS': 3, 'QSIZE': 0, 'SEMVAL': 0}}",
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_sem_rollback():
|
|
146
|
+
with TestProcess(sys.executable, "-mexampleworker", "1", "2", "10", "10") as target, dump_on_error(target.read):
|
|
147
|
+
wait_for_strings(
|
|
148
|
+
target.read,
|
|
149
|
+
15,
|
|
150
|
+
"PRODUCER fetch_task @ 1 RETURN",
|
|
151
|
+
"CONSUMER 0 perform_work JOB 1 START",
|
|
152
|
+
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
153
|
+
"CONSUMER 0 save_result JOB 1",
|
|
154
|
+
"PRODUCER fetch_task @ 2 SKIP",
|
|
155
|
+
"PRODUCER fetch_task @ 3 RETURN",
|
|
156
|
+
"CONSUMER 0 perform_work JOB 3 START",
|
|
157
|
+
"CONSUMER 0 perform_work JOB 3 DONE",
|
|
158
|
+
"CONSUMER 0 save_result JOB 3",
|
|
159
|
+
"PRODUCER fetch_task @ 4 SKIP",
|
|
160
|
+
"PRODUCER fetch_task @ 5 RETURN",
|
|
161
|
+
"CONSUMER 0 perform_work JOB 5 START",
|
|
162
|
+
"CONSUMER 0 perform_work JOB 5 DONE",
|
|
163
|
+
"CONSUMER 0 save_result JOB 5",
|
|
164
|
+
)
|
|
@@ -59,4 +59,4 @@ async def test_cancel():
|
|
|
59
59
|
with pytest.raises(TimeoutError):
|
|
60
60
|
await asyncio.wait_for(job1.done, 0.1)
|
|
61
61
|
result = await asyncio.wait_for(job2.done, 1)
|
|
62
|
-
assert result == {"result_type": ResultType.SUCCESS, "
|
|
62
|
+
assert result == {"result_type": ResultType.SUCCESS, "time": 0}
|
csu-2.2.4/src/csu/worker/asgi.py
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
from asgiref.typing import ASGIReceiveCallable
|
|
2
|
-
from asgiref.typing import ASGISendCallable
|
|
3
|
-
from asgiref.typing import Scope
|
|
4
|
-
from django.core.asgi import get_asgi_application
|
|
5
|
-
|
|
6
|
-
from . import logger
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def get_worker_lifespan_application():
|
|
10
|
-
django_application = get_asgi_application()
|
|
11
|
-
|
|
12
|
-
async def application_with_worker_lifespan(scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None:
|
|
13
|
-
if scope["type"] == "lifespan":
|
|
14
|
-
try:
|
|
15
|
-
from .registry import REGISTRY # noqa:PLC0415
|
|
16
|
-
|
|
17
|
-
while True:
|
|
18
|
-
message = await receive()
|
|
19
|
-
message_type = message["type"]
|
|
20
|
-
if message_type == "lifespan.startup":
|
|
21
|
-
logger.info("Found %s registered engines.", len(REGISTRY))
|
|
22
|
-
for module_name, engine in REGISTRY.items():
|
|
23
|
-
logger.info("Starting engine for %s: %s...", module_name, engine)
|
|
24
|
-
await engine.start()
|
|
25
|
-
await send({"type": "lifespan.startup.complete"})
|
|
26
|
-
if message_type == "lifespan.shutdown":
|
|
27
|
-
for engine in REGISTRY.values():
|
|
28
|
-
await engine.stop()
|
|
29
|
-
await send({"type": "lifespan.shutdown.complete"})
|
|
30
|
-
return
|
|
31
|
-
except Exception as exc:
|
|
32
|
-
logger.exception("Failed setting up lifespan: %r", exc)
|
|
33
|
-
else:
|
|
34
|
-
await django_application(scope, receive, send)
|
|
35
|
-
|
|
36
|
-
return application_with_worker_lifespan
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
import signal
|
|
2
|
-
import sys
|
|
3
|
-
import time
|
|
4
|
-
|
|
5
|
-
from process_tests import TestProcess
|
|
6
|
-
from process_tests import dump_on_error
|
|
7
|
-
from process_tests import wait_for_strings
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
def test_noskip():
|
|
11
|
-
with TestProcess(sys.executable, "-mexampleworker", "3", "0", "1000", "4") as target, dump_on_error(target.read):
|
|
12
|
-
wait_for_strings(
|
|
13
|
-
target.read,
|
|
14
|
-
5,
|
|
15
|
-
"INFO:worker:ExampleConsumer(1/UNKNOWN) started.",
|
|
16
|
-
"INFO:worker:ExampleConsumer(2/UNKNOWN) started.",
|
|
17
|
-
"INFO:worker:ExampleConsumer(3/UNKNOWN) started.",
|
|
18
|
-
"INFO:worker:ExampleProducer(1/UNKNOWN) started.",
|
|
19
|
-
"INFO:worker:ExampleProducer(1/UNKNOWN).run() started.",
|
|
20
|
-
"PRODUCER fetch_task @ 1 RETURN",
|
|
21
|
-
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=1))",
|
|
22
|
-
"INFO:worker:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=1).done of NoOpTask(id=1).",
|
|
23
|
-
"PRODUCER fetch_task @ 2 RETURN",
|
|
24
|
-
"INFO:worker:ExampleEngine(4w/1q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=2))",
|
|
25
|
-
"INFO:worker:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=2).done of NoOpTask(id=2).",
|
|
26
|
-
"PRODUCER fetch_task @ 3 RETURN",
|
|
27
|
-
"INFO:worker:ExampleEngine(4w/2q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=3))",
|
|
28
|
-
"INFO:worker:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=3).done of NoOpTask(id=3).",
|
|
29
|
-
"INFO:worker:ExampleConsumer(1/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=1)",
|
|
30
|
-
"INFO:worker:ExampleConsumer(1/WORKING).run() - performing...",
|
|
31
|
-
"CONSUMER 0 perform_work JOB 1 START",
|
|
32
|
-
"INFO:worker:ExampleConsumer(2/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=2)",
|
|
33
|
-
"INFO:worker:ExampleConsumer(2/WORKING).run() - performing...",
|
|
34
|
-
"CONSUMER 1 perform_work JOB 2 START",
|
|
35
|
-
"INFO:worker:ExampleConsumer(3/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=3)",
|
|
36
|
-
"INFO:worker:ExampleConsumer(3/WORKING).run() - performing...",
|
|
37
|
-
"CONSUMER 2 perform_work JOB 3 START",
|
|
38
|
-
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
39
|
-
"CONSUMER 0 save_result JOB 1",
|
|
40
|
-
"CONSUMER 1 perform_work JOB 2 DONE",
|
|
41
|
-
"CONSUMER 1 save_result JOB 2",
|
|
42
|
-
"CONSUMER 2 perform_work JOB 3 DONE",
|
|
43
|
-
"CONSUMER 2 save_result JOB 3",
|
|
44
|
-
"PRODUCER fetch_task @ 4 RETURN",
|
|
45
|
-
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=4))",
|
|
46
|
-
"INFO:worker:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=4).done of NoOpTask(id=4).",
|
|
47
|
-
"PRODUCER fetch_task @ 5 EXHAUSTED",
|
|
48
|
-
"INFO:worker:ExampleConsumer(1/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=4)",
|
|
49
|
-
"INFO:worker:ExampleConsumer(1/WORKING).run() - performing...",
|
|
50
|
-
"CONSUMER 0 perform_work JOB 4 START",
|
|
51
|
-
"PRODUCER fetch_task @ 6 EXHAUSTED",
|
|
52
|
-
"CONSUMER 0 perform_work JOB 4 DONE",
|
|
53
|
-
"CONSUMER 0 save_result JOB 4",
|
|
54
|
-
"PRODUCER fetch_task @ 7 EXHAUSTED",
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
def test_skip():
|
|
59
|
-
with TestProcess(sys.executable, "-mexampleworker", "3", "2", "100", "4") as target, dump_on_error(target.read):
|
|
60
|
-
wait_for_strings(
|
|
61
|
-
target.read,
|
|
62
|
-
5,
|
|
63
|
-
"INFO:worker:ExampleConsumer(1/UNKNOWN) started.",
|
|
64
|
-
"INFO:worker:ExampleConsumer(2/UNKNOWN) started.",
|
|
65
|
-
"INFO:worker:ExampleConsumer(3/UNKNOWN) started.",
|
|
66
|
-
"INFO:worker:ExampleProducer(1/UNKNOWN) started.",
|
|
67
|
-
"INFO:worker:ExampleProducer(1/UNKNOWN).run() started.",
|
|
68
|
-
"PRODUCER fetch_task @ 1 RETURN",
|
|
69
|
-
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=1))",
|
|
70
|
-
"INFO:worker:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=1).done of NoOpTask(id=1).",
|
|
71
|
-
"PRODUCER fetch_task @ 2 SKIP",
|
|
72
|
-
"INFO:worker:ExampleConsumer(1/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=1)",
|
|
73
|
-
"INFO:worker:ExampleConsumer(1/WORKING).run() - performing...",
|
|
74
|
-
"CONSUMER 0 perform_work JOB 1 START",
|
|
75
|
-
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
76
|
-
"CONSUMER 0 save_result JOB 1",
|
|
77
|
-
"PRODUCER fetch_task @ 3 RETURN",
|
|
78
|
-
"INFO:worker:ExampleEngine(4w/0q).push(ExampleJob(done=<Future pending>, started=<Future pending>, id=3))",
|
|
79
|
-
"INFO:worker:ExampleProducer(1/WORKING).run() awaiting ExampleJob(done=<Future pending>, started=<Future pending>, id=3).done of NoOpTask(id=3).",
|
|
80
|
-
"PRODUCER fetch_task @ 4 SKIP",
|
|
81
|
-
"INFO:worker:ExampleConsumer(2/READY).run() working on: ExampleJob(done=<Future pending>, started=<Future finished result=True>, id=3)",
|
|
82
|
-
"INFO:worker:ExampleConsumer(2/WORKING).run() - performing...",
|
|
83
|
-
"CONSUMER 1 perform_work JOB 3 START",
|
|
84
|
-
"CONSUMER 1 perform_work JOB 3 DONE",
|
|
85
|
-
"CONSUMER 1 save_result JOB 3",
|
|
86
|
-
"PRODUCER fetch_task @ 5 EXHAUSTED",
|
|
87
|
-
)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def test_graceful_shutdown():
|
|
91
|
-
with TestProcess(sys.executable, "-mexampleworker", "3", "0", "1000", "100") as target, dump_on_error(target.read):
|
|
92
|
-
wait_for_strings(
|
|
93
|
-
target.read,
|
|
94
|
-
15,
|
|
95
|
-
"PRODUCER fetch_task @ 1 RETURN",
|
|
96
|
-
)
|
|
97
|
-
target.proc.send_signal(signal.SIGINT)
|
|
98
|
-
wait_for_strings(
|
|
99
|
-
target.read,
|
|
100
|
-
15,
|
|
101
|
-
"INFO:worker:ExampleEngine(4w/0q).shutdown_handler(SIGINT) GRACEFUL",
|
|
102
|
-
"INFO:worker:ExampleEngine(4w/0q).stop(graceful=True)",
|
|
103
|
-
"INFO:worker:ExampleEngine(4w/0q) stats: {'ExampleConsumer': {'WORKING': 3}, 'ExampleProducer': {'WAITING': 1}, 'ExampleEngine': {'JOBS': 3, 'QSIZE': 0, 'SEMVAL': 0}}",
|
|
104
|
-
"INFO:worker:ExampleEngine(4w/0q) worker 1: <__main__.ExampleConsumer object at ",
|
|
105
|
-
"INFO:worker:ExampleEngine(4w/0q) worker 2: <__main__.ExampleConsumer object at ",
|
|
106
|
-
"INFO:worker:ExampleEngine(4w/0q) worker 3: <__main__.ExampleConsumer object at ",
|
|
107
|
-
"INFO:worker:ExampleEngine(4w/0q) worker 4: <__main__.ExampleProducer object at ",
|
|
108
|
-
"INFO:worker:ExampleEngine(4w/0q) requesting workers to stop and awaiting 1 producers...",
|
|
109
|
-
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
110
|
-
"CONSUMER 0 save_result JOB 1",
|
|
111
|
-
"INFO:worker:ExampleProducer(1/WAITING).on_exit(",
|
|
112
|
-
"INFO:worker:ExampleEngine(4w/0q) flushing queue (0 items) and awaiting consumers...",
|
|
113
|
-
"INFO:worker:ExampleConsumer(1/SHUTDOWN).on_exit(",
|
|
114
|
-
"INFO:worker:ExampleConsumer(2/SHUTDOWN).on_exit(",
|
|
115
|
-
"INFO:worker:ExampleConsumer(3/SHUTDOWN).on_exit(",
|
|
116
|
-
"INFO:worker:ExampleEngine(4w/0q) shutdown complete.",
|
|
117
|
-
"DONE: {'ExampleConsumer': {'EXITED': 3}, 'ExampleProducer': {'EXITED': 1}, 'ExampleEngine': {'JOBS': 3, 'QSIZE': 0, 'SEMVAL': 2}}",
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def test_forced_shutdown():
|
|
122
|
-
with TestProcess(sys.executable, "-mexampleworker", "3", "0", "2000", "100") as target, dump_on_error(target.read):
|
|
123
|
-
wait_for_strings(
|
|
124
|
-
target.read,
|
|
125
|
-
15,
|
|
126
|
-
"PRODUCER fetch_task @ 1 RETURN",
|
|
127
|
-
)
|
|
128
|
-
target.proc.send_signal(signal.SIGINT)
|
|
129
|
-
time.sleep(1)
|
|
130
|
-
target.proc.send_signal(signal.SIGTERM)
|
|
131
|
-
wait_for_strings(
|
|
132
|
-
target.read,
|
|
133
|
-
15,
|
|
134
|
-
"INFO:worker:ExampleEngine(4w/0q).shutdown_handler(SIGINT) GRACEFUL",
|
|
135
|
-
"INFO:worker:ExampleEngine(4w/0q).stop(graceful=True)",
|
|
136
|
-
"INFO:worker:ExampleEngine(4w/0q) requesting workers to stop and awaiting 1 producers...",
|
|
137
|
-
"INFO:worker:ExampleEngine(4w/0q).shutdown_handler(SIGTERM) FORCED",
|
|
138
|
-
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
139
|
-
"CONSUMER 1 perform_work JOB 2 DONE",
|
|
140
|
-
"CONSUMER 2 perform_work JOB 3 DONE",
|
|
141
|
-
"DONE: {'ExampleConsumer': {'CANCELED': 3}, 'ExampleProducer': {'CANCELED': 1}, 'ExampleEngine': {'JOBS': 3, 'QSIZE': 0, 'SEMVAL': 0}}",
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
def test_sem_rollback():
|
|
146
|
-
with TestProcess(sys.executable, "-mexampleworker", "1", "2", "10", "10") as target, dump_on_error(target.read):
|
|
147
|
-
wait_for_strings(
|
|
148
|
-
target.read,
|
|
149
|
-
15,
|
|
150
|
-
"PRODUCER fetch_task @ 1 RETURN",
|
|
151
|
-
"CONSUMER 0 perform_work JOB 1 START",
|
|
152
|
-
"CONSUMER 0 perform_work JOB 1 DONE",
|
|
153
|
-
"CONSUMER 0 save_result JOB 1",
|
|
154
|
-
"PRODUCER fetch_task @ 2 SKIP",
|
|
155
|
-
"PRODUCER fetch_task @ 3 RETURN",
|
|
156
|
-
"CONSUMER 0 perform_work JOB 3 START",
|
|
157
|
-
"CONSUMER 0 perform_work JOB 3 DONE",
|
|
158
|
-
"CONSUMER 0 save_result JOB 3",
|
|
159
|
-
"PRODUCER fetch_task @ 4 SKIP",
|
|
160
|
-
"PRODUCER fetch_task @ 5 RETURN",
|
|
161
|
-
"CONSUMER 0 perform_work JOB 5 START",
|
|
162
|
-
"CONSUMER 0 perform_work JOB 5 DONE",
|
|
163
|
-
"CONSUMER 0 save_result JOB 5",
|
|
164
|
-
)
|
|
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.2.4 → csu-2.3.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
|
{csu-2.2.4 → csu-2.3.0}/tox.ini
RENAMED
|
File without changes
|