iqm-station-control-client 8.1.0__tar.gz → 9.1.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.
Files changed (84) hide show
  1. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/CHANGELOG.rst +41 -0
  2. {iqm_station_control_client-8.1.0/src/iqm_station_control_client.egg-info → iqm_station_control_client-9.1.0}/PKG-INFO +2 -2
  3. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/requirements/base.in +1 -1
  4. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/requirements/base.txt +1 -1
  5. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/__init__.py +1 -1
  6. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/error.py +1 -1
  7. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/grpc_utils.py +5 -3
  8. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/iqm_server_client.py +179 -46
  9. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/list_models.py +18 -12
  10. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/serializers/__init__.py +1 -1
  11. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/serializers/channel_property_serializer.py +12 -6
  12. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/serializers/datetime_serializers.py +1 -1
  13. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/serializers/playlist_serializers.py +1 -1
  14. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/serializers/run_serializers.py +1 -1
  15. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/serializers/setting_node_serializer.py +1 -1
  16. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/serializers/struct_serializer.py +1 -1
  17. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/serializers/sweep_serializers.py +2 -3
  18. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/serializers/task_serializers.py +1 -1
  19. iqm_station_control_client-9.1.0/src/iqm/station_control/client/station_control.py +634 -0
  20. iqm_station_control_client-9.1.0/src/iqm/station_control/client/utils.py +71 -0
  21. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/__init__.py +1 -1
  22. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/list_with_meta.py +1 -1
  23. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/models/__init__.py +13 -1
  24. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/models/dut.py +1 -1
  25. iqm_station_control_client-9.1.0/src/iqm/station_control/interface/models/dynamic_quantum_architecture.py +98 -0
  26. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/models/observation.py +1 -1
  27. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/models/observation_set.py +15 -1
  28. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/models/run.py +1 -1
  29. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/models/sequence.py +1 -1
  30. iqm_station_control_client-9.1.0/src/iqm/station_control/interface/models/static_quantum_architecture.py +40 -0
  31. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/models/sweep.py +1 -1
  32. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/models/type_aliases.py +7 -1
  33. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/pydantic_base.py +1 -1
  34. iqm_station_control_client-9.1.0/src/iqm/station_control/interface/station_control.py +511 -0
  35. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0/src/iqm_station_control_client.egg-info}/PKG-INFO +2 -2
  36. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm_station_control_client.egg-info/SOURCES.txt +3 -1
  37. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm_station_control_client.egg-info/requires.txt +1 -1
  38. iqm_station_control_client-9.1.0/version.txt +1 -0
  39. iqm_station_control_client-8.1.0/src/iqm/station_control/client/iqm_server/meta_class.py +0 -38
  40. iqm_station_control_client-8.1.0/src/iqm/station_control/client/station_control.py +0 -876
  41. iqm_station_control_client-8.1.0/src/iqm/station_control/client/utils.py +0 -44
  42. iqm_station_control_client-8.1.0/version.txt +0 -1
  43. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/LICENSE.txt +0 -0
  44. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/MANIFEST.in +0 -0
  45. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/README.rst +0 -0
  46. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/API.rst +0 -0
  47. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/Makefile +0 -0
  48. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/_static/css/custom.css +0 -0
  49. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/_static/images/favicon.ico +0 -0
  50. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/_static/images/logo.png +0 -0
  51. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/_templates/autosummary-class-template.rst +0 -0
  52. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/_templates/autosummary-module-template.rst +0 -0
  53. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/changelog.rst +0 -0
  54. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/conf.py +0 -0
  55. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/index.rst +0 -0
  56. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/docs/license.rst +0 -0
  57. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/pyproject.toml +0 -0
  58. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/setup.cfg +0 -0
  59. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/setup.py +0 -0
  60. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/__init__.py +0 -0
  61. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/__init__.py +0 -0
  62. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/calibration_pb2.py +0 -0
  63. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/calibration_pb2.pyi +0 -0
  64. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/calibration_pb2_grpc.py +0 -0
  65. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/common_pb2.py +0 -0
  66. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/common_pb2.pyi +0 -0
  67. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/common_pb2_grpc.py +0 -0
  68. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/job_pb2.py +0 -0
  69. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/job_pb2.pyi +0 -0
  70. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/job_pb2_grpc.py +0 -0
  71. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/qc_pb2.py +0 -0
  72. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/qc_pb2.pyi +0 -0
  73. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/qc_pb2_grpc.py +0 -0
  74. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/uuid_pb2.py +0 -0
  75. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/uuid_pb2.pyi +0 -0
  76. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/proto/uuid_pb2_grpc.py +0 -0
  77. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/testing/__init__.py +0 -0
  78. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/client/iqm_server/testing/iqm_server_mock.py +0 -0
  79. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/models/jobs.py +0 -0
  80. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm/station_control/interface/models/monitor.py +0 -0
  81. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm_station_control_client.egg-info/dependency_links.txt +0 -0
  82. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/src/iqm_station_control_client.egg-info/top_level.txt +0 -0
  83. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/tests/.pylintrc +0 -0
  84. {iqm_station_control_client-8.1.0 → iqm_station_control_client-9.1.0}/tests/__init__.py +0 -0
@@ -2,6 +2,47 @@
2
2
  Changelog
3
3
  =========
4
4
 
5
+ Version 9.1.0 (2025-06-24)
6
+ ==========================
7
+
8
+ Bug fixes
9
+ ---------
10
+
11
+ - Requests now always have User-Agent header.
12
+
13
+ Version 9.0.0 (2025-06-13)
14
+ ==========================
15
+
16
+ Features
17
+ --------
18
+
19
+ - Reintroduce :class:`.StationControlInterface` and make both :class:`.StationControlClient` and :class:`.IQMServerClient` inherit it
20
+ to implement the same interface.
21
+ - Add new methods to :class:`StationControlInterface`, implemented by :class:`StationControlClient`. :class:`IQMServerClient` doesn't
22
+ implement these new methods yet, but raises ``NotImplementedError`` instead. :issue:`SW-1078`
23
+
24
+ - :meth:`get_default_calibration_set`
25
+ - :meth:`get_default_calibration_set_observations`
26
+ - :meth:`get_dynamic_quantum_architecture`
27
+ - :meth:`get_default_dynamic_quantum_architecture`
28
+ - :meth:`get_static_quantum_architecture`
29
+
30
+ - Support also string UUIDs in :class:`StationControlClient` methods, instead of only :class:`UUID` objects. This always worked in
31
+ practice, since UUIDs were instantly serialized to strings anyway. However, it gave type warnings for the users
32
+ when string given. No functional changes, but no more type warnings.
33
+
34
+ Breaking changes
35
+ ----------------
36
+
37
+ - Remove :meth:`StationControlClient.init`, :meth:`StationControlClient.get_calibration_set_values` and
38
+ :meth:`StationControlClient.get_latest_calibration_set_id` methods. Those aren't REST API related
39
+ and needed only by Pulla for now, and they don't belong to :class:`StationControlInterface` since there is already
40
+ :meth:`StationControlInterface.query_observation_sets`,
41
+ :meth:`StationControlInterface.get_observation_set` and
42
+ :meth:`StationControlInterface.get_observation_set_observations`
43
+ which can be used for the same purpose. :class:`IQMServerClient` still has the original methods
44
+ and it should be later refactored to implement the same interface as :class:`StationControlClient`.
45
+
5
46
  Version 8.1.0 (2025-06-13)
6
47
  ==========================
7
48
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iqm-station-control-client
3
- Version: 8.1.0
3
+ Version: 9.1.0
4
4
  Summary: Python client for communicating with Station Control Service
5
5
  Author-email: IQM Finland Oy <info@meetiqm.com>
6
6
  License: Apache License
@@ -214,7 +214,7 @@ Requires-Python: >=3.11
214
214
  Description-Content-Type: text/x-rst
215
215
  License-File: LICENSE.txt
216
216
  Requires-Dist: iqm-exa-common<27,>=26
217
- Requires-Dist: iqm-data-definitions<3.0,>=2.8
217
+ Requires-Dist: iqm-data-definitions<3.0,>=2.13
218
218
  Requires-Dist: opentelemetry-exporter-otlp==1.25.0
219
219
  Requires-Dist: protobuf<5.0,>=4.25.3
220
220
  Requires-Dist: grpcio<2.0,>=1.65.4
@@ -1,4 +1,4 @@
1
- iqm-data-definitions >= 2.8, < 3.0
1
+ iqm-data-definitions >= 2.13, < 3.0
2
2
  opentelemetry-exporter-otlp == 1.25.0
3
3
  protobuf >= 4.25.3, < 5.0
4
4
  grpcio >= 1.65.4, < 2.0
@@ -1,5 +1,5 @@
1
1
  iqm-exa-common>=26,<27
2
- iqm-data-definitions >= 2.8, < 3.0
2
+ iqm-data-definitions >= 2.13, < 3.0
3
3
  opentelemetry-exporter-otlp == 1.25.0
4
4
  protobuf >= 4.25.3, < 5.0
5
5
  grpcio >= 1.65.4, < 2.0
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from exa.common.errors.server_errors import StationControlError
15
+ from exa.common.errors.station_control_errors import StationControlError
16
16
 
17
17
 
18
18
  class IqmServerError(StationControlError):
@@ -25,6 +25,7 @@ from pydantic import HttpUrl
25
25
 
26
26
  from iqm.station_control.client.iqm_server import proto
27
27
  from iqm.station_control.client.iqm_server.error import IqmServerError
28
+ from iqm.station_control.interface.models.type_aliases import StrUUID
28
29
 
29
30
 
30
31
  class ClientCallDetails(grpc.ClientCallDetails):
@@ -43,8 +44,7 @@ class ApiTokenAuth(grpc.UnaryUnaryClientInterceptor, grpc.UnaryStreamClientInter
43
44
 
44
45
  def _add_auth_header(self, client_call_details) -> ClientCallDetails:
45
46
  details = ClientCallDetails(client_call_details)
46
- token = self.get_token_callback()
47
- details.metadata.append(("authorization", f"Bearer {token}"))
47
+ details.metadata.append(("authorization", self.get_token_callback()))
48
48
  return details
49
49
 
50
50
  def intercept_unary_stream(self, continuation, client_call_details, request):
@@ -115,7 +115,9 @@ def create_channel(
115
115
  return channel
116
116
 
117
117
 
118
- def to_proto_uuid(value: uuid.UUID) -> proto.Uuid:
118
+ def to_proto_uuid(value: StrUUID) -> proto.Uuid:
119
+ if isinstance(value, str):
120
+ value = uuid.UUID(value)
119
121
  return proto.Uuid(raw=value.bytes)
120
122
 
121
123
 
@@ -11,9 +11,9 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- """StationControlClient implementation for IQM Server"""
14
+ """Client implementation for IQM Server."""
15
15
 
16
- from collections.abc import Callable, Iterable
16
+ from collections.abc import Callable, Iterable, Sequence
17
17
  from contextlib import contextmanager
18
18
  import dataclasses
19
19
  from io import BytesIO
@@ -22,9 +22,11 @@ import logging
22
22
  from time import sleep
23
23
  from typing import Any, TypeVar, cast
24
24
  import uuid
25
+ from uuid import UUID
25
26
 
26
27
  import grpc
27
28
  from iqm.models.channel_properties import ChannelProperties
29
+ import requests
28
30
 
29
31
  from exa.common.data.setting_node import SettingNode
30
32
  from exa.common.data.value import ObservationValue, validate_value
@@ -39,42 +41,64 @@ from iqm.station_control.client.iqm_server.grpc_utils import (
39
41
  to_datetime,
40
42
  to_proto_uuid,
41
43
  )
42
- from iqm.station_control.client.iqm_server.meta_class import IqmServerClientMeta
43
44
  from iqm.station_control.client.list_models import DutFieldDataList, DutList
44
45
  from iqm.station_control.client.serializers import deserialize_sweep_results, serialize_sweep_job_request
45
46
  from iqm.station_control.client.serializers.channel_property_serializer import unpack_channel_properties
46
47
  from iqm.station_control.client.serializers.setting_node_serializer import deserialize_setting_node
47
48
  from iqm.station_control.client.serializers.task_serializers import deserialize_sweep_job_request
48
- from iqm.station_control.client.station_control import StationControlClient
49
+ from iqm.station_control.client.station_control import _StationControlClientBase
50
+ from iqm.station_control.interface.list_with_meta import ListWithMeta
49
51
  from iqm.station_control.interface.models import (
50
52
  DutData,
51
53
  DutFieldData,
54
+ DynamicQuantumArchitecture,
55
+ JobData,
52
56
  JobExecutorStatus,
57
+ JobResult,
58
+ ObservationData,
59
+ ObservationDefinition,
60
+ ObservationLite,
61
+ ObservationSetData,
62
+ ObservationSetDefinition,
63
+ ObservationSetUpdate,
64
+ ObservationUpdate,
65
+ QualityMetrics,
66
+ RunData,
67
+ RunDefinition,
68
+ RunLite,
69
+ SequenceMetadataData,
70
+ SequenceMetadataDefinition,
71
+ SequenceResultData,
72
+ SequenceResultDefinition,
73
+ StaticQuantumArchitecture,
53
74
  Statuses,
54
75
  SweepData,
55
76
  SweepDefinition,
56
77
  SweepResults,
57
78
  )
58
- from iqm.station_control.interface.models.jobs import JobData, JobError, JobResult
79
+ from iqm.station_control.interface.models.jobs import JobError
59
80
  from iqm.station_control.interface.models.sweep import SweepBase
81
+ from iqm.station_control.interface.models.type_aliases import GetObservationsMode, SoftwareVersionSet, StrUUID
60
82
 
61
83
  logger = logging.getLogger(__name__)
62
84
 
63
85
  T = TypeVar("T")
64
86
 
65
87
 
66
- class IqmServerClient(StationControlClient, metaclass=IqmServerClientMeta):
88
+ class IqmServerClient(_StationControlClientBase):
67
89
  def __init__(
68
90
  self,
69
91
  root_url: str,
92
+ *,
70
93
  get_token_callback: Callable[[], str] | None = None,
94
+ client_signature: str | None = None,
71
95
  grpc_channel: grpc.Channel | None = None,
72
96
  ):
73
- self.root_url = root_url
97
+ super().__init__(root_url, get_token_callback=get_token_callback, client_signature=client_signature)
74
98
  self._connection_params = parse_connection_params(root_url)
75
99
  self._cached_resources = {}
76
100
  self._latest_submitted_sweep = None
77
- self._channel = grpc_channel or create_channel(self._connection_params, get_token_callback)
101
+ self._channel = grpc_channel or create_channel(self._connection_params, self._get_token_callback)
78
102
  self._current_qc = resolve_current_qc(self._channel, self._connection_params.quantum_computer)
79
103
 
80
104
  def __del__(self):
@@ -86,9 +110,18 @@ class IqmServerClient(StationControlClient, metaclass=IqmServerClientMeta):
86
110
  def get_about(self) -> dict:
87
111
  return self._get_resource("about", parse_json)
88
112
 
113
+ def get_health(self) -> dict:
114
+ raise NotImplementedError
115
+
89
116
  def get_configuration(self) -> dict:
90
117
  return self._get_resource("configuration", parse_json)
91
118
 
119
+ def get_exa_configuration(self) -> str:
120
+ raise NotImplementedError
121
+
122
+ def get_or_create_software_version_set(self, software_version_set: SoftwareVersionSet) -> int:
123
+ raise NotImplementedError
124
+
92
125
  def get_settings(self) -> SettingNode:
93
126
  return self._get_resource("settings", deserialize_setting_node).copy()
94
127
 
@@ -98,14 +131,6 @@ class IqmServerClient(StationControlClient, metaclass=IqmServerClientMeta):
98
131
  def get_channel_properties(self) -> dict[str, ChannelProperties]:
99
132
  return self._get_resource("channel-properties", unpack_channel_properties)
100
133
 
101
- def get_duts(self) -> list[DutData]:
102
- return self._get_resource("duts", lambda data: DutList.model_validate(parse_json(data)))
103
-
104
- def get_dut_fields(self, dut_label: str) -> list[DutFieldData]:
105
- return self._get_resource(
106
- f"dut-fields/{dut_label}", lambda data: DutFieldDataList.model_validate(parse_json(data))
107
- )
108
-
109
134
  def sweep(self, sweep_definition: SweepDefinition) -> dict:
110
135
  with wrap_error("Job submission failed"):
111
136
  jobs = proto.JobsStub(self._channel)
@@ -126,8 +151,11 @@ class IqmServerClient(StationControlClient, metaclass=IqmServerClientMeta):
126
151
  "job_id": str(job_id),
127
152
  }
128
153
 
129
- def get_sweep(self, sweep_id: uuid.UUID) -> SweepData:
154
+ def get_sweep(self, sweep_id: UUID) -> SweepData:
130
155
  with wrap_error("Job loading failed"):
156
+ if isinstance(sweep_id, str):
157
+ sweep_id = uuid.UUID(sweep_id)
158
+
131
159
  jobs = proto.JobsStub(self._channel)
132
160
  job_lookup = proto.JobLookupV1(id=to_proto_uuid(sweep_id))
133
161
  job: proto.JobV1 = jobs.GetJobV1(job_lookup)
@@ -146,18 +174,118 @@ class IqmServerClient(StationControlClient, metaclass=IqmServerClientMeta):
146
174
  **{f.name: getattr(sweep, f.name) for f in dataclasses.fields(SweepBase)},
147
175
  )
148
176
 
149
- def get_sweep_results(self, sweep_id: uuid.UUID) -> SweepResults:
177
+ def delete_sweep(self, sweep_id: UUID) -> None:
178
+ raise NotImplementedError
179
+
180
+ def get_sweep_results(self, sweep_id: UUID) -> SweepResults:
150
181
  with wrap_error("Job result loading failed"):
151
182
  jobs = proto.JobsStub(self._channel)
152
183
  data_chunks = jobs.GetJobResultsV1(proto.JobLookupV1(id=to_proto_uuid(sweep_id)))
153
184
  return deserialize_sweep_results(load_all(data_chunks))
154
185
 
155
- def abort_job(self, sweep_id: uuid.UUID) -> None:
156
- with wrap_error("Job cancellation failed"):
157
- jobs = proto.JobsStub(self._channel)
158
- jobs.CancelJobV1(proto.JobLookupV1(id=to_proto_uuid(sweep_id)))
186
+ def run(
187
+ self,
188
+ run_definition: RunDefinition,
189
+ update_progress_callback: Callable[[Statuses], None] | None = None,
190
+ wait_job_completion: bool = True,
191
+ ) -> bool:
192
+ raise NotImplementedError
193
+
194
+ def get_run(self, run_id: UUID) -> RunData:
195
+ raise NotImplementedError
196
+
197
+ def query_runs(self, **kwargs) -> ListWithMeta[RunLite]:
198
+ raise NotImplementedError
199
+
200
+ def create_observations(
201
+ self, observation_definitions: Sequence[ObservationDefinition]
202
+ ) -> ListWithMeta[ObservationData]:
203
+ raise NotImplementedError
204
+
205
+ def get_observations(
206
+ self,
207
+ *,
208
+ mode: GetObservationsMode,
209
+ dut_label: str | None = None,
210
+ dut_field: str | None = None,
211
+ tags: list[str] | None = None,
212
+ invalid: bool | None = False,
213
+ run_ids: list[UUID] | None = None,
214
+ sequence_ids: list[UUID] | None = None,
215
+ limit: int | None = None,
216
+ ) -> list[ObservationData]:
217
+ raise NotImplementedError
218
+
219
+ def query_observations(self, **kwargs) -> ListWithMeta[ObservationData]:
220
+ raise NotImplementedError
221
+
222
+ def update_observations(self, observation_updates: Sequence[ObservationUpdate]) -> list[ObservationData]:
223
+ raise NotImplementedError
224
+
225
+ def query_observation_sets(self, **kwargs) -> ListWithMeta[ObservationSetData]:
226
+ raise NotImplementedError
227
+
228
+ def create_observation_set(self, observation_set_definition: ObservationSetDefinition) -> ObservationSetData:
229
+ raise NotImplementedError
230
+
231
+ def get_observation_set(self, observation_set_id: UUID) -> ObservationSetData:
232
+ raise NotImplementedError
233
+
234
+ def update_observation_set(self, observation_set_update: ObservationSetUpdate) -> ObservationSetData:
235
+ raise NotImplementedError
236
+
237
+ def finalize_observation_set(self, observation_set_id: UUID) -> None:
238
+ raise NotImplementedError
239
+
240
+ def get_observation_set_observations(self, observation_set_id: UUID) -> list[ObservationLite]:
241
+ raise NotImplementedError
242
+
243
+ def get_default_calibration_set(self) -> ObservationSetData:
244
+ raise NotImplementedError
245
+
246
+ def get_default_calibration_set_observations(self) -> list[ObservationLite]:
247
+ raise NotImplementedError
248
+
249
+ def get_dynamic_quantum_architecture(self, calibration_set_id: UUID) -> DynamicQuantumArchitecture:
250
+ response = self._send_request(requests.get, f"api/v1/calibration/{calibration_set_id}/gates")
251
+ return DynamicQuantumArchitecture.model_validate_json(response.text)
252
+
253
+ def get_default_dynamic_quantum_architecture(self) -> DynamicQuantumArchitecture:
254
+ raise NotImplementedError
255
+
256
+ def get_default_calibration_set_quality_metrics(self) -> QualityMetrics:
257
+ raise NotImplementedError
258
+
259
+ def get_calibration_set_quality_metrics(self, calibration_set_id: UUID) -> QualityMetrics:
260
+ raise NotImplementedError
261
+
262
+ def get_duts(self) -> list[DutData]:
263
+ return self._get_resource("duts", lambda data: DutList.model_validate(parse_json(data)))
264
+
265
+ def get_dut_fields(self, dut_label: str) -> list[DutFieldData]:
266
+ return self._get_resource(
267
+ f"dut-fields/{dut_label}", lambda data: DutFieldDataList.model_validate(parse_json(data))
268
+ )
269
+
270
+ def query_sequence_metadatas(self, **kwargs) -> ListWithMeta[SequenceMetadataData]:
271
+ raise NotImplementedError
272
+
273
+ def create_sequence_metadata(
274
+ self, sequence_metadata_definition: SequenceMetadataDefinition
275
+ ) -> SequenceMetadataData:
276
+ raise NotImplementedError
277
+
278
+ def save_sequence_result(self, sequence_result_definition: SequenceResultDefinition) -> SequenceResultData:
279
+ raise NotImplementedError
280
+
281
+ def get_sequence_result(self, sequence_id: UUID) -> SequenceResultData:
282
+ raise NotImplementedError
159
283
 
160
- def get_job(self, job_id: uuid.UUID) -> JobData:
284
+ def get_static_quantum_architecture(self, dut_label: str) -> StaticQuantumArchitecture:
285
+ response = self._send_request(requests.get, "api/v1/quantum-architecture")
286
+ return StaticQuantumArchitecture.model_validate_json(response.text)
287
+
288
+ def get_job(self, job_id: StrUUID) -> JobData:
161
289
  with wrap_error("Job loading failed"):
162
290
  jobs = proto.JobsStub(self._channel)
163
291
  job: proto.JobV1 = jobs.GetJobV1(proto.JobLookupV1(id=to_proto_uuid(job_id)))
@@ -175,6 +303,34 @@ class IqmServerClient(StationControlClient, metaclass=IqmServerClientMeta):
175
303
  position=job.queue_position if job.HasField("queue_position") else None,
176
304
  )
177
305
 
306
+ def abort_job(self, job_id: StrUUID) -> None:
307
+ with wrap_error("Job cancellation failed"):
308
+ jobs = proto.JobsStub(self._channel)
309
+ jobs.CancelJobV1(proto.JobLookupV1(id=to_proto_uuid(job_id)))
310
+
311
+ def get_calibration_set_values(self, calibration_set_id: StrUUID) -> dict[str, ObservationValue]:
312
+ with wrap_error("Calibration set loading failed"):
313
+ calibrations = proto.CalibrationsStub(self._channel)
314
+ data_chunks = calibrations.GetFullCalibrationDataV1(
315
+ proto.CalibrationLookupV1(
316
+ id=to_proto_uuid(calibration_set_id),
317
+ )
318
+ )
319
+ _, cal_set_values = parse_calibration_set(load_all(data_chunks))
320
+ return cal_set_values
321
+
322
+ def get_latest_calibration_set_id(self, dut_label: str) -> uuid.UUID:
323
+ with wrap_error("Calibration set metadata loading failed"):
324
+ calibrations = proto.CalibrationsStub(self._channel)
325
+ metadata: proto.CalibrationMetadataV1 = calibrations.GetLatestQuantumComputerCalibrationV1(
326
+ proto.LatestQuantumComputerCalibrationLookupV1(
327
+ qc_id=self._current_qc.id,
328
+ )
329
+ )
330
+ if metadata.dut_label != dut_label:
331
+ raise ValueError(f"No calibration set for dut_label = {dut_label}")
332
+ return from_proto_uuid(metadata.id)
333
+
178
334
  def _wait_job_completion(
179
335
  self,
180
336
  task_id: str,
@@ -202,29 +358,6 @@ class IqmServerClient(StationControlClient, metaclass=IqmServerClientMeta):
202
358
  except KeyboardInterrupt:
203
359
  return True
204
360
 
205
- def get_calibration_set_values(self, calibration_set_id: uuid.UUID) -> dict[str, ObservationValue]:
206
- with wrap_error("Calibration set loading failed"):
207
- calibrations = proto.CalibrationsStub(self._channel)
208
- data_chunks = calibrations.GetFullCalibrationDataV1(
209
- proto.CalibrationLookupV1(
210
- id=to_proto_uuid(calibration_set_id),
211
- )
212
- )
213
- _, cal_set_values = parse_calibration_set(load_all(data_chunks))
214
- return cal_set_values
215
-
216
- def get_latest_calibration_set_id(self, dut_label: str) -> uuid.UUID:
217
- with wrap_error("Calibration set metadata loading failed"):
218
- calibrations = proto.CalibrationsStub(self._channel)
219
- metadata: proto.CalibrationMetadataV1 = calibrations.GetLatestQuantumComputerCalibrationV1(
220
- proto.LatestQuantumComputerCalibrationLookupV1(
221
- qc_id=self._current_qc.id,
222
- )
223
- )
224
- if metadata.dut_label != dut_label:
225
- raise ValueError(f"No calibration set for dut_label = {dut_label}")
226
- return from_proto_uuid(metadata.id)
227
-
228
361
  def _get_cached_sweep(self, sweep_id: uuid.UUID) -> SweepDefinition | None:
229
362
  latest_submitted = self._latest_submitted_sweep
230
363
  if latest_submitted and latest_submitted.sweep_id == sweep_id:
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -11,9 +11,12 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
- """Station control client type adapters."""
14
+ """Station control client list types for different models.
15
15
 
16
- from typing import Generic, TypeVar
16
+ These are used mainly for easy serialization and deserialization of list of objects.
17
+ """
18
+
19
+ from typing import Generic, TypeAlias, TypeVar
17
20
 
18
21
  from pydantic import ConfigDict, RootModel
19
22
 
@@ -53,17 +56,20 @@ class ListModel(RootModel):
53
56
  def __len__(self) -> int:
54
57
  return len(self.root)
55
58
 
59
+ def __str__(self) -> str:
60
+ return str(self.root)
61
+
56
62
  model_config = ConfigDict(
57
63
  ser_json_inf_nan="constants", # Will serialize Infinity and NaN values as Infinity and NaN
58
64
  )
59
65
 
60
66
 
61
- DutList = ListModel[list[DutData]] # type: ignore
62
- DutFieldDataList = ListModel[list[DutFieldData]] # type: ignore
63
- ObservationDataList = ListModel[list[ObservationData]] # type: ignore
64
- ObservationDefinitionList = ListModel[list[ObservationDefinition]] # type: ignore
65
- ObservationLiteList = ListModel[list[ObservationLite]] # type: ignore
66
- ObservationUpdateList = ListModel[list[ObservationUpdate]] # type: ignore
67
- ObservationSetDataList = ListModel[list[ObservationSetData]] # type: ignore
68
- SequenceMetadataDataList = ListModel[list[SequenceMetadataData]] # type: ignore
69
- RunLiteList = ListModel[list[RunLite]] # type: ignore
67
+ DutList: TypeAlias = ListModel[list[DutData]]
68
+ DutFieldDataList: TypeAlias = ListModel[list[DutFieldData]]
69
+ ObservationDataList: TypeAlias = ListModel[list[ObservationData]]
70
+ ObservationDefinitionList: TypeAlias = ListModel[list[ObservationDefinition]]
71
+ ObservationLiteList: TypeAlias = ListModel[list[ObservationLite]]
72
+ ObservationUpdateList: TypeAlias = ListModel[list[ObservationUpdate]]
73
+ ObservationSetDataList: TypeAlias = ListModel[list[ObservationSetData]]
74
+ SequenceMetadataDataList: TypeAlias = ListModel[list[SequenceMetadataData]]
75
+ RunLiteList: TypeAlias = ListModel[list[RunLite]]
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,10 +1,16 @@
1
- # ********************************************************************************
2
- # Copyright (c) 2019-2024 IQM Finland Oy.
3
- # All rights reserved. Confidential and proprietary.
1
+ # Copyright 2025 IQM
4
2
  #
5
- # Distribution or reproduction of any information contained herein
6
- # is prohibited without IQM Finland Oy’s prior written permission.
7
- # ********************************************************************************
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
8
14
  """Serializers and deserializers for :class:`~iqm.models.channel_properties.channel_properties.ChannelProperties`"""
9
15
 
10
16
  from collections.abc import Iterable
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -27,8 +27,7 @@ from exa.common.data.setting_node import SettingNode
27
27
  from exa.common.sweep.database_serialization import decode_and_validate_sweeps, encode_nd_sweeps
28
28
  from iqm.station_control.client.serializers.datetime_serializers import deserialize_datetime, serialize_datetime
29
29
  from iqm.station_control.client.serializers.playlist_serializers import pack_playlist, unpack_playlist
30
- from iqm.station_control.interface.models import SweepData, SweepDefinition, SweepResults
31
- from iqm.station_control.interface.models.jobs import JobExecutorStatus
30
+ from iqm.station_control.interface.models import JobExecutorStatus, SweepData, SweepDefinition, SweepResults
32
31
 
33
32
 
34
33
  def serialize_sweep_definition(sweep_definition: SweepDefinition) -> SweepDefinitionProto:
@@ -1,4 +1,4 @@
1
- # Copyright 2024 IQM
1
+ # Copyright 2025 IQM
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.