iqm-station-control-client 4.1.0__tar.gz → 5.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/CHANGELOG.rst +17 -0
- {iqm_station_control_client-4.1.0/src/iqm_station_control_client.egg-info → iqm_station_control_client-5.0.0}/PKG-INFO +1 -1
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/iqm_server_client.py +4 -2
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/station_control.py +96 -39
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/models/jobs.py +8 -1
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0/src/iqm_station_control_client.egg-info}/PKG-INFO +1 -1
- iqm_station_control_client-5.0.0/version.txt +1 -0
- iqm_station_control_client-4.1.0/version.txt +0 -1
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/LICENSE.txt +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/MANIFEST.in +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/README.rst +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/API.rst +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/Makefile +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/_static/css/custom.css +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/_static/images/favicon.ico +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/_static/images/logo.png +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/_templates/autosummary-class-template.rst +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/_templates/autosummary-module-template.rst +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/changelog.rst +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/conf.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/index.rst +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/license.rst +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/pyproject.toml +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/requirements/base.in +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/requirements/base.txt +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/setup.cfg +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/setup.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/__init__.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/__init__.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/error.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/grpc_utils.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/meta_class.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/__init__.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/calibration_pb2.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/calibration_pb2.pyi +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/calibration_pb2_grpc.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/common_pb2.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/common_pb2.pyi +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/common_pb2_grpc.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/job_pb2.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/job_pb2.pyi +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/job_pb2_grpc.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/qc_pb2.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/qc_pb2.pyi +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/qc_pb2_grpc.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/uuid_pb2.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/uuid_pb2.pyi +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/proto/uuid_pb2_grpc.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/testing/__init__.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/iqm_server/testing/iqm_server_mock.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/list_models.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/serializers/__init__.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/serializers/channel_property_serializer.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/serializers/datetime_serializers.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/serializers/playlist_serializers.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/serializers/run_serializers.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/serializers/setting_node_serializer.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/serializers/struct_serializer.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/serializers/sweep_serializers.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/serializers/task_serializers.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/client/utils.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/__init__.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/list_with_meta.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/models/__init__.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/models/dut.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/models/monitor.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/models/observation.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/models/observation_set.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/models/run.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/models/sequence.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/models/sweep.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/models/type_aliases.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm/station_control/interface/pydantic_base.py +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm_station_control_client.egg-info/SOURCES.txt +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm_station_control_client.egg-info/dependency_links.txt +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm_station_control_client.egg-info/requires.txt +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/src/iqm_station_control_client.egg-info/top_level.txt +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/tests/.pylintrc +0 -0
- {iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/tests/__init__.py +0 -0
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
Version 5.0.0 (2025-05-28)
|
|
6
|
+
==========================
|
|
7
|
+
|
|
8
|
+
Breaking changes
|
|
9
|
+
----------------
|
|
10
|
+
|
|
11
|
+
- :attr:`.JobData.job_error` now has separate fields for user error message and full error log, instead of only
|
|
12
|
+
containing the full error log. :issue:`SW-1179`
|
|
13
|
+
|
|
14
|
+
Version 4.2.0 (2025-05-23)
|
|
15
|
+
==========================
|
|
16
|
+
|
|
17
|
+
Bug fixes
|
|
18
|
+
---------
|
|
19
|
+
|
|
20
|
+
- Use explicit UTF-8 encoding for JSON data in HTTP requests.
|
|
21
|
+
|
|
5
22
|
Version 4.1.0 (2025-05-21)
|
|
6
23
|
==========================
|
|
7
24
|
|
|
@@ -55,7 +55,7 @@ from iqm.station_control.interface.models import (
|
|
|
55
55
|
SweepDefinition,
|
|
56
56
|
SweepResults,
|
|
57
57
|
)
|
|
58
|
-
from iqm.station_control.interface.models.jobs import JobData, JobResult
|
|
58
|
+
from iqm.station_control.interface.models.jobs import JobData, JobError, JobResult
|
|
59
59
|
from iqm.station_control.interface.models.sweep import SweepBase
|
|
60
60
|
|
|
61
61
|
logger = logging.getLogger(__name__)
|
|
@@ -169,7 +169,9 @@ class IqmServerClient(StationControlClient, metaclass=IqmServerClientMeta):
|
|
|
169
169
|
parallel_sweep_progress=[],
|
|
170
170
|
interrupted=False,
|
|
171
171
|
),
|
|
172
|
-
job_error=job.error
|
|
172
|
+
job_error=JobError(full_error_log=job.error, user_error_message=job.error)
|
|
173
|
+
if job.HasField("error")
|
|
174
|
+
else None,
|
|
173
175
|
position=job.queue_position if job.HasField("queue_position") else None,
|
|
174
176
|
)
|
|
175
177
|
|
|
@@ -28,6 +28,7 @@ import uuid
|
|
|
28
28
|
from iqm.models.channel_properties import ChannelProperties
|
|
29
29
|
from opentelemetry import propagate, trace
|
|
30
30
|
from packaging.version import Version, parse
|
|
31
|
+
from pydantic import BaseModel
|
|
31
32
|
import requests
|
|
32
33
|
|
|
33
34
|
from exa.common.data.setting_node import SettingNode
|
|
@@ -159,7 +160,7 @@ class StationControlClient:
|
|
|
159
160
|
self._qcm_data_client = QCMDataClient(qcm_url) if qcm_url else None
|
|
160
161
|
|
|
161
162
|
@property
|
|
162
|
-
def version(self):
|
|
163
|
+
def version(self) -> str:
|
|
163
164
|
"""Return the version of the station control API this client is using."""
|
|
164
165
|
return "v1"
|
|
165
166
|
|
|
@@ -220,8 +221,8 @@ class StationControlClient:
|
|
|
220
221
|
def get_or_create_software_version_set(self, software_version_set: SoftwareVersionSet) -> int:
|
|
221
222
|
"""Get software version set ID from the database, or create if it doesn't exist."""
|
|
222
223
|
# FIXME: We don't have information if the object was created or fetched. Thus, server always responds 200 (OK).
|
|
223
|
-
|
|
224
|
-
response = self._send_request(requests.post, "software-version-sets",
|
|
224
|
+
json_str = json.dumps(software_version_set)
|
|
225
|
+
response = self._send_request(requests.post, "software-version-sets", json_str=json_str)
|
|
225
226
|
return int(response.content)
|
|
226
227
|
|
|
227
228
|
def get_settings(self) -> SettingNode:
|
|
@@ -230,7 +231,7 @@ class StationControlClient:
|
|
|
230
231
|
|
|
231
232
|
@cache
|
|
232
233
|
def _get_cached_settings(self) -> SettingNode:
|
|
233
|
-
response = self._send_request(requests.get, "settings"
|
|
234
|
+
response = self._send_request(requests.get, "settings")
|
|
234
235
|
return deserialize_setting_node(response.content)
|
|
235
236
|
|
|
236
237
|
@cache
|
|
@@ -286,8 +287,7 @@ class StationControlClient:
|
|
|
286
287
|
|
|
287
288
|
"""
|
|
288
289
|
data = serialize_sweep_job_request(sweep_definition, queue_name="sweeps")
|
|
289
|
-
|
|
290
|
-
return self._send_request(requests.post, "sweeps", data=data, headers=headers).json()
|
|
290
|
+
return self._send_request(requests.post, "sweeps", octets=data).json()
|
|
291
291
|
|
|
292
292
|
def get_sweep(self, sweep_id: uuid.UUID) -> SweepData:
|
|
293
293
|
"""Get N-dimensional sweep data from the database."""
|
|
@@ -320,8 +320,7 @@ class StationControlClient:
|
|
|
320
320
|
"""Execute an N-dimensional sweep of selected variables and save run, sweep and results."""
|
|
321
321
|
data = serialize_run_job_request(run_definition, queue_name="sweeps")
|
|
322
322
|
|
|
323
|
-
|
|
324
|
-
response = self._send_request(requests.post, "runs", data=data, headers=headers)
|
|
323
|
+
response = self._send_request(requests.post, "runs", octets=data)
|
|
325
324
|
if wait_job_completion:
|
|
326
325
|
return self._wait_job_completion(response.json()["job_id"], update_progress_callback)
|
|
327
326
|
return False
|
|
@@ -378,8 +377,8 @@ class StationControlClient:
|
|
|
378
377
|
Created observations, each including also the database created fields like ID and timestamps.
|
|
379
378
|
|
|
380
379
|
"""
|
|
381
|
-
|
|
382
|
-
response = self._send_request(requests.post, "observations",
|
|
380
|
+
json_str = self._serialize_model(ObservationDefinitionList(observation_definitions))
|
|
381
|
+
response = self._send_request(requests.post, "observations", json_str=json_str)
|
|
383
382
|
return self._create_list_with_meta(response, ObservationDataList)
|
|
384
383
|
|
|
385
384
|
def get_observations(
|
|
@@ -477,8 +476,8 @@ class StationControlClient:
|
|
|
477
476
|
Updated observations, each including also the database created fields like ID and timestamps.
|
|
478
477
|
|
|
479
478
|
"""
|
|
480
|
-
|
|
481
|
-
response = self._send_request(requests.patch, "observations",
|
|
479
|
+
json_str = self._serialize_model(ObservationUpdateList(observation_updates))
|
|
480
|
+
response = self._send_request(requests.patch, "observations", json_str=json_str)
|
|
482
481
|
return ObservationDataList.model_validate(response.json())
|
|
483
482
|
|
|
484
483
|
def query_observation_sets(self, **kwargs) -> ListWithMeta[ObservationSetData]:
|
|
@@ -519,8 +518,8 @@ class StationControlClient:
|
|
|
519
518
|
ExaError: If creation failed.
|
|
520
519
|
|
|
521
520
|
"""
|
|
522
|
-
|
|
523
|
-
response = self._send_request(requests.post, "observation-sets",
|
|
521
|
+
json_str = self._serialize_model(observation_set_definition)
|
|
522
|
+
response = self._send_request(requests.post, "observation-sets", json_str=json_str)
|
|
524
523
|
return ObservationSetData.model_validate(response.json())
|
|
525
524
|
|
|
526
525
|
def get_observation_set(self, observation_set_id: uuid.UUID) -> ObservationSetData:
|
|
@@ -552,8 +551,8 @@ class StationControlClient:
|
|
|
552
551
|
ExaError: If updating failed.
|
|
553
552
|
|
|
554
553
|
"""
|
|
555
|
-
|
|
556
|
-
response = self._send_request(requests.patch, "observation-sets",
|
|
554
|
+
json_str = self._serialize_model(observation_set_update)
|
|
555
|
+
response = self._send_request(requests.patch, "observation-sets", json_str=json_str)
|
|
557
556
|
return ObservationSetData.model_validate(response.json())
|
|
558
557
|
|
|
559
558
|
def finalize_observation_set(self, observation_set_id: uuid.UUID) -> None:
|
|
@@ -652,8 +651,8 @@ class StationControlClient:
|
|
|
652
651
|
self, sequence_metadata_definition: SequenceMetadataDefinition
|
|
653
652
|
) -> SequenceMetadataData:
|
|
654
653
|
"""Create sequence metadata in the database."""
|
|
655
|
-
|
|
656
|
-
response = self._send_request(requests.post, "sequence-metadatas",
|
|
654
|
+
json_str = self._serialize_model(sequence_metadata_definition)
|
|
655
|
+
response = self._send_request(requests.post, "sequence-metadatas", json_str=json_str)
|
|
657
656
|
return SequenceMetadataData.model_validate(response.json())
|
|
658
657
|
|
|
659
658
|
def save_sequence_result(self, sequence_result_definition: SequenceResultDefinition) -> SequenceResultData:
|
|
@@ -663,10 +662,10 @@ class StationControlClient:
|
|
|
663
662
|
Timestamps are assigned by the database. "modified_timestamp" is not set on initial creation,
|
|
664
663
|
but it's updated on each subsequent call.
|
|
665
664
|
"""
|
|
666
|
-
data = sequence_result_definition.model_dump_json()
|
|
667
665
|
# FIXME: We don't have information if the object was created or updated. Thus, server always responds 200 (OK).
|
|
666
|
+
json_str = self._serialize_model(sequence_result_definition)
|
|
668
667
|
response = self._send_request(
|
|
669
|
-
requests.put, f"sequence-results/{sequence_result_definition.sequence_id}",
|
|
668
|
+
requests.put, f"sequence-results/{sequence_result_definition.sequence_id}", json_str=json_str
|
|
670
669
|
)
|
|
671
670
|
return SequenceResultData.model_validate(response.json())
|
|
672
671
|
|
|
@@ -678,7 +677,7 @@ class StationControlClient:
|
|
|
678
677
|
def get_job(self, job_id: uuid.UUID) -> JobData:
|
|
679
678
|
"""Get job data."""
|
|
680
679
|
response = self._send_request(requests.get, f"jobs/{job_id}")
|
|
681
|
-
return response.json()
|
|
680
|
+
return JobData.model_validate(response.json())
|
|
682
681
|
|
|
683
682
|
def _wait_job_completion(self, job_id: str, update_progress_callback: Callable[[Statuses], None] | None) -> bool:
|
|
684
683
|
logger.info("Waiting for job ID: %s", job_id)
|
|
@@ -700,11 +699,11 @@ class StationControlClient:
|
|
|
700
699
|
max_seen_position = 0
|
|
701
700
|
while True:
|
|
702
701
|
job = self._poll_job(job_id)
|
|
703
|
-
if job
|
|
702
|
+
if job.job_status >= JobStatus.EXECUTION_START:
|
|
704
703
|
if max_seen_position:
|
|
705
704
|
update_progress_callback([("Progress in queue", max_seen_position, max_seen_position)])
|
|
706
|
-
return job
|
|
707
|
-
position = job
|
|
705
|
+
return job.job_status
|
|
706
|
+
position = job.position
|
|
708
707
|
|
|
709
708
|
if position == 0:
|
|
710
709
|
sleep(1)
|
|
@@ -721,26 +720,70 @@ class StationControlClient:
|
|
|
721
720
|
# Keep polling job status until it finishes, and update progress with `update_progress_callback`.
|
|
722
721
|
while True:
|
|
723
722
|
job = self._poll_job(job_id)
|
|
724
|
-
update_progress_callback(job
|
|
725
|
-
if
|
|
723
|
+
update_progress_callback(job.job_result.parallel_sweep_progress)
|
|
724
|
+
if job.job_status in JobStatus.terminal_statuses():
|
|
726
725
|
return
|
|
727
726
|
sleep(1)
|
|
728
727
|
|
|
729
728
|
def _poll_job(self, job_id: str) -> JobData:
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
729
|
+
response = self._send_request(requests.get, f"jobs/{job_id}")
|
|
730
|
+
job = JobData.model_validate(response.json())
|
|
731
|
+
if job.job_status == JobStatus.FAILED:
|
|
732
|
+
raise InternalServerError(f"Job: {job.job_id}\n{job.job_error}")
|
|
733
733
|
return job
|
|
734
734
|
|
|
735
|
+
@staticmethod
|
|
736
|
+
def _serialize_model(model: BaseModel) -> str:
|
|
737
|
+
"""Serialize a Pydantic model into a JSON string.
|
|
738
|
+
|
|
739
|
+
All Pydantic models should be serialized using this method, to keep the client behavior uniform.
|
|
740
|
+
|
|
741
|
+
TODO add a corresponding deserialization method.
|
|
742
|
+
|
|
743
|
+
Args:
|
|
744
|
+
model: Pydantic model to JSON-serialize.
|
|
745
|
+
|
|
746
|
+
Returns:
|
|
747
|
+
Corresponding JSON string, may contain arbitrary Unicode characters.
|
|
748
|
+
|
|
749
|
+
"""
|
|
750
|
+
# Strings in model can contain non-latin-1 characters. Unlike json.dumps which encodes non-latin-1 chars
|
|
751
|
+
# using the \uXXXX syntax, BaseModel.model_dump_json() keeps them in the produced JSON str.
|
|
752
|
+
return model.model_dump_json()
|
|
753
|
+
|
|
735
754
|
def _send_request(
|
|
736
|
-
self,
|
|
755
|
+
self,
|
|
756
|
+
http_method: Callable[..., requests.Response],
|
|
757
|
+
url_path: str,
|
|
758
|
+
*,
|
|
759
|
+
json_str: str | None = None,
|
|
760
|
+
octets: bytes | None = None,
|
|
761
|
+
params: dict[str, Any] | None = None,
|
|
762
|
+
headers: dict[str, str] | None = None,
|
|
737
763
|
) -> requests.Response:
|
|
738
|
-
|
|
764
|
+
"""Send a HTTP request.
|
|
765
|
+
|
|
766
|
+
Parameters ``json_str``, ``octets`` and ``params`` are mutually exclusive.
|
|
767
|
+
The first non-None argument (in this order) will be used to construct the body of the request.
|
|
768
|
+
|
|
769
|
+
Args:
|
|
770
|
+
http_method: HTTP method to use for the request, any of requests.[post|get|put|head|delete|patch|options].
|
|
771
|
+
url_path: URL for the request.
|
|
772
|
+
json_str: JSON string to store in the body, may contain arbitrary Unicode characters.
|
|
773
|
+
octets: Pre-serialized binary data to store in the body.
|
|
774
|
+
params: HTTP query to store in the body.
|
|
775
|
+
headers: Additional HTTP headers for the request. Some may be overridden.
|
|
776
|
+
|
|
777
|
+
Returns:
|
|
778
|
+
Response to the request.
|
|
779
|
+
|
|
780
|
+
Raises:
|
|
781
|
+
StationControlError: Request was not successful.
|
|
782
|
+
|
|
783
|
+
"""
|
|
739
784
|
# Will raise an error if respectively an error response code is returned.
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
headers = kwargs.get("headers", {})
|
|
743
|
-
data = kwargs.get("data", None)
|
|
785
|
+
headers = headers or {}
|
|
786
|
+
|
|
744
787
|
if self._enable_opentelemetry:
|
|
745
788
|
parent_span_context = trace.set_span_in_context(trace.get_current_span())
|
|
746
789
|
propagate.inject(carrier=headers, context=parent_span_context)
|
|
@@ -749,9 +792,23 @@ class StationControlClient:
|
|
|
749
792
|
headers["Authorization"] = self._get_token_callback()
|
|
750
793
|
|
|
751
794
|
# Build request options explicitly
|
|
752
|
-
http_request_options
|
|
753
|
-
|
|
754
|
-
|
|
795
|
+
http_request_options: dict[str, Any] = {}
|
|
796
|
+
if json_str is not None:
|
|
797
|
+
# Must be able to handle JSON strings with arbitrary unicode characters, so we use an explicit
|
|
798
|
+
# encoding into bytes, and set the headers so the recipient can decode the request body correctly.
|
|
799
|
+
http_request_options["data"] = json_str.encode("utf-8")
|
|
800
|
+
headers["Content-Type"] = "application/json; charset=UTF-8"
|
|
801
|
+
elif octets is not None:
|
|
802
|
+
http_request_options["data"] = octets
|
|
803
|
+
headers["Content-Type"] = "application/octet-stream"
|
|
804
|
+
elif params is not None:
|
|
805
|
+
http_request_options["params"] = params
|
|
806
|
+
# otherwise no body-related parameter will be passed to requests
|
|
807
|
+
|
|
808
|
+
if headers:
|
|
809
|
+
# do not pass empty headers dict
|
|
810
|
+
http_request_options["headers"] = headers
|
|
811
|
+
|
|
755
812
|
url = f"{self.root_url}/{url_path}"
|
|
756
813
|
# TODO SW-1387: Use v1 API
|
|
757
814
|
# url = f"{self.root_url}/{self.version}/{url_path}"
|
|
@@ -803,7 +860,7 @@ class StationControlClient:
|
|
|
803
860
|
return parse(version("iqm-station-control-client"))
|
|
804
861
|
|
|
805
862
|
@staticmethod
|
|
806
|
-
def _clean_query_parameters(model: Any, **kwargs) -> dict:
|
|
863
|
+
def _clean_query_parameters(model: Any, **kwargs) -> dict[str, Any]:
|
|
807
864
|
if issubclass(model, PydanticBase) and "invalid" in model.model_fields.keys() and "invalid" not in kwargs:
|
|
808
865
|
# Get only valid items by default, "invalid=None" would return also invalid ones.
|
|
809
866
|
# This default has to be set on the client side, server side uses default "None".
|
|
@@ -38,13 +38,20 @@ class JobResult(PydanticBase):
|
|
|
38
38
|
interrupted: bool
|
|
39
39
|
|
|
40
40
|
|
|
41
|
+
class JobError(PydanticBase):
|
|
42
|
+
"""Error log for a job."""
|
|
43
|
+
|
|
44
|
+
full_error_log: str
|
|
45
|
+
user_error_message: str
|
|
46
|
+
|
|
47
|
+
|
|
41
48
|
class JobData(PydanticBase):
|
|
42
49
|
"""Job response data model"""
|
|
43
50
|
|
|
44
51
|
job_id: UUID
|
|
45
52
|
job_status: JobStatus
|
|
46
53
|
job_result: JobResult
|
|
47
|
-
job_error:
|
|
54
|
+
job_error: JobError | None
|
|
48
55
|
position: int | None
|
|
49
56
|
|
|
50
57
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
5.0.0
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
4.1.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/_static/css/custom.css
RENAMED
|
File without changes
|
|
File without changes
|
{iqm_station_control_client-4.1.0 → iqm_station_control_client-5.0.0}/docs/_static/images/logo.png
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|