quapp-common 0.0.14.dev5__tar.gz → 0.0.14.dev6__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.14.dev5/quapp_common.egg-info → quapp_common-0.0.14.dev6}/PKG-INFO +1 -1
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/pyproject.toml +1 -1
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/async_tasks/post_processing_task.py +14 -9
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/config/log_context.py +9 -1
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/model/device/custom_device.py +23 -15
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6/quapp_common.egg-info}/PKG-INFO +1 -1
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common.egg-info/SOURCES.txt +1 -0
- quapp_common-0.0.14.dev6/tests/test_phase_coverage.py +196 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/LICENSE +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/README.md +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/async_tasks/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/async_tasks/async_invocation_task.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/async_tasks/async_task.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/async_tasks/export_circuit_task.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/invocation.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/job_fetcher.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/job_fetching.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/job_heartbeat.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/job_manager.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/bridge.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/callback/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/callback/update_job_metadata.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/circuit_adapter.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/device/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/device/device_selection.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/dispatcher.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/result_serializer.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/config/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/config/logging_config.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/config/thread_config.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/async_task/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/async_task/circuit_export/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/async_task/circuit_export/backend_holder.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/async_task/circuit_export/circuit_holder.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/backend/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/backend/backend_information.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/callback/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/callback/callback_url.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/device/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/device/circuit_running_option.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/promise/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/promise/post_processing_promise.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/promise/promise.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/request/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/request/invocation_request.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/request/job_fetching_request.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/request/request.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/response/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/response/authentication.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/response/custom_header.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/response/error_report.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/response/job_response.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/base_enum.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/error_category.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/http_header.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/invocation_step.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/language.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/media_type.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/processing_unit.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/provider_tag.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/runtime_phase.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/sdk.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/status/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/status/job_status.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/status/status_code.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/token_type.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/factory/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/factory/device_factory.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/factory/handler_factory.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/factory/provider_factory.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/handler/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/handler/handler.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/model/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/model/device/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/model/device/device.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/model/invocation.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/model/provider/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/model/provider/provider.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/util/__init__.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/util/error_classifier.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/util/file_utils.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/util/http_utils.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/util/invocation_failure.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/util/json_parser_utils.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/util/response_utils.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common.egg-info/dependency_links.txt +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common.egg-info/requires.txt +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common.egg-info/top_level.txt +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/setup.cfg +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_circuit_adapter.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_dispatcher_runner_error.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_error_classifier.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_error_log_dedupe.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_error_report.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_invocation_failure.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_job_heartbeat.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_json_parser_utils.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_language_dispatcher.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_log_secret_leak.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_logging_config.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/tests/test_no_sdk_dependency.py +0 -0
- {quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/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.14.
|
|
7
|
+
version = "0.0.14.dev6"
|
|
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" }]
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
from .async_task import AsyncTask
|
|
5
5
|
from ..config.logging_config import job_logger
|
|
6
6
|
from ..data.promise.post_processing_promise import PostProcessingPromise
|
|
7
|
+
from ..config.log_context import phase
|
|
7
8
|
from ..enum.media_type import MediaType
|
|
8
9
|
from ..enum.runtime_phase import RuntimePhase
|
|
9
10
|
from ..util.http_utils import get_job_id_from_url
|
|
@@ -32,15 +33,19 @@ class PostProcessingTask(AsyncTask):
|
|
|
32
33
|
job_response.content_type = MediaType.APPLICATION_JSON
|
|
33
34
|
|
|
34
35
|
try:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
36
|
+
# RuntimePhase.POST_PROCESSING duoc dinh nghia nhung truoc day khong
|
|
37
|
+
# noi nao dung, nen code user chay trong post_processing_fn khong he
|
|
38
|
+
# co phase trong log lan trong ErrorReport.
|
|
39
|
+
with phase(RuntimePhase.POST_PROCESSING):
|
|
40
|
+
self.logger.info("Execute post_processing ...")
|
|
41
|
+
job_result_post_processing = self.post_processing_fn(
|
|
42
|
+
self.promise.job_result)
|
|
43
|
+
|
|
44
|
+
# print() cũ ghi thẳng ra stdout, không qua sink nên không được
|
|
45
|
+
# redact và không có job_id.
|
|
46
|
+
self.logger.debug(
|
|
47
|
+
f"Parsing job result: type={type(job_result_post_processing).__name__}")
|
|
48
|
+
job_response.job_result = parse(job_result_post_processing)
|
|
44
49
|
|
|
45
50
|
update_job_metadata(job_response, self.promise.callback_url.on_done)
|
|
46
51
|
|
|
@@ -69,8 +69,16 @@ def bind_job(job_id: str = None, *, sdk=None, provider=None, device=None,
|
|
|
69
69
|
@param trace_id: tự sinh nếu không truyền; dùng để join Function Log <-> Job Log
|
|
70
70
|
@return: logger đã bind toàn bộ context
|
|
71
71
|
"""
|
|
72
|
+
# Một job bị bind hai lần (AsyncInvocationTask rồi Handler.__init__), trước
|
|
73
|
+
# đây mỗi lần sinh một trace_id mới nên cùng một job có hai traceId khác
|
|
74
|
+
# nhau và filter theo traceId mất các dòng ở cửa vào. Giữ lại trace đang có
|
|
75
|
+
# khi vẫn là job đó; job khác thì phải sinh trace mới, không được trộn.
|
|
76
|
+
current = _JOB_CONTEXT.get()
|
|
77
|
+
inherited = (current.get(TRACE_ID)
|
|
78
|
+
if current.get(JOB_ID) in (None, job_id) else None)
|
|
79
|
+
|
|
72
80
|
context = {JOB_ID : job_id,
|
|
73
|
-
TRACE_ID : trace_id or uuid.uuid4().hex[:16],
|
|
81
|
+
TRACE_ID : trace_id or inherited or uuid.uuid4().hex[:16],
|
|
74
82
|
'sdk' : sdk,
|
|
75
83
|
'provider' : provider,
|
|
76
84
|
'device' : device,
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/model/device/custom_device.py
RENAMED
|
@@ -8,6 +8,7 @@ from ...component.callback.update_job_metadata import update_job_metadata
|
|
|
8
8
|
from ...data.callback.callback_url import CallbackUrl
|
|
9
9
|
from ...data.response.job_response import JobResponse
|
|
10
10
|
from ...enum.runtime_phase import RuntimePhase
|
|
11
|
+
from ...config.log_context import phase
|
|
11
12
|
from ...model.device.device import Device
|
|
12
13
|
from ...model.provider.provider import Provider
|
|
13
14
|
from ...util.response_utils import build_error_job_response
|
|
@@ -34,21 +35,28 @@ class CustomDevice(Device, ABC):
|
|
|
34
35
|
update_job_metadata(job_response, analysis_callback.on_start)
|
|
35
36
|
|
|
36
37
|
try:
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
38
|
+
# Device._on_analysis co `with phase(...)`, ban override nay khong --
|
|
39
|
+
# va day moi la ban thuc su chay: QiskitDevice, OqcCloudDevice...
|
|
40
|
+
# deu ke thua CustomDevice. Thieu no thi log cua analysis khong co
|
|
41
|
+
# field `phase`, Grafana khong loc duoc va ErrorReport cua loi trong
|
|
42
|
+
# analysis ra phase=None.
|
|
43
|
+
with phase(RuntimePhase.ANALYSIS):
|
|
44
|
+
self.logger.debug(
|
|
45
|
+
"Producing histogram data from original job result")
|
|
46
|
+
|
|
47
|
+
job_response.job_histogram = self._produce_histogram_data(
|
|
48
|
+
original_job_result)
|
|
49
|
+
|
|
50
|
+
self.logger.debug("Parsing original job result")
|
|
51
|
+
original_job_result_dict = parse(original_job_result)
|
|
52
|
+
|
|
53
|
+
self.logger.debug(
|
|
54
|
+
"Calculating execution time from parsed results")
|
|
55
|
+
self._calculate_execution_time(original_job_result_dict)
|
|
56
|
+
job_response.execution_time = self.execution_time
|
|
57
|
+
|
|
58
|
+
self.logger.debug("Retrieving shot count from job result")
|
|
59
|
+
job_response.shots = self._get_shots(original_job_result)
|
|
52
60
|
|
|
53
61
|
update_job_metadata(job_response, analysis_callback.on_done)
|
|
54
62
|
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# Quapp Platform Project
|
|
2
|
+
# test_phase_coverage.py
|
|
3
|
+
# Copyright © CITYNOW Co. Ltd. All rights reserved.
|
|
4
|
+
|
|
5
|
+
"""Mọi giai đoạn của một job phải mang `phase` trong log và trong ErrorReport.
|
|
6
|
+
|
|
7
|
+
Ba bug bị khoá ở đây, cả ba đều phát hiện từ log thật của job
|
|
8
|
+
4a5d2c56-f248-41e7-974f-a1bbea6db1c0 trên dev:
|
|
9
|
+
|
|
10
|
+
1. `CustomDevice._on_analysis` không có `with phase(...)`. `Device._on_analysis`
|
|
11
|
+
thì có, nhưng bản CHẠY THẬT là bản override trong CustomDevice --
|
|
12
|
+
QiskitDevice, OqcCloudDevice... đều kế thừa nó. Log của analysis vì thế
|
|
13
|
+
không có field `phase`, và lỗi trong analysis ra `phase=None`.
|
|
14
|
+
|
|
15
|
+
2. `RuntimePhase.POST_PROCESSING` được định nghĩa nhưng không nơi nào dùng, nên
|
|
16
|
+
code user chạy trong `post_processing_fn` không có phase.
|
|
17
|
+
|
|
18
|
+
3. `bind_job()` sinh trace_id mới mỗi lần gọi. Một job bị bind hai lần
|
|
19
|
+
(AsyncInvocationTask rồi Handler.__init__) nên cùng một job có hai traceId,
|
|
20
|
+
filter theo traceId mất các dòng ở cửa vào.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
import ast
|
|
24
|
+
import pathlib
|
|
25
|
+
|
|
26
|
+
import pytest
|
|
27
|
+
from loguru import logger
|
|
28
|
+
from quapp_common.config import log_context
|
|
29
|
+
from quapp_common.config.log_context import (
|
|
30
|
+
TRACE_ID, bind_job, effective_phase, trace_id,
|
|
31
|
+
)
|
|
32
|
+
from quapp_common.config.logging_config import _prepare_record, configure_logging
|
|
33
|
+
from quapp_common.enum.runtime_phase import RuntimePhase
|
|
34
|
+
|
|
35
|
+
PACKAGE_ROOT = pathlib.Path(__file__).resolve().parent.parent / 'quapp_common'
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@pytest.fixture(autouse=True)
|
|
39
|
+
def clean_context():
|
|
40
|
+
log_context.reset()
|
|
41
|
+
yield
|
|
42
|
+
log_context.reset()
|
|
43
|
+
configure_logging(force=True)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@pytest.fixture
|
|
47
|
+
def captured():
|
|
48
|
+
records = []
|
|
49
|
+
logger.remove()
|
|
50
|
+
sink_id = logger.add(lambda message: records.append(message.record['extra']),
|
|
51
|
+
level='DEBUG', filter=_prepare_record)
|
|
52
|
+
yield records
|
|
53
|
+
logger.remove(sink_id)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def phases_wrapped_in(relative_path):
|
|
57
|
+
"""Tập RuntimePhase được mở bằng `with phase(...)` trong một file."""
|
|
58
|
+
tree = ast.parse((PACKAGE_ROOT / relative_path).read_text(encoding='utf-8'))
|
|
59
|
+
|
|
60
|
+
found = set()
|
|
61
|
+
for node in ast.walk(tree):
|
|
62
|
+
if not isinstance(node, ast.With):
|
|
63
|
+
continue
|
|
64
|
+
for item in node.items:
|
|
65
|
+
call = item.context_expr
|
|
66
|
+
if not isinstance(call, ast.Call):
|
|
67
|
+
continue
|
|
68
|
+
name = getattr(call.func, 'id', None) or getattr(call.func, 'attr',
|
|
69
|
+
None)
|
|
70
|
+
if name != 'phase' or not call.args:
|
|
71
|
+
continue
|
|
72
|
+
argument = call.args[0]
|
|
73
|
+
if isinstance(argument, ast.Attribute):
|
|
74
|
+
found.add(argument.attr)
|
|
75
|
+
return found
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ------------------------------------------- (1) CustomDevice phai co ANALYSIS
|
|
79
|
+
|
|
80
|
+
def test_custom_device_analysis_is_wrapped():
|
|
81
|
+
"""Ban override moi la ban chay that, no phai mo phase nhu ban goc."""
|
|
82
|
+
assert 'ANALYSIS' in phases_wrapped_in('model/device/custom_device.py'), (
|
|
83
|
+
'CustomDevice._on_analysis khong co `with phase(RuntimePhase.ANALYSIS)`; '
|
|
84
|
+
'moi device Qiskit dung ban nay nen log analysis se mat field phase')
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_base_device_still_wraps_its_phases():
|
|
88
|
+
"""Khong duoc lam hong ban goc khi sua ban override."""
|
|
89
|
+
wrapped = phases_wrapped_in('model/device/device.py')
|
|
90
|
+
assert {'COMPILATION', 'EXECUTION', 'ANALYSIS'} <= wrapped
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
# ------------------------------------ (2) post_processing phai co POST_PROCESSING
|
|
94
|
+
|
|
95
|
+
def test_post_processing_task_is_wrapped():
|
|
96
|
+
wrapped = phases_wrapped_in('async_tasks/post_processing_task.py')
|
|
97
|
+
assert 'POST_PROCESSING' in wrapped, (
|
|
98
|
+
'post_processing_fn (code cua user) chay ngoai moi phase, nen loi trong '
|
|
99
|
+
'do khong biet thuoc giai doan nao')
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_every_declared_phase_is_actually_used():
|
|
103
|
+
"""RuntimePhase khai bao ra thi phai co noi mo -- tru CALLBACK/POLLING.
|
|
104
|
+
|
|
105
|
+
CALLBACK va POLLING duoc dat qua `phase=` cua build_error_job_response chu
|
|
106
|
+
khong qua `with phase(...)`, nen khong tinh o day.
|
|
107
|
+
"""
|
|
108
|
+
used = set()
|
|
109
|
+
for path in PACKAGE_ROOT.rglob('*.py'):
|
|
110
|
+
used |= phases_wrapped_in(path.relative_to(PACKAGE_ROOT))
|
|
111
|
+
|
|
112
|
+
declared = {member.name for member in RuntimePhase}
|
|
113
|
+
never_opened = declared - used - {'CALLBACK', 'POLLING'}
|
|
114
|
+
assert not never_opened, (
|
|
115
|
+
f'RuntimePhase khong bao gio duoc mo: {sorted(never_opened)}')
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# --------------------------------------------------- hanh vi khi chay that
|
|
119
|
+
|
|
120
|
+
def test_analysis_phase_reaches_the_log_record(captured):
|
|
121
|
+
"""Trong `with phase(ANALYSIS)`, dong log phai mang phase=ANALYSIS."""
|
|
122
|
+
from quapp_common.config.log_context import phase
|
|
123
|
+
|
|
124
|
+
bind_job('job_A', sdk='qiskit')
|
|
125
|
+
with phase(RuntimePhase.ANALYSIS):
|
|
126
|
+
logger.bind(**log_context.job_context()).debug('producing histogram')
|
|
127
|
+
|
|
128
|
+
record = next(r for r in captured if r.get('event') is None)
|
|
129
|
+
assert record.get('phase') == 'ANALYSIS'
|
|
130
|
+
assert record.get('job_id') == 'job_A'
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def test_failure_inside_analysis_keeps_the_phase():
|
|
134
|
+
"""`phase()` reset contextvar trong finally, nen ErrorReport phai doc duoc
|
|
135
|
+
phase da that bai qua _FAILED_PHASE."""
|
|
136
|
+
from quapp_common.config.log_context import phase
|
|
137
|
+
|
|
138
|
+
bind_job('job_B')
|
|
139
|
+
with pytest.raises(ValueError):
|
|
140
|
+
with phase(RuntimePhase.ANALYSIS):
|
|
141
|
+
raise ValueError('histogram vo')
|
|
142
|
+
|
|
143
|
+
assert effective_phase() is RuntimePhase.ANALYSIS
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def test_failure_inside_post_processing_keeps_the_phase():
|
|
147
|
+
from quapp_common.config.log_context import phase
|
|
148
|
+
|
|
149
|
+
bind_job('job_C')
|
|
150
|
+
with pytest.raises(KeyError):
|
|
151
|
+
with phase(RuntimePhase.POST_PROCESSING):
|
|
152
|
+
raise KeyError('counts')
|
|
153
|
+
|
|
154
|
+
assert effective_phase() is RuntimePhase.POST_PROCESSING
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# ------------------------------------------------- (3) trace_id khong bi tach
|
|
158
|
+
|
|
159
|
+
def test_rebinding_same_job_keeps_one_trace_id():
|
|
160
|
+
"""AsyncInvocationTask bind roi Handler.__init__ bind lai -- van mot trace."""
|
|
161
|
+
bind_job('job_SAME')
|
|
162
|
+
first = trace_id()
|
|
163
|
+
|
|
164
|
+
bind_job('job_SAME', sdk='qiskit', provider='QUAO_QUANTUM_SIMULATOR')
|
|
165
|
+
assert trace_id() == first, 'cung mot job ma co hai traceId'
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_binding_without_job_id_then_with_it_keeps_the_trace():
|
|
169
|
+
"""Cua vao HTTP bind truoc khi biet job_id; trace phai duoc mang theo."""
|
|
170
|
+
bind_job(None)
|
|
171
|
+
first = trace_id()
|
|
172
|
+
|
|
173
|
+
bind_job('job_LATE')
|
|
174
|
+
assert trace_id() == first
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def test_different_job_gets_a_fresh_trace_id():
|
|
178
|
+
"""Khong duoc tron trace giua hai job khac nhau."""
|
|
179
|
+
bind_job('job_ONE')
|
|
180
|
+
first = trace_id()
|
|
181
|
+
|
|
182
|
+
bind_job('job_TWO')
|
|
183
|
+
assert trace_id() != first
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def test_explicit_trace_id_always_wins():
|
|
187
|
+
bind_job('job_X')
|
|
188
|
+
bind_job('job_X', trace_id='deadbeefdeadbeef')
|
|
189
|
+
assert trace_id() == 'deadbeefdeadbeef'
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def test_trace_id_is_present_on_every_record(captured):
|
|
193
|
+
bind_job('job_T')
|
|
194
|
+
logger.bind(**log_context.job_context()).info('mot dong bat ky')
|
|
195
|
+
|
|
196
|
+
assert captured[-1].get(TRACE_ID)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/async_tasks/async_task.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/invocation.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/job_fetcher.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/job_fetching.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/backend/job_manager.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/callback/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/circuit_adapter.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/component/device/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/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.14.dev5 → quapp_common-0.0.14.dev6}/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.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/callback/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/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.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/response/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/response/authentication.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/response/custom_header.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/data/response/error_report.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/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
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/status/job_status.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/enum/status/status_code.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/factory/device_factory.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/factory/handler_factory.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/factory/provider_factory.py
RENAMED
|
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.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/model/provider/__init__.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/model/provider/provider.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/util/invocation_failure.py
RENAMED
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/quapp_common/util/json_parser_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
{quapp_common-0.0.14.dev5 → quapp_common-0.0.14.dev6}/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
|
|
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
|