iqm-client 31.8.0__py3-none-any.whl → 32.1.1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: iqm-client
3
- Version: 31.8.0
3
+ Version: 32.1.1
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
@@ -19,7 +19,7 @@ iqm/cirq_iqm/examples/demo_iqm_execution.py,sha256=I9hh1DfveBky-ngJlLmuxucIzlGtR
19
19
  iqm/cirq_iqm/examples/usage.ipynb,sha256=Kyfyu_MwqzTavHVNjgrWJo1tZPeJwTw7ExcU0MFYNRk,34208
20
20
  iqm/iqm_client/__init__.py,sha256=D-8W54EcQIxk_1JZo_86GYlR1YitHhPIiFwwLJ2IfGE,1411
21
21
  iqm/iqm_client/api.py,sha256=_c6OVuv2dyzBF7J2XlK_qxisTSPyOiI4gYokZPsuaJY,3083
22
- iqm/iqm_client/authentication.py,sha256=4V-rYUZKFMvMOVsroiKB61voHKX_6t7S847NDCdNbQE,7582
22
+ iqm/iqm_client/authentication.py,sha256=VtndzZ-nxuaU7q2G8bXYctGThcTuFZW0PqMNE9k4OCA,7447
23
23
  iqm/iqm_client/errors.py,sha256=ty2P-sg80zlAoL3_kC3PlprgDUv4PI-KFhmmxaaapS0,1429
24
24
  iqm/iqm_client/iqm_client.py,sha256=oX_wu7aIJCdwc-DZy8iF43n4t7mphtp7ha5Ec38kOkM,41160
25
25
  iqm/iqm_client/models.py,sha256=0i0jYI8G_mJlNGCbH9W89OBS3JeqbcD2n_kUcM1AqIs,41942
@@ -27,28 +27,22 @@ iqm/iqm_client/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
27
  iqm/iqm_client/transpile.py,sha256=2hDxQK8F4eMgpJC06XMIA8YbguFnRVNMlE1iKQNiTZk,37268
28
28
  iqm/iqm_client/util.py,sha256=obzh1g6PNEXOj7k3gUkiylNUhyqutbWlxlEpfyyU_fk,1505
29
29
  iqm/iqm_client/validation.py,sha256=GAe5GQAEXyplbmS_ioIRLR4fhCYKSranB0vE06qMfe8,12192
30
- iqm/iqm_client/cli/__init__.py,sha256=zzLDDz5rc3lJke3OKU8zxR5zQyQoM9oI2bLJ2YKk_zQ,692
31
- iqm/iqm_client/cli/auth.py,sha256=kESEK9-vpEhrjba3Lb6Wqx24yGfbjxUASeCArnVRYrw,6364
32
- iqm/iqm_client/cli/cli.py,sha256=vdhRJPKbqKRL0D_Z0uc3V73jQKKftAKE5Hx44oOCBwA,28689
33
- iqm/iqm_client/cli/models.py,sha256=Hu-t6c_07Cth3AuQBo0CDTcWVQg1xbJCpy_94V0o64U,1199
34
- iqm/iqm_client/cli/token_manager.py,sha256=125uRj8kBzKlWAhQWNf-8n-aDG6fQridVd95qCktzD4,6867
35
30
  iqm/qiskit_iqm/__init__.py,sha256=Mv9V_r8ZcmGC8Ke5S8-7yLOx02vjZ1qiVx8mtbOpwnY,1420
36
31
  iqm/qiskit_iqm/iqm_backend.py,sha256=HddizT6yHHq-MOG_U48n6ftE9AqmzaqbXYayEC1ljso,5548
37
32
  iqm/qiskit_iqm/iqm_circuit.py,sha256=jaPo3zc5FC0vAIumh5d56fr44fDaJXXwcquBzQEy1Yg,1400
38
33
  iqm/qiskit_iqm/iqm_circuit_validation.py,sha256=9pneZKs-KjBDGeDI6RHj6lB-ACqugbnYr1BqkJwLcXg,1737
39
34
  iqm/qiskit_iqm/iqm_job.py,sha256=YLkAF4oqQ44dOLWbsqMiYCyj7pd3Rk6NIZ_TI1XtcQ8,11700
40
35
  iqm/qiskit_iqm/iqm_move_layout.py,sha256=ECf1BcRmXKeClc7AL0lHedvJbqtwV5rEHcOOFR8shKU,10534
41
- iqm/qiskit_iqm/iqm_naive_move_pass.py,sha256=jhTfvhrNDKt6NhhJg_3Y-5x6E1HRNzC_n4A27ZQTuvQ,12962
36
+ iqm/qiskit_iqm/iqm_naive_move_pass.py,sha256=3wuZMGIFJ1UZnfvsmrBAhA_W3cYWdQ8xKZiaJZbk4x0,12987
42
37
  iqm/qiskit_iqm/iqm_provider.py,sha256=KHF27Qk12-_OA7_UZ8Rd25qDfFq4np2jv11_9f4tchQ,18599
43
- iqm/qiskit_iqm/iqm_target.py,sha256=UyULiGMn6UJsyILBQiriso9KbhlmzP9TZItS2URaXWg,15832
44
- iqm/qiskit_iqm/iqm_transpilation.py,sha256=6_6Mri01_HQBV_GTX94WSvIbu-pDMLMzEU6zVMEt6Gc,9153
38
+ iqm/qiskit_iqm/iqm_target.py,sha256=NHpfCmkE090eblH3asNKUtD361kGfX6ZeXzUb8iMku4,16098
39
+ iqm/qiskit_iqm/iqm_transpilation.py,sha256=RccAHY7lNXvg7TWKG7fBNBCgFu5Xd9B_J9V9uKLlmuY,18336
45
40
  iqm/qiskit_iqm/move_gate.py,sha256=UbrQSfrpVV3QKGJ93TelxEfZkl1wY4uWL8IH_QDpGUw,2840
46
41
  iqm/qiskit_iqm/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
- iqm/qiskit_iqm/qiskit_to_iqm.py,sha256=3Sm1gLELFLCLf6QiATZSiPzLzkjvzUakhU2N6KoBXC8,14948
48
- iqm/qiskit_iqm/transpiler_plugins.py,sha256=iuReGL42fCe5aOoH-KMUsb6t7Ok9qmIIj2S4yotJJ-U,8749
42
+ iqm/qiskit_iqm/qiskit_to_iqm.py,sha256=-CWYTxNTeBNXlgllStwyjNkCor5FjkM_qfjP37L6z8Y,18872
43
+ iqm/qiskit_iqm/transpiler_plugins.py,sha256=BrCkUFQUdi0m8K8V0ERYgtPAkvoX4VlYIJwnFKyCN9Q,8831
49
44
  iqm/qiskit_iqm/examples/__init__.py,sha256=M4ElQHCo-WxtVXK39bF3QiFT3IGXPtZ1khqexHiTBEc,20
50
- iqm/qiskit_iqm/examples/bell_measure.py,sha256=9ZuaAZybnEL9KhVom5P-mClQscyEF6k2dn6wV1QJaBQ,1925
51
- iqm/qiskit_iqm/examples/resonance_example.py,sha256=1byRYxoFRtQcOpWtTzGfCYVyOlwtJvdpFrGo_kMR8ng,2954
45
+ iqm/qiskit_iqm/examples/bell_measure.py,sha256=vlayliApHU70a2zTnN_gs6iKSSdaB4Jip-iQHJNYN5M,3064
52
46
  iqm/qiskit_iqm/examples/transpile_example.py,sha256=cQmXXx3lqvmhgWYFK_U5HmHzMOKQqXUXPYfQhkyHH14,2135
53
47
  iqm/qiskit_iqm/fake_backends/__init__.py,sha256=fkw2UHT-3aJbAKvR1WYUN7_4N5Gdwpx9bm6vlWj1tm0,874
54
48
  iqm/qiskit_iqm/fake_backends/fake_adonis.py,sha256=vdWaVB6Hsa-SYgMWuYBtRQH5x5Y1qVHvdomWT2bh5QQ,2225
@@ -57,10 +51,10 @@ iqm/qiskit_iqm/fake_backends/fake_apollo.py,sha256=6YSaKtUc6zZ4m3iAREbmxL4tRs_DW
57
51
  iqm/qiskit_iqm/fake_backends/fake_deneb.py,sha256=_xpo_NJdu9t4ER8p5be-xTRmApkFY6kQw1UEGUPizk0,3193
58
52
  iqm/qiskit_iqm/fake_backends/fake_garnet.py,sha256=GI0xafTCj1Um09qVuccO6GPOGBm6ygul_O40Wu220Ys,5555
59
53
  iqm/qiskit_iqm/fake_backends/iqm_fake_backend.py,sha256=wJtfsxjPYbDKmzaz5R4AuaXvvPHa21WyPtRgNctL9eY,16785
60
- iqm_client-31.8.0.dist-info/AUTHORS.rst,sha256=qsxeK5A3-B_xK3hNbhFHEIkoHNpo7sdzYyRTs7Bdtm8,795
61
- iqm_client-31.8.0.dist-info/LICENSE.txt,sha256=2DXrmQtVVUV9Fc9RBFJidMiTEaQlG2oAtlC9PMrEwTk,11333
62
- iqm_client-31.8.0.dist-info/METADATA,sha256=RBkzHY_qwa7JBgDrySzr5yyH0-oGaOCi65XBbTvZ6z4,17887
63
- iqm_client-31.8.0.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
64
- iqm_client-31.8.0.dist-info/entry_points.txt,sha256=Kk2qfRwk8vbIJ7qCAvmaUogfRRn6t92_hBFhe6kqAE4,1317
65
- iqm_client-31.8.0.dist-info/top_level.txt,sha256=NB4XRfyDS6_wG9gMsyX-9LTU7kWnTQxNvkbzIxGv3-c,4
66
- iqm_client-31.8.0.dist-info/RECORD,,
54
+ iqm_client-32.1.1.dist-info/AUTHORS.rst,sha256=qsxeK5A3-B_xK3hNbhFHEIkoHNpo7sdzYyRTs7Bdtm8,795
55
+ iqm_client-32.1.1.dist-info/LICENSE.txt,sha256=2DXrmQtVVUV9Fc9RBFJidMiTEaQlG2oAtlC9PMrEwTk,11333
56
+ iqm_client-32.1.1.dist-info/METADATA,sha256=KUS_M5HU81o7THuT1V1TG2-vtRiXCn2_S8umUJaXA04,17887
57
+ iqm_client-32.1.1.dist-info/WHEEL,sha256=y4mX-SOX4fYIkonsAGA5N0Oy-8_gI4FXw5HNI1xqvWg,91
58
+ iqm_client-32.1.1.dist-info/entry_points.txt,sha256=Kk2qfRwk8vbIJ7qCAvmaUogfRRn6t92_hBFhe6kqAE4,1317
59
+ iqm_client-32.1.1.dist-info/top_level.txt,sha256=NB4XRfyDS6_wG9gMsyX-9LTU7kWnTQxNvkbzIxGv3-c,4
60
+ iqm_client-32.1.1.dist-info/RECORD,,
@@ -1,14 +0,0 @@
1
- # Copyright 2021-2023 IQM client developers
2
- #
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.
14
- """Command-line interface (CLI) for managing user authentication when using IQM quantum computers."""
@@ -1,168 +0,0 @@
1
- # Copyright 2021-2023 IQM client developers
2
- #
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.
14
- """Authorization and session management for IQM Client CLI."""
15
-
16
- from base64 import b64decode
17
- from enum import Enum
18
- import json
19
- import time
20
-
21
- from pydantic import BaseModel, Field
22
- import requests
23
-
24
- REFRESH_MARGIN_SECONDS = 15
25
- AUTH_REQUESTS_TIMEOUT = 20
26
-
27
-
28
- class ClientAuthenticationError(RuntimeError):
29
- """Something went wrong with user authentication."""
30
-
31
-
32
- class ClientAccountSetupError(RuntimeError):
33
- """User's account has not been fully set up yet."""
34
-
35
-
36
- class GrantType(str, Enum):
37
- """Type of token request."""
38
-
39
- PASSWORD = "password"
40
- REFRESH = "refresh_token"
41
-
42
-
43
- class AuthRequest(BaseModel):
44
- """Request sent to authentication server for access token and refresh token, or for terminating the session.
45
-
46
- * Token request with grant type ``'password'`` starts a new session in the authentication server.
47
- It uses fields ``client_id``, ``grant_type``, ``username`` and ``password``.
48
- * Token request with grant type ``'refresh_token'`` is used for maintaining an existing session.
49
- It uses field ``client_id``, ``grant_type``, ``refresh_token``.
50
- * Logout request uses only fields ``client_id`` and ``refresh_token``.
51
- """
52
-
53
- client_id: str = Field(...)
54
- "name of the client for all request types"
55
- grant_type: GrantType | None = Field(None)
56
- "type of token request, in ``{'password', 'refresh_token'}``"
57
- username: str | None = Field(None)
58
- "username for grant type ``'password'``"
59
- password: str | None = Field(None)
60
- "password for grant type ``'password'``"
61
- refresh_token: str | None = Field(None)
62
- "refresh token for grant type ``'refresh_token'`` and logout request"
63
-
64
-
65
- def slash_join(a: str, b: str) -> str:
66
- """Join two URL segments together, ensuring a single slash between them."""
67
- return a.rstrip("/") + "/" + b.lstrip("/")
68
-
69
-
70
- def login_request(url: str, realm: str, client_id: str, username: str, password: str) -> dict[str, str]:
71
- """Sends login request to the authentication server.
72
-
73
- Raises:
74
- ClientAuthenticationError: obtaining the tokens failed
75
-
76
- Returns:
77
- Tokens dictionary
78
-
79
- """
80
- data = AuthRequest(
81
- client_id=client_id, grant_type=GrantType.PASSWORD, username=username, password=password, refresh_token=None
82
- )
83
-
84
- request_url = slash_join(url, f"realms/{realm}/protocol/openid-connect/token")
85
- result = requests.post(request_url, data=data.model_dump(exclude_none=True), timeout=AUTH_REQUESTS_TIMEOUT)
86
- if result.status_code == 404:
87
- raise ClientAuthenticationError(f"token endpoint is not available at {url}")
88
- if result.status_code == 400 and result.json().get("error_description", "") == "Account is not fully set up":
89
- raise ClientAccountSetupError("Account is not fully set up")
90
- if result.status_code != 200:
91
- raise ClientAuthenticationError("invalid username and/or password")
92
- tokens = result.json()
93
- tokens = {key: tokens.get(key, "") for key in ["access_token", "refresh_token"]}
94
- return tokens
95
-
96
-
97
- def refresh_request(url: str, realm: str, client_id: str, refresh_token: str) -> dict[str, str] | None:
98
- """Sends refresh request to the authentication server.
99
-
100
- Raises:
101
- Timeout: no response from auth server within the timeout period
102
- ConnectionError: connecting the auth server failed on all retries
103
- ClientAuthenticationError: updating the tokens failed
104
-
105
- Returns:
106
- Tokens dictionary, or None if refresh_token is expired.
107
-
108
- """
109
- if not token_is_valid(refresh_token):
110
- raise ClientAuthenticationError("Refresh token has expired")
111
-
112
- # Update tokens using existing refresh_token
113
- data = AuthRequest(
114
- client_id=client_id, grant_type=GrantType.REFRESH, username=None, password=None, refresh_token=refresh_token
115
- )
116
-
117
- request_url = slash_join(url, f"realms/{realm}/protocol/openid-connect/token")
118
- result = requests.post(request_url, data=data.model_dump(exclude_none=True), timeout=AUTH_REQUESTS_TIMEOUT)
119
- if result.status_code != 200:
120
- raise ClientAuthenticationError(f"Failed to update tokens, {result.text}")
121
- tokens = result.json()
122
- if not tokens or "access_token" not in tokens or "refresh_token" not in tokens:
123
- raise ClientAuthenticationError("Failed to get new tokens")
124
- tokens = {key: tokens.get(key, "") for key in ["access_token", "refresh_token"]}
125
- return tokens
126
-
127
-
128
- def logout_request(url: str, realm: str, client_id: str, refresh_token: str) -> bool:
129
- """Sends logout request to the authentication server.
130
-
131
- Raises:
132
- ClientAuthenticationError: updating the tokens failed
133
-
134
- Returns:
135
- True if logout was successful
136
-
137
- """
138
- data = AuthRequest(client_id=client_id, grant_type=None, username=None, password=None, refresh_token=refresh_token)
139
- request_url = slash_join(url, f"realms/{realm}/protocol/openid-connect/logout")
140
- result = requests.post(request_url, data=data.model_dump(exclude_none=True), timeout=AUTH_REQUESTS_TIMEOUT)
141
-
142
- if result.status_code != 204:
143
- raise ClientAuthenticationError(f"Failed to logout, {result.text}")
144
- return True
145
-
146
-
147
- def time_left_seconds(token: str) -> int:
148
- """Check how much time is left until the token expires.
149
-
150
- Returns:
151
- Time left on token in seconds.
152
-
153
- """
154
- _, body, _ = token.split(".", 2)
155
- # Add padding to adjust body length to a multiple of 4 chars as required by base64 decoding
156
- body += "=" * (-len(body) % 4)
157
- exp_time = int(json.loads(b64decode(body)).get("exp", "0"))
158
- return max(0, exp_time - int(time.time()))
159
-
160
-
161
- def token_is_valid(refresh_token: str) -> bool:
162
- """Check if token is not about to expire.
163
-
164
- Returns:
165
- True if token is still valid, False otherwise.
166
-
167
- """
168
- return time_left_seconds(refresh_token) > REFRESH_MARGIN_SECONDS