quapp-common 0.0.12.dev3__tar.gz → 0.0.12.dev4__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.12.dev3/quapp_common.egg-info → quapp_common-0.0.12.dev4}/PKG-INFO +23 -6
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/README.md +22 -5
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/pyproject.toml +1 -1
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/async_tasks/async_invocation_task.py +26 -2
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/backend/invocation.py +1 -1
- quapp_common-0.0.12.dev4/quapp_common/component/bridge.py +94 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/circuit_adapter.py +2 -3
- quapp_common-0.0.12.dev4/quapp_common/component/dispatcher.py +80 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4/quapp_common.egg-info}/PKG-INFO +23 -6
- quapp_common-0.0.12.dev3/quapp_common/component/bridge.py +0 -94
- quapp_common-0.0.12.dev3/quapp_common/component/dispatcher.py +0 -79
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/LICENSE +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/async_tasks/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/async_tasks/async_task.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/async_tasks/export_circuit_task.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/async_tasks/post_processing_task.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/backend/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/backend/job_fetcher.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/backend/job_fetching.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/backend/job_manager.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/callback/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/callback/update_job_metadata.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/device/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/device/device_selection.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/result_serializer.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/config/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/config/logging_config.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/config/thread_config.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/async_task/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/async_task/circuit_export/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/async_task/circuit_export/backend_holder.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/async_task/circuit_export/circuit_holder.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/backend/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/backend/backend_information.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/callback/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/callback/callback_url.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/device/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/device/circuit_running_option.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/promise/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/promise/post_processing_promise.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/promise/promise.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/request/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/request/invocation_request.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/request/job_fetching_request.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/request/request.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/response/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/response/authentication.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/response/custom_header.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/response/job_response.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/base_enum.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/http_header.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/invocation_step.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/language.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/media_type.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/processing_unit.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/provider_tag.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/sdk.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/status/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/status/job_status.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/status/status_code.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/token_type.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/factory/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/factory/device_factory.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/factory/handler_factory.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/factory/provider_factory.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/handler/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/handler/handler.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/model/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/model/device/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/model/device/custom_device.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/model/device/device.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/model/invocation.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/model/provider/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/model/provider/provider.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/util/__init__.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/util/file_utils.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/util/http_utils.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/util/json_parser_utils.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/util/response_utils.py +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common.egg-info/SOURCES.txt +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common.egg-info/dependency_links.txt +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common.egg-info/requires.txt +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common.egg-info/top_level.txt +0 -0
- {quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quapp-common
|
|
3
|
-
Version: 0.0.12.
|
|
3
|
+
Version: 0.0.12.dev4
|
|
4
4
|
Summary: Quapp common library supporting Quapp Platform for Quantum Computing
|
|
5
5
|
Author-email: "CITYNOW Co. Ltd. " <corp@citynow.vn>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -49,11 +49,6 @@ Recent improvements add first-class asynchronous job processing with a
|
|
|
49
49
|
cross-process JobManager,
|
|
50
50
|
background task execution, and standardized result models for immediate client
|
|
51
51
|
responses while work continues in the background.
|
|
52
|
-
Version 0.0.12.dev1 introduces multi-language SDK support, enabling
|
|
53
|
-
dispatching quantum tasks to non-Python runtimes (e.g., JavaScript) via
|
|
54
|
-
subprocess with circuit adaptation and result serialization.
|
|
55
|
-
Version 0.0.12.dev2 fixes the JavaScript handler runner path in
|
|
56
|
-
`SubprocessDispatcher` so that the subprocess command resolves correctly.
|
|
57
52
|
|
|
58
53
|
## Features
|
|
59
54
|
|
|
@@ -106,6 +101,28 @@ Notes:
|
|
|
106
101
|
|
|
107
102
|
## Recently Changes Highlights
|
|
108
103
|
|
|
104
|
+
### v0.0.12.dev4
|
|
105
|
+
- Version bumps to 0.0.12.dev4.
|
|
106
|
+
- Fix: `AsyncInvocationTask._on_task_done` now properly fires the step error
|
|
107
|
+
callback when a background task raises an unhandled exception — reads
|
|
108
|
+
`onErrorCallbackUrl` from the event's `preparation` step and calls
|
|
109
|
+
`update_job_metadata` with a structured error response.
|
|
110
|
+
- Fix: circuit preparation failure in `Invocation.__pre_execute` now returns
|
|
111
|
+
`None` instead of raising `ValueError`, allowing the caller to handle the
|
|
112
|
+
empty result gracefully without an unhandled exception propagating.
|
|
113
|
+
- Refactor: replace stdlib `logging` with the project’s loguru-based logger
|
|
114
|
+
(via `config.logging_config`) in `SubprocessBridge`, `CircuitAdapter`, and
|
|
115
|
+
`SubprocessDispatcher`; all three now call `logger.bind(context=__name__)`.
|
|
116
|
+
- Style: reformat `SubprocessBridge` and `SubprocessDispatcher` from 2-space
|
|
117
|
+
to 4-space indentation, consistent with the rest of the codebase.
|
|
118
|
+
|
|
119
|
+
### v0.0.12.dev3
|
|
120
|
+
- Version bumps to 0.0.12.dev3.
|
|
121
|
+
- Add `QUANTUM_HPC` SDK enum value (display name `"quantum hpc"`) to support
|
|
122
|
+
the Quapp HPC backend.
|
|
123
|
+
- Make `circuitExportUrl` optional in `Invocation` for SDKs that do not
|
|
124
|
+
require a circuit-export step.
|
|
125
|
+
|
|
109
126
|
### v0.0.12.dev2
|
|
110
127
|
- Version bumps to 0.0.12.dev2.
|
|
111
128
|
- Fix: correct JavaScript handler runner path in `SubprocessDispatcher.RUNNER_MAP` —
|
|
@@ -12,11 +12,6 @@ Recent improvements add first-class asynchronous job processing with a
|
|
|
12
12
|
cross-process JobManager,
|
|
13
13
|
background task execution, and standardized result models for immediate client
|
|
14
14
|
responses while work continues in the background.
|
|
15
|
-
Version 0.0.12.dev1 introduces multi-language SDK support, enabling
|
|
16
|
-
dispatching quantum tasks to non-Python runtimes (e.g., JavaScript) via
|
|
17
|
-
subprocess with circuit adaptation and result serialization.
|
|
18
|
-
Version 0.0.12.dev2 fixes the JavaScript handler runner path in
|
|
19
|
-
`SubprocessDispatcher` so that the subprocess command resolves correctly.
|
|
20
15
|
|
|
21
16
|
## Features
|
|
22
17
|
|
|
@@ -69,6 +64,28 @@ Notes:
|
|
|
69
64
|
|
|
70
65
|
## Recently Changes Highlights
|
|
71
66
|
|
|
67
|
+
### v0.0.12.dev4
|
|
68
|
+
- Version bumps to 0.0.12.dev4.
|
|
69
|
+
- Fix: `AsyncInvocationTask._on_task_done` now properly fires the step error
|
|
70
|
+
callback when a background task raises an unhandled exception — reads
|
|
71
|
+
`onErrorCallbackUrl` from the event's `preparation` step and calls
|
|
72
|
+
`update_job_metadata` with a structured error response.
|
|
73
|
+
- Fix: circuit preparation failure in `Invocation.__pre_execute` now returns
|
|
74
|
+
`None` instead of raising `ValueError`, allowing the caller to handle the
|
|
75
|
+
empty result gracefully without an unhandled exception propagating.
|
|
76
|
+
- Refactor: replace stdlib `logging` with the project’s loguru-based logger
|
|
77
|
+
(via `config.logging_config`) in `SubprocessBridge`, `CircuitAdapter`, and
|
|
78
|
+
`SubprocessDispatcher`; all three now call `logger.bind(context=__name__)`.
|
|
79
|
+
- Style: reformat `SubprocessBridge` and `SubprocessDispatcher` from 2-space
|
|
80
|
+
to 4-space indentation, consistent with the rest of the codebase.
|
|
81
|
+
|
|
82
|
+
### v0.0.12.dev3
|
|
83
|
+
- Version bumps to 0.0.12.dev3.
|
|
84
|
+
- Add `QUANTUM_HPC` SDK enum value (display name `"quantum hpc"`) to support
|
|
85
|
+
the Quapp HPC backend.
|
|
86
|
+
- Make `circuitExportUrl` optional in `Invocation` for SDKs that do not
|
|
87
|
+
require a circuit-export step.
|
|
88
|
+
|
|
72
89
|
### v0.0.12.dev2
|
|
73
90
|
- Version bumps to 0.0.12.dev2.
|
|
74
91
|
- Fix: correct JavaScript handler runner path in `SubprocessDispatcher.RUNNER_MAP` —
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "quapp-common"
|
|
7
|
-
version = "0.0.12.
|
|
7
|
+
version = "0.0.12.dev4"
|
|
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" }]
|
|
@@ -9,9 +9,12 @@ from starlette.responses import JSONResponse
|
|
|
9
9
|
|
|
10
10
|
from ..async_tasks.async_task import AsyncTask
|
|
11
11
|
from ..component.backend.job_manager import JobManager
|
|
12
|
+
from ..component.callback.update_job_metadata import update_job_metadata
|
|
12
13
|
from ..config.logging_config import logger
|
|
13
14
|
from ..factory.handler_factory import HandlerFactory
|
|
14
15
|
from ..model.invocation import Event, Error, Success
|
|
16
|
+
from ..util.http_utils import get_custom_header
|
|
17
|
+
from ..util.response_utils import build_error_job_response
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
class AsyncInvocationTask(AsyncTask):
|
|
@@ -72,9 +75,30 @@ class AsyncInvocationTask(AsyncTask):
|
|
|
72
75
|
task = asyncio.create_task(run_in_threadpool(_run_handle))
|
|
73
76
|
|
|
74
77
|
def _on_task_done(t: asyncio.Task):
|
|
75
|
-
if
|
|
78
|
+
if t.cancelled() or t.exception() is None:
|
|
79
|
+
return
|
|
80
|
+
exc = t.exception()
|
|
81
|
+
logger.exception(
|
|
82
|
+
f"Background job task failed for job {job_id}: {exc}")
|
|
83
|
+
try:
|
|
84
|
+
event_data = self.event.json()
|
|
85
|
+
on_error_url = (event_data.get('preparation') or {}).get(
|
|
86
|
+
'onErrorCallbackUrl')
|
|
87
|
+
if on_error_url:
|
|
88
|
+
error_response = build_error_job_response(
|
|
89
|
+
exc,
|
|
90
|
+
message=f"Unhandled background task error: {exc}")
|
|
91
|
+
error_response.user_token = event_data.get('userToken')
|
|
92
|
+
error_response.user_identity = event_data.get(
|
|
93
|
+
'userIdentity')
|
|
94
|
+
error_response.project_header = get_custom_header(
|
|
95
|
+
event_data, 'projectHeader')
|
|
96
|
+
error_response.workspace_header = get_custom_header(
|
|
97
|
+
event_data, 'workspaceHeader')
|
|
98
|
+
update_job_metadata(error_response, on_error_url)
|
|
99
|
+
except Exception as fallback_exc:
|
|
76
100
|
logger.exception(
|
|
77
|
-
f"
|
|
101
|
+
f"Failed to notify BE of error for job {job_id}: {fallback_exc}")
|
|
78
102
|
|
|
79
103
|
task.add_done_callback(_on_task_done)
|
|
80
104
|
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""
|
|
2
|
+
QApp Platform Project
|
|
3
|
+
bridge.py
|
|
4
|
+
Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
5
|
+
"""
|
|
6
|
+
# Quapp Platform Project
|
|
7
|
+
# bridge.py
|
|
8
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
9
|
+
|
|
10
|
+
from config.logging_config import logger
|
|
11
|
+
from .circuit_adapter import CircuitAdapter
|
|
12
|
+
from .dispatcher import SubprocessDispatcher
|
|
13
|
+
from .result_serializer import ResultSerializer
|
|
14
|
+
from ..enum.language import Language
|
|
15
|
+
from ..enum.sdk import Sdk
|
|
16
|
+
|
|
17
|
+
logger = logger.bind(context=__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SubprocessBridge:
|
|
21
|
+
"""Bridge between the Python invocation chain and JS subprocess handlers.
|
|
22
|
+
|
|
23
|
+
Provides processing() and post_processing() callables that can be passed
|
|
24
|
+
to AsyncInvocationTask in place of Python handler functions. Internally,
|
|
25
|
+
each call dispatches to the JS subprocess with the appropriate action,
|
|
26
|
+
then converts the result (circuit or post-processed data) back to
|
|
27
|
+
Python-native objects.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def __init__(self, language: Language, sdk: Sdk):
|
|
31
|
+
self.language = language
|
|
32
|
+
self.sdk = sdk
|
|
33
|
+
|
|
34
|
+
def processing(self, invocation_input: dict):
|
|
35
|
+
"""Call JS handler's processing() and convert a result to a native circuit.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
invocation_input: The input dict from the invocation request.
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
A native SDK circuit object (QuantumCircuit, BQM, etc.)
|
|
42
|
+
|
|
43
|
+
Raises:
|
|
44
|
+
RuntimeError: If the JS subprocess returns an error status.
|
|
45
|
+
"""
|
|
46
|
+
logger.info(
|
|
47
|
+
f"SubprocessBridge.processing: dispatching to {self.language.value}")
|
|
48
|
+
|
|
49
|
+
result = SubprocessDispatcher.dispatch(self.language, {
|
|
50
|
+
"action": "processing",
|
|
51
|
+
"input" : invocation_input
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
if result.get("status") == "error":
|
|
55
|
+
raise RuntimeError(f"JS processing failed: {result.get('error')}")
|
|
56
|
+
|
|
57
|
+
payload = result.get("result")
|
|
58
|
+
if payload is None:
|
|
59
|
+
raise RuntimeError("JS processing returned no 'result' field")
|
|
60
|
+
logger.debug(
|
|
61
|
+
f"SubprocessBridge.processing: JS returned format={payload.get('format')}")
|
|
62
|
+
|
|
63
|
+
return CircuitAdapter.adapt(self.sdk, payload)
|
|
64
|
+
|
|
65
|
+
def post_processing(self, job_result):
|
|
66
|
+
"""Serialize a job result, send to JS handler's postProcessing(), return a result.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
job_result: The raw job results from the quantum device execution.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
The post-processed result from the JS handler.
|
|
73
|
+
|
|
74
|
+
Raises:
|
|
75
|
+
RuntimeError: If the JS subprocess returns an error status.
|
|
76
|
+
"""
|
|
77
|
+
logger.info(
|
|
78
|
+
f"SubprocessBridge.post_processing: dispatching to {self.language.value}")
|
|
79
|
+
|
|
80
|
+
serialized = ResultSerializer.serialize(job_result)
|
|
81
|
+
|
|
82
|
+
result = SubprocessDispatcher.dispatch(self.language, {
|
|
83
|
+
"action" : "post_processing",
|
|
84
|
+
"job_result": serialized
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
if result.get("status") == "error":
|
|
88
|
+
raise RuntimeError(
|
|
89
|
+
f"JS post_processing failed: {result.get('error')}")
|
|
90
|
+
|
|
91
|
+
payload = result.get("result")
|
|
92
|
+
if payload is None:
|
|
93
|
+
raise RuntimeError("JS post_processing returned no 'result' field")
|
|
94
|
+
return payload
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/circuit_adapter.py
RENAMED
|
@@ -5,11 +5,10 @@
|
|
|
5
5
|
# circuit_adapter.py
|
|
6
6
|
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
|
|
8
|
+
from config.logging_config import logger
|
|
10
9
|
from ..enum.sdk import Sdk
|
|
11
10
|
|
|
12
|
-
logger =
|
|
11
|
+
logger = logger.bind(context=__name__)
|
|
13
12
|
|
|
14
13
|
|
|
15
14
|
class CircuitAdapter:
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""
|
|
2
|
+
QApp Platform Project dispatcher.py Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
# Quapp Platform Project
|
|
6
|
+
# dispatcher.py
|
|
7
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import subprocess
|
|
12
|
+
|
|
13
|
+
from config.logging_config import logger
|
|
14
|
+
from ..enum.language import Language
|
|
15
|
+
|
|
16
|
+
logger = logger.bind(context=__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class SubprocessDispatcher:
|
|
20
|
+
"""Dispatch handler execution to non-Python runtimes via subprocess."""
|
|
21
|
+
|
|
22
|
+
# Override this at application startup to set a stable working directory
|
|
23
|
+
# for subprocess runners (e.g., the project root containing function/).
|
|
24
|
+
# Defaults to os.getcwd() at dispatch time if not set.
|
|
25
|
+
WORKING_DIR: str = None
|
|
26
|
+
|
|
27
|
+
RUNNER_MAP = {
|
|
28
|
+
Language.JAVASCRIPT: {
|
|
29
|
+
'command': ['node', 'handler_runner.js'],
|
|
30
|
+
'timeout': 300,
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@staticmethod
|
|
35
|
+
def dispatch(language: Language, request_data: dict,
|
|
36
|
+
timeout: int = None) -> dict:
|
|
37
|
+
"""Dispatch request_data to a subprocess runner.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
language: The target language runtime.
|
|
41
|
+
request_data: Dict to serialize as JSON and pipe to stdin.
|
|
42
|
+
timeout: Optional timeout in seconds. Overrides the runner by default.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Parsed JSON dict from the subprocess stdout.
|
|
46
|
+
"""
|
|
47
|
+
runner = SubprocessDispatcher.RUNNER_MAP.get(language)
|
|
48
|
+
if runner is None:
|
|
49
|
+
raise ValueError(f"No runner configured for language: {language}")
|
|
50
|
+
|
|
51
|
+
effective_timeout = timeout if timeout is not None else runner[
|
|
52
|
+
'timeout']
|
|
53
|
+
action = request_data.get('action', 'handle')
|
|
54
|
+
logger.info(f"Dispatching to {language.value} runner, action={action}, "
|
|
55
|
+
f"timeout={effective_timeout}s")
|
|
56
|
+
|
|
57
|
+
payload = json.dumps(request_data)
|
|
58
|
+
cwd = SubprocessDispatcher.WORKING_DIR or os.getcwd()
|
|
59
|
+
result = subprocess.run(runner['command'], input=payload,
|
|
60
|
+
capture_output=True, text=True,
|
|
61
|
+
timeout=effective_timeout, cwd=cwd)
|
|
62
|
+
|
|
63
|
+
if result.stderr:
|
|
64
|
+
logger.debug(f"Subprocess stderr: {result.stderr[:500]}")
|
|
65
|
+
|
|
66
|
+
if result.returncode != 0:
|
|
67
|
+
logger.error(
|
|
68
|
+
f"Handler runner failed (exit={result.returncode}): {result.stderr[:500]}")
|
|
69
|
+
raise RuntimeError(f"Handler runner failed: {result.stderr}")
|
|
70
|
+
|
|
71
|
+
logger.debug(f"Subprocess completed successfully, action={action}")
|
|
72
|
+
return json.loads(result.stdout)
|
|
73
|
+
|
|
74
|
+
@staticmethod
|
|
75
|
+
def is_subprocess_language(language_str: str) -> bool:
|
|
76
|
+
try:
|
|
77
|
+
lang = Language.resolve(language_str)
|
|
78
|
+
return lang != Language.PYTHON
|
|
79
|
+
except Exception:
|
|
80
|
+
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: quapp-common
|
|
3
|
-
Version: 0.0.12.
|
|
3
|
+
Version: 0.0.12.dev4
|
|
4
4
|
Summary: Quapp common library supporting Quapp Platform for Quantum Computing
|
|
5
5
|
Author-email: "CITYNOW Co. Ltd. " <corp@citynow.vn>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -49,11 +49,6 @@ Recent improvements add first-class asynchronous job processing with a
|
|
|
49
49
|
cross-process JobManager,
|
|
50
50
|
background task execution, and standardized result models for immediate client
|
|
51
51
|
responses while work continues in the background.
|
|
52
|
-
Version 0.0.12.dev1 introduces multi-language SDK support, enabling
|
|
53
|
-
dispatching quantum tasks to non-Python runtimes (e.g., JavaScript) via
|
|
54
|
-
subprocess with circuit adaptation and result serialization.
|
|
55
|
-
Version 0.0.12.dev2 fixes the JavaScript handler runner path in
|
|
56
|
-
`SubprocessDispatcher` so that the subprocess command resolves correctly.
|
|
57
52
|
|
|
58
53
|
## Features
|
|
59
54
|
|
|
@@ -106,6 +101,28 @@ Notes:
|
|
|
106
101
|
|
|
107
102
|
## Recently Changes Highlights
|
|
108
103
|
|
|
104
|
+
### v0.0.12.dev4
|
|
105
|
+
- Version bumps to 0.0.12.dev4.
|
|
106
|
+
- Fix: `AsyncInvocationTask._on_task_done` now properly fires the step error
|
|
107
|
+
callback when a background task raises an unhandled exception — reads
|
|
108
|
+
`onErrorCallbackUrl` from the event's `preparation` step and calls
|
|
109
|
+
`update_job_metadata` with a structured error response.
|
|
110
|
+
- Fix: circuit preparation failure in `Invocation.__pre_execute` now returns
|
|
111
|
+
`None` instead of raising `ValueError`, allowing the caller to handle the
|
|
112
|
+
empty result gracefully without an unhandled exception propagating.
|
|
113
|
+
- Refactor: replace stdlib `logging` with the project’s loguru-based logger
|
|
114
|
+
(via `config.logging_config`) in `SubprocessBridge`, `CircuitAdapter`, and
|
|
115
|
+
`SubprocessDispatcher`; all three now call `logger.bind(context=__name__)`.
|
|
116
|
+
- Style: reformat `SubprocessBridge` and `SubprocessDispatcher` from 2-space
|
|
117
|
+
to 4-space indentation, consistent with the rest of the codebase.
|
|
118
|
+
|
|
119
|
+
### v0.0.12.dev3
|
|
120
|
+
- Version bumps to 0.0.12.dev3.
|
|
121
|
+
- Add `QUANTUM_HPC` SDK enum value (display name `"quantum hpc"`) to support
|
|
122
|
+
the Quapp HPC backend.
|
|
123
|
+
- Make `circuitExportUrl` optional in `Invocation` for SDKs that do not
|
|
124
|
+
require a circuit-export step.
|
|
125
|
+
|
|
109
126
|
### v0.0.12.dev2
|
|
110
127
|
- Version bumps to 0.0.12.dev2.
|
|
111
128
|
- Fix: correct JavaScript handler runner path in `SubprocessDispatcher.RUNNER_MAP` —
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
QApp Platform Project
|
|
3
|
-
bridge.py
|
|
4
|
-
Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
5
|
-
"""
|
|
6
|
-
# Quapp Platform Project
|
|
7
|
-
# bridge.py
|
|
8
|
-
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
9
|
-
|
|
10
|
-
import logging
|
|
11
|
-
|
|
12
|
-
from .circuit_adapter import CircuitAdapter
|
|
13
|
-
from .dispatcher import SubprocessDispatcher
|
|
14
|
-
from .result_serializer import ResultSerializer
|
|
15
|
-
from ..enum.language import Language
|
|
16
|
-
from ..enum.sdk import Sdk
|
|
17
|
-
|
|
18
|
-
logger = logging.getLogger(__name__)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class SubprocessBridge:
|
|
22
|
-
"""Bridge between the Python invocation chain and JS subprocess handlers.
|
|
23
|
-
|
|
24
|
-
Provides processing() and post_processing() callables that can be passed
|
|
25
|
-
to AsyncInvocationTask in place of Python handler functions. Internally,
|
|
26
|
-
each call dispatches to the JS subprocess with the appropriate action,
|
|
27
|
-
then converts the result (circuit or post-processed data) back to
|
|
28
|
-
Python-native objects.
|
|
29
|
-
"""
|
|
30
|
-
|
|
31
|
-
def __init__(self, language: Language, sdk: Sdk):
|
|
32
|
-
self.language = language
|
|
33
|
-
self.sdk = sdk
|
|
34
|
-
|
|
35
|
-
def processing(self, invocation_input: dict):
|
|
36
|
-
"""Call JS handler's processing() and convert a result to a native circuit.
|
|
37
|
-
|
|
38
|
-
Args:
|
|
39
|
-
invocation_input: The input dict from the invocation request.
|
|
40
|
-
|
|
41
|
-
Returns:
|
|
42
|
-
A native SDK circuit object (QuantumCircuit, BQM, etc.)
|
|
43
|
-
|
|
44
|
-
Raises:
|
|
45
|
-
RuntimeError: If the JS subprocess returns an error status.
|
|
46
|
-
"""
|
|
47
|
-
logger.info(
|
|
48
|
-
f"SubprocessBridge.processing: dispatching to {self.language.value}")
|
|
49
|
-
|
|
50
|
-
result = SubprocessDispatcher.dispatch(self.language, {
|
|
51
|
-
"action": "processing",
|
|
52
|
-
"input" : invocation_input
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
if result.get("status") == "error":
|
|
56
|
-
raise RuntimeError(f"JS processing failed: {result.get('error')}")
|
|
57
|
-
|
|
58
|
-
payload = result.get("result")
|
|
59
|
-
if payload is None:
|
|
60
|
-
raise RuntimeError("JS processing returned no 'result' field")
|
|
61
|
-
logger.debug(
|
|
62
|
-
f"SubprocessBridge.processing: JS returned format={payload.get('format')}")
|
|
63
|
-
|
|
64
|
-
return CircuitAdapter.adapt(self.sdk, payload)
|
|
65
|
-
|
|
66
|
-
def post_processing(self, job_result):
|
|
67
|
-
"""Serialize a job result, send to JS handler's postProcessing(), return a result.
|
|
68
|
-
|
|
69
|
-
Args:
|
|
70
|
-
job_result: The raw job results from the quantum device execution.
|
|
71
|
-
|
|
72
|
-
Returns:
|
|
73
|
-
The post-processed result from the JS handler.
|
|
74
|
-
|
|
75
|
-
Raises:
|
|
76
|
-
RuntimeError: If the JS subprocess returns an error status.
|
|
77
|
-
"""
|
|
78
|
-
logger.info(
|
|
79
|
-
f"SubprocessBridge.post_processing: dispatching to {self.language.value}")
|
|
80
|
-
|
|
81
|
-
serialized = ResultSerializer.serialize(job_result)
|
|
82
|
-
|
|
83
|
-
result = SubprocessDispatcher.dispatch(self.language, {
|
|
84
|
-
"action" : "post_processing",
|
|
85
|
-
"job_result": serialized
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
if result.get("status") == "error":
|
|
89
|
-
raise RuntimeError(f"JS post_processing failed: {result.get('error')}")
|
|
90
|
-
|
|
91
|
-
payload = result.get("result")
|
|
92
|
-
if payload is None:
|
|
93
|
-
raise RuntimeError("JS post_processing returned no 'result' field")
|
|
94
|
-
return payload
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
QApp Platform Project dispatcher.py Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
# Quapp Platform Project
|
|
6
|
-
# dispatcher.py
|
|
7
|
-
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
import logging
|
|
11
|
-
import os
|
|
12
|
-
import subprocess
|
|
13
|
-
|
|
14
|
-
from ..enum.language import Language
|
|
15
|
-
|
|
16
|
-
logger = logging.getLogger(__name__)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class SubprocessDispatcher:
|
|
20
|
-
"""Dispatch handler execution to non-Python runtimes via subprocess."""
|
|
21
|
-
|
|
22
|
-
# Override this at application startup to set a stable working directory
|
|
23
|
-
# for subprocess runners (e.g., the project root containing function/).
|
|
24
|
-
# Defaults to os.getcwd() at dispatch time if not set.
|
|
25
|
-
WORKING_DIR: str = None
|
|
26
|
-
|
|
27
|
-
RUNNER_MAP = {
|
|
28
|
-
Language.JAVASCRIPT: {
|
|
29
|
-
'command': ['node', 'handler_runner.js'],
|
|
30
|
-
'timeout': 300,
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
@staticmethod
|
|
35
|
-
def dispatch(language: Language, request_data: dict,
|
|
36
|
-
timeout: int = None) -> dict:
|
|
37
|
-
"""Dispatch request_data to a subprocess runner.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
language: The target language runtime.
|
|
41
|
-
request_data: Dict to serialize as JSON and pipe to stdin.
|
|
42
|
-
timeout: Optional timeout in seconds. Overrides the runner by default.
|
|
43
|
-
|
|
44
|
-
Returns:
|
|
45
|
-
Parsed JSON dict from the subprocess stdout.
|
|
46
|
-
"""
|
|
47
|
-
runner = SubprocessDispatcher.RUNNER_MAP.get(language)
|
|
48
|
-
if runner is None:
|
|
49
|
-
raise ValueError(f"No runner configured for language: {language}")
|
|
50
|
-
|
|
51
|
-
effective_timeout = timeout if timeout is not None else runner['timeout']
|
|
52
|
-
action = request_data.get('action', 'handle')
|
|
53
|
-
logger.info(f"Dispatching to {language.value} runner, action={action}, "
|
|
54
|
-
f"timeout={effective_timeout}s")
|
|
55
|
-
|
|
56
|
-
payload = json.dumps(request_data)
|
|
57
|
-
cwd = SubprocessDispatcher.WORKING_DIR or os.getcwd()
|
|
58
|
-
result = subprocess.run(runner['command'], input=payload,
|
|
59
|
-
capture_output=True, text=True,
|
|
60
|
-
timeout=effective_timeout, cwd=cwd)
|
|
61
|
-
|
|
62
|
-
if result.stderr:
|
|
63
|
-
logger.debug(f"Subprocess stderr: {result.stderr[:500]}")
|
|
64
|
-
|
|
65
|
-
if result.returncode != 0:
|
|
66
|
-
logger.error(
|
|
67
|
-
f"Handler runner failed (exit={result.returncode}): {result.stderr[:500]}")
|
|
68
|
-
raise RuntimeError(f"Handler runner failed: {result.stderr}")
|
|
69
|
-
|
|
70
|
-
logger.debug(f"Subprocess completed successfully, action={action}")
|
|
71
|
-
return json.loads(result.stdout)
|
|
72
|
-
|
|
73
|
-
@staticmethod
|
|
74
|
-
def is_subprocess_language(language_str: str) -> bool:
|
|
75
|
-
try:
|
|
76
|
-
lang = Language.resolve(language_str)
|
|
77
|
-
return lang != Language.PYTHON
|
|
78
|
-
except Exception:
|
|
79
|
-
return False
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/async_tasks/async_task.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/backend/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/backend/job_fetcher.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/backend/job_fetching.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/backend/job_manager.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/callback/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/component/device/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/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.12.dev3 → quapp_common-0.0.12.dev4}/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.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/callback/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/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.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/response/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/response/authentication.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/data/response/custom_header.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/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.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/status/job_status.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/enum/status/status_code.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/factory/device_factory.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/factory/handler_factory.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/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.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/model/device/custom_device.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/model/provider/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/model/provider/provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common/util/json_parser_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.12.dev3 → quapp_common-0.0.12.dev4}/quapp_common.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|