quapp-common 0.0.13.dev7__tar.gz → 0.0.13.dev8__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.
- {quapp_common-0.0.13.dev7/quapp_common.egg-info → quapp_common-0.0.13.dev8}/PKG-INFO +1 -1
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/pyproject.toml +1 -1
- quapp_common-0.0.13.dev8/quapp_common/component/backend/job_heartbeat.py +83 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/backend/job_manager.py +6 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8/quapp_common.egg-info}/PKG-INFO +1 -1
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common.egg-info/SOURCES.txt +2 -0
- quapp_common-0.0.13.dev8/tests/test_job_heartbeat.py +115 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/LICENSE +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/README.md +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/async_tasks/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/async_tasks/async_invocation_task.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/async_tasks/async_task.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/async_tasks/export_circuit_task.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/async_tasks/post_processing_task.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/backend/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/backend/invocation.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/backend/job_fetcher.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/backend/job_fetching.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/bridge.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/callback/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/callback/update_job_metadata.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/circuit_adapter.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/device/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/device/device_selection.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/dispatcher.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/result_serializer.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/config/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/config/logging_config.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/config/thread_config.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/async_task/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/async_task/circuit_export/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/async_task/circuit_export/backend_holder.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/async_task/circuit_export/circuit_holder.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/backend/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/backend/backend_information.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/callback/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/callback/callback_url.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/device/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/device/circuit_running_option.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/promise/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/promise/post_processing_promise.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/promise/promise.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/request/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/request/invocation_request.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/request/job_fetching_request.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/request/request.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/response/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/response/authentication.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/response/custom_header.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/response/job_response.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/base_enum.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/http_header.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/invocation_step.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/language.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/media_type.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/processing_unit.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/provider_tag.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/sdk.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/status/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/status/job_status.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/status/status_code.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/token_type.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/factory/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/factory/device_factory.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/factory/handler_factory.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/factory/provider_factory.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/handler/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/handler/handler.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/model/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/model/device/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/model/device/custom_device.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/model/device/device.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/model/invocation.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/model/provider/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/model/provider/provider.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/util/__init__.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/util/file_utils.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/util/http_utils.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/util/json_parser_utils.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/util/response_utils.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common.egg-info/dependency_links.txt +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common.egg-info/requires.txt +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common.egg-info/top_level.txt +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/setup.cfg +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/tests/test_circuit_adapter.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/tests/test_json_parser_utils.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/tests/test_language_dispatcher.py +0 -0
- {quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/tests/test_result_serializer.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quapp-common"
|
|
7
|
-
version = "0.0.13.
|
|
7
|
+
version = "0.0.13.dev8"
|
|
8
8
|
description = "Quapp common library supporting Quapp Platform for Quantum Computing"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
authors = [{ name = "CITYNOW Co. Ltd. ", email = "corp@citynow.vn" }]
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# job_heartbeat.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import threading
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
from ...config.logging_config import logger
|
|
11
|
+
|
|
12
|
+
DEFAULT_INTERVAL_SEC = 30.0
|
|
13
|
+
REQUEST_TIMEOUT_SEC = 5.0
|
|
14
|
+
TERMINAL_STATUSES = ('DONE', 'FAILED')
|
|
15
|
+
|
|
16
|
+
_lock = threading.Lock()
|
|
17
|
+
_thread = None
|
|
18
|
+
_stop_event = threading.Event()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def heartbeat_interval_sec() -> float:
|
|
22
|
+
raw = os.getenv('HEARTBEAT_INTERVAL_SEC', '')
|
|
23
|
+
try:
|
|
24
|
+
value = float(raw)
|
|
25
|
+
if value > 0:
|
|
26
|
+
return value
|
|
27
|
+
except ValueError:
|
|
28
|
+
pass
|
|
29
|
+
return DEFAULT_INTERVAL_SEC
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def beat_once() -> int:
|
|
33
|
+
from .job_manager import JobManager, META, STATUS, SCHEDULER_CALLBACK_URL
|
|
34
|
+
|
|
35
|
+
sent = 0
|
|
36
|
+
for job_id, data in JobManager.list_jobs().items():
|
|
37
|
+
if data.get(STATUS) in TERMINAL_STATUSES:
|
|
38
|
+
continue
|
|
39
|
+
url = (data.get(META) or {}).get(SCHEDULER_CALLBACK_URL)
|
|
40
|
+
if not url:
|
|
41
|
+
continue
|
|
42
|
+
try:
|
|
43
|
+
response = requests.patch(
|
|
44
|
+
url, json={'status': 'RUNNING', 'heartbeat': True},
|
|
45
|
+
timeout=REQUEST_TIMEOUT_SEC)
|
|
46
|
+
logger.debug(
|
|
47
|
+
f'Heartbeat for job {job_id} returned {response.status_code}')
|
|
48
|
+
sent += 1
|
|
49
|
+
except Exception as exception:
|
|
50
|
+
logger.debug(f'Heartbeat for job {job_id} failed: {exception}')
|
|
51
|
+
return sent
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _run() -> None:
|
|
55
|
+
while True:
|
|
56
|
+
try:
|
|
57
|
+
beat_once()
|
|
58
|
+
except Exception as exception:
|
|
59
|
+
logger.exception(f'Heartbeat loop error: {exception}')
|
|
60
|
+
if _stop_event.wait(heartbeat_interval_sec()):
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def ensure_started() -> None:
|
|
65
|
+
global _thread
|
|
66
|
+
with _lock:
|
|
67
|
+
if _thread is not None and _thread.is_alive():
|
|
68
|
+
return
|
|
69
|
+
_stop_event.clear()
|
|
70
|
+
_thread = threading.Thread(target=_run, name='job-heartbeat',
|
|
71
|
+
daemon=True)
|
|
72
|
+
_thread.start()
|
|
73
|
+
logger.info(
|
|
74
|
+
f'Job heartbeat started with interval {heartbeat_interval_sec()}s')
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def stop() -> None:
|
|
78
|
+
global _thread
|
|
79
|
+
with _lock:
|
|
80
|
+
_stop_event.set()
|
|
81
|
+
if _thread is not None:
|
|
82
|
+
_thread.join(timeout=1.0)
|
|
83
|
+
_thread = None
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/backend/job_manager.py
RENAMED
|
@@ -66,6 +66,9 @@ class JobManager:
|
|
|
66
66
|
"""
|
|
67
67
|
Stop any listener threads in this process and shutdown Manager.
|
|
68
68
|
"""
|
|
69
|
+
from .job_heartbeat import stop
|
|
70
|
+
stop()
|
|
71
|
+
|
|
69
72
|
# stop listeners
|
|
70
73
|
for key, (t, stop_ev) in list(cls._listener_threads.items()):
|
|
71
74
|
stop_ev.set()
|
|
@@ -100,6 +103,9 @@ class JobManager:
|
|
|
100
103
|
cls._jobs[job_id] = data # proxy is process-safe
|
|
101
104
|
cls._publish(job_id, {"event": "added", **data})
|
|
102
105
|
|
|
106
|
+
from .job_heartbeat import ensure_started
|
|
107
|
+
ensure_started()
|
|
108
|
+
|
|
103
109
|
@classmethod
|
|
104
110
|
def update_status(cls, job_id: str, status: str,
|
|
105
111
|
meta_update: Optional[Dict[str, Any]] = None) -> None:
|
|
@@ -21,6 +21,7 @@ quapp_common/component/backend/__init__.py
|
|
|
21
21
|
quapp_common/component/backend/invocation.py
|
|
22
22
|
quapp_common/component/backend/job_fetcher.py
|
|
23
23
|
quapp_common/component/backend/job_fetching.py
|
|
24
|
+
quapp_common/component/backend/job_heartbeat.py
|
|
24
25
|
quapp_common/component/backend/job_manager.py
|
|
25
26
|
quapp_common/component/callback/__init__.py
|
|
26
27
|
quapp_common/component/callback/update_job_metadata.py
|
|
@@ -83,6 +84,7 @@ quapp_common/util/http_utils.py
|
|
|
83
84
|
quapp_common/util/json_parser_utils.py
|
|
84
85
|
quapp_common/util/response_utils.py
|
|
85
86
|
tests/test_circuit_adapter.py
|
|
87
|
+
tests/test_job_heartbeat.py
|
|
86
88
|
tests/test_json_parser_utils.py
|
|
87
89
|
tests/test_language_dispatcher.py
|
|
88
90
|
tests/test_result_serializer.py
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import patch, MagicMock
|
|
3
|
+
|
|
4
|
+
from quapp_common.component.backend import job_heartbeat
|
|
5
|
+
from quapp_common.component.backend.job_manager import JobManager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestHeartbeatInterval(unittest.TestCase):
|
|
9
|
+
def test_default_interval(self):
|
|
10
|
+
with patch.dict('os.environ', {}, clear=False):
|
|
11
|
+
import os
|
|
12
|
+
os.environ.pop('HEARTBEAT_INTERVAL_SEC', None)
|
|
13
|
+
self.assertEqual(job_heartbeat.heartbeat_interval_sec(),
|
|
14
|
+
job_heartbeat.DEFAULT_INTERVAL_SEC)
|
|
15
|
+
|
|
16
|
+
def test_env_override(self):
|
|
17
|
+
with patch.dict('os.environ', {'HEARTBEAT_INTERVAL_SEC': '10'}):
|
|
18
|
+
self.assertEqual(job_heartbeat.heartbeat_interval_sec(), 10.0)
|
|
19
|
+
|
|
20
|
+
def test_invalid_env_falls_back(self):
|
|
21
|
+
with patch.dict('os.environ', {'HEARTBEAT_INTERVAL_SEC': 'abc'}):
|
|
22
|
+
self.assertEqual(job_heartbeat.heartbeat_interval_sec(),
|
|
23
|
+
job_heartbeat.DEFAULT_INTERVAL_SEC)
|
|
24
|
+
|
|
25
|
+
def test_non_positive_env_falls_back(self):
|
|
26
|
+
with patch.dict('os.environ', {'HEARTBEAT_INTERVAL_SEC': '0'}):
|
|
27
|
+
self.assertEqual(job_heartbeat.heartbeat_interval_sec(),
|
|
28
|
+
job_heartbeat.DEFAULT_INTERVAL_SEC)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TestBeatOnce(unittest.TestCase):
|
|
32
|
+
def setUp(self):
|
|
33
|
+
for job_id in list(JobManager.list_jobs()):
|
|
34
|
+
JobManager.remove_job(job_id)
|
|
35
|
+
|
|
36
|
+
def tearDown(self):
|
|
37
|
+
for job_id in list(JobManager.list_jobs()):
|
|
38
|
+
JobManager.remove_job(job_id)
|
|
39
|
+
job_heartbeat.stop()
|
|
40
|
+
|
|
41
|
+
def test_beats_active_job_with_url(self):
|
|
42
|
+
JobManager.add_job('job-active', status='RUNNING', meta={
|
|
43
|
+
'scheduler_callback_url': 'http://scheduler/v1/callbacks/job-active?gen=2'})
|
|
44
|
+
with patch.object(job_heartbeat.requests, 'patch',
|
|
45
|
+
return_value=MagicMock(status_code=200)) as mock_patch:
|
|
46
|
+
sent = job_heartbeat.beat_once()
|
|
47
|
+
self.assertEqual(sent, 1)
|
|
48
|
+
mock_patch.assert_called_once_with(
|
|
49
|
+
'http://scheduler/v1/callbacks/job-active?gen=2',
|
|
50
|
+
json={'status': 'RUNNING', 'heartbeat': True},
|
|
51
|
+
timeout=job_heartbeat.REQUEST_TIMEOUT_SEC)
|
|
52
|
+
|
|
53
|
+
def test_skips_terminal_jobs(self):
|
|
54
|
+
JobManager.add_job('job-done', status='DONE', meta={
|
|
55
|
+
'scheduler_callback_url': 'http://scheduler/v1/callbacks/job-done'})
|
|
56
|
+
JobManager.add_job('job-failed', status='FAILED', meta={
|
|
57
|
+
'scheduler_callback_url': 'http://scheduler/v1/callbacks/job-failed'})
|
|
58
|
+
with patch.object(job_heartbeat.requests, 'patch') as mock_patch:
|
|
59
|
+
sent = job_heartbeat.beat_once()
|
|
60
|
+
self.assertEqual(sent, 0)
|
|
61
|
+
mock_patch.assert_not_called()
|
|
62
|
+
|
|
63
|
+
def test_skips_job_without_url(self):
|
|
64
|
+
JobManager.add_job('job-no-url', status='RUNNING', meta={})
|
|
65
|
+
with patch.object(job_heartbeat.requests, 'patch') as mock_patch:
|
|
66
|
+
sent = job_heartbeat.beat_once()
|
|
67
|
+
self.assertEqual(sent, 0)
|
|
68
|
+
mock_patch.assert_not_called()
|
|
69
|
+
|
|
70
|
+
def test_network_error_does_not_raise(self):
|
|
71
|
+
JobManager.add_job('job-net', status='RUNNING', meta={
|
|
72
|
+
'scheduler_callback_url': 'http://scheduler/v1/callbacks/job-net'})
|
|
73
|
+
with patch.object(job_heartbeat.requests, 'patch',
|
|
74
|
+
side_effect=ConnectionError('down')):
|
|
75
|
+
sent = job_heartbeat.beat_once()
|
|
76
|
+
self.assertEqual(sent, 0)
|
|
77
|
+
|
|
78
|
+
def test_pending_job_is_beaten(self):
|
|
79
|
+
JobManager.add_job('job-pending', status='PENDING', meta={
|
|
80
|
+
'scheduler_callback_url': 'http://scheduler/v1/callbacks/job-pending'})
|
|
81
|
+
with patch.object(job_heartbeat.requests, 'patch',
|
|
82
|
+
return_value=MagicMock(status_code=200)) as mock_patch:
|
|
83
|
+
sent = job_heartbeat.beat_once()
|
|
84
|
+
self.assertEqual(sent, 1)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
class TestDaemonLifecycle(unittest.TestCase):
|
|
88
|
+
def tearDown(self):
|
|
89
|
+
job_heartbeat.stop()
|
|
90
|
+
for job_id in list(JobManager.list_jobs()):
|
|
91
|
+
JobManager.remove_job(job_id)
|
|
92
|
+
|
|
93
|
+
def test_ensure_started_is_idempotent(self):
|
|
94
|
+
job_heartbeat.ensure_started()
|
|
95
|
+
first = job_heartbeat._thread
|
|
96
|
+
job_heartbeat.ensure_started()
|
|
97
|
+
self.assertIs(job_heartbeat._thread, first)
|
|
98
|
+
self.assertTrue(first.is_alive())
|
|
99
|
+
|
|
100
|
+
def test_stop_terminates_thread(self):
|
|
101
|
+
job_heartbeat.ensure_started()
|
|
102
|
+
thread = job_heartbeat._thread
|
|
103
|
+
job_heartbeat.stop()
|
|
104
|
+
self.assertFalse(thread.is_alive())
|
|
105
|
+
self.assertIsNone(job_heartbeat._thread)
|
|
106
|
+
|
|
107
|
+
def test_add_job_starts_daemon(self):
|
|
108
|
+
job_heartbeat.stop()
|
|
109
|
+
JobManager.add_job('job-autostart', status='RUNNING', meta={})
|
|
110
|
+
self.assertIsNotNone(job_heartbeat._thread)
|
|
111
|
+
self.assertTrue(job_heartbeat._thread.is_alive())
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
if __name__ == '__main__':
|
|
115
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/async_tasks/async_task.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/backend/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/backend/invocation.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/backend/job_fetcher.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/backend/job_fetching.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/callback/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/circuit_adapter.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/device/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/component/result_serializer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/async_task/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/callback/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/callback/callback_url.py
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
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/response/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/response/authentication.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/response/custom_header.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/data/response/job_response.py
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
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/status/job_status.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/enum/status/status_code.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/factory/device_factory.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/factory/handler_factory.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/factory/provider_factory.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/model/device/custom_device.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/model/provider/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/model/provider/provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common/util/json_parser_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.13.dev7 → quapp_common-0.0.13.dev8}/quapp_common.egg-info/dependency_links.txt
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
|