iqm-client 25.1.0__py3-none-any.whl → 25.3.0__py3-none-any.whl

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.
@@ -170,6 +170,7 @@ class IQMClient:
170
170
  *,
171
171
  timeout: float,
172
172
  retry: bool = False,
173
+ allow_errors: bool = False,
173
174
  ) -> requests.Response:
174
175
  """Make a HTTP GET request to an IQM server endpoint.
175
176
 
@@ -180,6 +181,7 @@ class IQMClient:
180
181
  endpoint_args: Arguments for the endpoint.
181
182
  timeout: HTTP request timeout (in seconds).
182
183
  retry: Iff True, keep trying if you get a 502 error.
184
+ allow_errors: Iff true, don't raise exceptions for error responses.
183
185
 
184
186
  Returns:
185
187
  HTTP response to the request.
@@ -199,9 +201,10 @@ class IQMClient:
199
201
  )
200
202
 
201
203
  response = self._retry_request_on_error(request) if retry else request()
202
- self._check_not_found_error(response)
203
- self._check_authentication_errors(response)
204
- response.raise_for_status()
204
+ if not allow_errors:
205
+ self._check_not_found_error(response)
206
+ self._check_authentication_errors(response)
207
+ response.raise_for_status()
205
208
  return response
206
209
 
207
210
  def _deserialize_response(
@@ -354,7 +357,10 @@ class IQMClient:
354
357
  ID for the created job. This ID is needed to query the job status and the execution results.
355
358
 
356
359
  """
357
- headers = {"Expect": "100-Continue", **self._default_headers()}
360
+ headers = {
361
+ "Expect": "100-Continue",
362
+ **self._default_headers(),
363
+ }
358
364
  try:
359
365
  # check if someone is trying to profile us with OpenTelemetry
360
366
  from opentelemetry import propagate
@@ -367,11 +373,12 @@ class IQMClient:
367
373
  if os.environ.get("IQM_CLIENT_DEBUG") == "1":
368
374
  print(f"\nIQM CLIENT DEBUGGING ENABLED\nSUBMITTING RUN REQUEST:\n{run_request}\n")
369
375
 
376
+ # use UTF-8 encoding for the JSON payload
370
377
  result = self._retry_request_on_error(
371
378
  lambda: requests.post(
372
379
  self._api.url(APIEndpoint.SUBMIT_JOB),
373
- json=json.loads(run_request.model_dump_json(exclude_none=True)),
374
- headers=headers,
380
+ data=run_request.model_dump_json(exclude_none=True).encode("utf-8"),
381
+ headers=headers | {"Content-Type": "application/json; charset=UTF-8"},
375
382
  timeout=REQUESTS_TIMEOUT,
376
383
  )
377
384
  )
@@ -668,91 +675,60 @@ class IQMClient:
668
675
  )
669
676
  status = status_response.json()
670
677
  if Status(status["status"]) not in Status.terminal_statuses():
671
- return RunResult.from_dict(
672
- {
673
- "measurements": [],
674
- "status": status["status"],
675
- "message": "",
676
- "metadata": {
677
- "calibration_set_id": None,
678
- "circuits_batch": [],
679
- "parameters": None,
680
- "timestamps": {},
681
- },
682
- }
683
- )
678
+ return RunResult.from_dict({"status": status["status"], "metadata": {}})
684
679
 
685
- result = self._retry_request_on_error(
686
- lambda: requests.get(
687
- self._api.url(APIEndpoint.GET_JOB_RESULT, str(job_id)),
688
- headers=self._default_headers(),
689
- timeout=timeout_secs,
690
- )
691
- )
692
- if result.status_code != 404:
693
- result.raise_for_status()
694
- measurements = [] if result.status_code == 404 else result.json()
695
- request_parameters = (
696
- {}
697
- if result.status_code == 404
698
- else requests.get(
699
- self._api.url(APIEndpoint.GET_JOB_REQUEST_PARAMETERS, str(job_id)),
700
- headers=self._default_headers(),
701
- timeout=timeout_secs,
702
- ).json()
703
- )
704
- calibration_set_id = (
705
- None
706
- if result.status_code == 404
707
- else requests.get(
708
- self._api.url(APIEndpoint.GET_JOB_CALIBRATION_SET_ID, str(job_id)),
709
- headers=self._default_headers(),
710
- timeout=timeout_secs,
711
- ).json()
712
- )
713
- circuits_batch = (
714
- []
715
- if result.status_code == 404
716
- else requests.get(
717
- self._api.url(APIEndpoint.GET_JOB_CIRCUITS_BATCH, str(job_id)),
718
- headers=self._default_headers(),
719
- timeout=timeout_secs,
720
- ).json()
680
+ result = self._get_request(
681
+ APIEndpoint.GET_JOB_RESULT, (str(job_id),), timeout=timeout_secs, retry=True, allow_errors=True
721
682
  )
722
- timeline = (
723
- []
724
- if result.status_code == 404
725
- else requests.get(
726
- self._api.url(APIEndpoint.GET_JOB_TIMELINE, str(job_id)),
727
- headers=self._default_headers(),
728
- timeout=timeout_secs,
729
- ).json()
730
- )
731
- error_message_response = requests.get(
732
- self._api.url(APIEndpoint.GET_JOB_ERROR_LOG, str(job_id)),
733
- headers=self._default_headers(),
734
- timeout=timeout_secs,
683
+
684
+ error_log_response = self._get_request(
685
+ APIEndpoint.GET_JOB_ERROR_LOG, (str(job_id),), timeout=timeout_secs, allow_errors=True
735
686
  )
736
- error_message = error_message_response.text if error_message_response.status_code == 200 else None
687
+ if error_log_response.status_code == 200:
688
+ error_log = error_log_response.json()
689
+ if isinstance(error_log, dict) and "user_error_message" in error_log:
690
+ error_message = error_log["user_error_message"]
691
+ else:
692
+ # backwards compatibility for older error_log format
693
+ # TODO: remove when not needed anymore
694
+ error_message = error_log_response.text
695
+ else:
696
+ error_message = None
697
+
698
+ if result.status_code == 404:
699
+ return RunResult.from_dict({"status": status["status"], "message": error_message, "metadata": {}})
700
+ else:
701
+ result.raise_for_status()
702
+
703
+ measurements = result.json()
704
+ request_parameters = self._get_request(
705
+ APIEndpoint.GET_JOB_REQUEST_PARAMETERS, (str(job_id),), timeout=timeout_secs, allow_errors=True
706
+ ).json()
707
+ calibration_set_id = self._get_request(
708
+ APIEndpoint.GET_JOB_CALIBRATION_SET_ID, (str(job_id),), timeout=timeout_secs, allow_errors=True
709
+ ).json()
710
+ circuits_batch = self._get_request(
711
+ APIEndpoint.GET_JOB_CIRCUITS_BATCH, (str(job_id),), timeout=timeout_secs, allow_errors=True
712
+ ).json()
713
+ timeline = self._get_request(
714
+ APIEndpoint.GET_JOB_TIMELINE, (str(job_id),), timeout=timeout_secs, allow_errors=True
715
+ ).json()
716
+
737
717
  return RunResult.from_dict(
738
718
  {
739
719
  "measurements": measurements,
740
720
  "status": status["status"],
741
721
  "message": error_message,
742
722
  "metadata": {
743
- "`": calibration_set_id,
723
+ "calibration_set_id": calibration_set_id,
744
724
  "circuits_batch": circuits_batch,
745
- "parameters": (
746
- None
747
- if result.status_code == 404
748
- else {
749
- "shots": request_parameters["shots"],
750
- "max_circuit_duration_over_t2": request_parameters["max_circuit_duration_over_t2"],
751
- "heralding_mode": request_parameters["heralding_mode"],
752
- "move_validation_mode": request_parameters["move_validation_mode"],
753
- "move_gate_frame_tracking_mode": request_parameters["move_gate_frame_tracking_mode"],
754
- }
755
- ),
725
+ "parameters": {
726
+ "shots": request_parameters["shots"],
727
+ "max_circuit_duration_over_t2": request_parameters["max_circuit_duration_over_t2"],
728
+ "heralding_mode": request_parameters["heralding_mode"],
729
+ "move_validation_mode": request_parameters["move_validation_mode"],
730
+ "move_gate_frame_tracking_mode": request_parameters["move_gate_frame_tracking_mode"],
731
+ },
756
732
  "timestamps": {datapoint["status"]: datapoint["timestamp"] for datapoint in timeline},
757
733
  },
758
734
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iqm-client
3
- Version: 25.1.0
3
+ Version: 25.3.0
4
4
  Summary: Client library for accessing an IQM quantum computer
5
5
  Author-email: IQM Finland Oy <developers@meetiqm.com>
6
6
  License: Apache License
@@ -21,7 +21,7 @@ iqm/iqm_client/__init__.py,sha256=D-8W54EcQIxk_1JZo_86GYlR1YitHhPIiFwwLJ2IfGE,14
21
21
  iqm/iqm_client/api.py,sha256=V57vslYSn5g1IgXWtWuxp3hD1DbY18dKUexRdxEuX78,8268
22
22
  iqm/iqm_client/authentication.py,sha256=Zbc5DpTwrcwNePKyZ_7KAFxwQFSVyZelQR_CWRCmlME,12187
23
23
  iqm/iqm_client/errors.py,sha256=ty2P-sg80zlAoL3_kC3PlprgDUv4PI-KFhmmxaaapS0,1429
24
- iqm/iqm_client/iqm_client.py,sha256=WEJkQqyOpuZKNQ-FPD3sPsvVfrpgkR8Vp9Kpw89gw-E,50504
24
+ iqm/iqm_client/iqm_client.py,sha256=SFImZk5DrAq51hr2G5sk242mO-8LYz-Mlc6Rc_JIuZE,50019
25
25
  iqm/iqm_client/models.py,sha256=YjlerNleeE1kOmzRsux-o02h7XSqv0u0KQEERufdxKk,50302
26
26
  iqm/iqm_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  iqm/iqm_client/transpile.py,sha256=-iIZaxaTaQLy6pMkRG15VN8X1ZE_iNhX3pjsNjqg6P8,36935
@@ -56,10 +56,10 @@ iqm/qiskit_iqm/fake_backends/fake_apollo.py,sha256=eT2vd3kQBi1rrvxCpePymBCfFK84d
56
56
  iqm/qiskit_iqm/fake_backends/fake_deneb.py,sha256=RzQXmLXmBARDiMKVxk5Aw9fVbc6IYlW0A5jibk9iYD0,3156
57
57
  iqm/qiskit_iqm/fake_backends/fake_garnet.py,sha256=GI0xafTCj1Um09qVuccO6GPOGBm6ygul_O40Wu220Ys,5555
58
58
  iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py,sha256=wJtfsxjPYbDKmzaz5R4AuaXvvPHa21WyPtRgNctL9eY,16785
59
- iqm_client-25.1.0.dist-info/AUTHORS.rst,sha256=qsxeK5A3-B_xK3hNbhFHEIkoHNpo7sdzYyRTs7Bdtm8,795
60
- iqm_client-25.1.0.dist-info/LICENSE.txt,sha256=2DXrmQtVVUV9Fc9RBFJidMiTEaQlG2oAtlC9PMrEwTk,11333
61
- iqm_client-25.1.0.dist-info/METADATA,sha256=ngNbRgXiX4Yk3OYXN2MWdYX6cUhx8j_pGGhli-_rLns,17079
62
- iqm_client-25.1.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
63
- iqm_client-25.1.0.dist-info/entry_points.txt,sha256=Kk2qfRwk8vbIJ7qCAvmaUogfRRn6t92_hBFhe6kqAE4,1317
64
- iqm_client-25.1.0.dist-info/top_level.txt,sha256=NB4XRfyDS6_wG9gMsyX-9LTU7kWnTQxNvkbzIxGv3-c,4
65
- iqm_client-25.1.0.dist-info/RECORD,,
59
+ iqm_client-25.3.0.dist-info/AUTHORS.rst,sha256=qsxeK5A3-B_xK3hNbhFHEIkoHNpo7sdzYyRTs7Bdtm8,795
60
+ iqm_client-25.3.0.dist-info/LICENSE.txt,sha256=2DXrmQtVVUV9Fc9RBFJidMiTEaQlG2oAtlC9PMrEwTk,11333
61
+ iqm_client-25.3.0.dist-info/METADATA,sha256=aId3j1veUFCJiceuZYn0veT8kjgr9EQdXR_bAOZVpi8,17079
62
+ iqm_client-25.3.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
63
+ iqm_client-25.3.0.dist-info/entry_points.txt,sha256=Kk2qfRwk8vbIJ7qCAvmaUogfRRn6t92_hBFhe6kqAE4,1317
64
+ iqm_client-25.3.0.dist-info/top_level.txt,sha256=NB4XRfyDS6_wG9gMsyX-9LTU7kWnTQxNvkbzIxGv3-c,4
65
+ iqm_client-25.3.0.dist-info/RECORD,,