azure-quantum 3.3.0__py3-none-any.whl → 3.4.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.
- azure/quantum/_client/_version.py +1 -1
- azure/quantum/_workspace_connection_params.py +3 -6
- azure/quantum/cirq/targets/ionq.py +1 -1
- azure/quantum/cirq/targets/quantinuum.py +1 -1
- azure/quantum/qiskit/backends/ionq.py +4 -4
- azure/quantum/qiskit/backends/quantinuum.py +0 -8
- azure/quantum/target/ionq.py +3 -34
- azure/quantum/target/quantinuum.py +2 -16
- azure/quantum/target/target.py +3 -32
- azure/quantum/target/target_factory.py +1 -5
- azure/quantum/version.py +1 -1
- {azure_quantum-3.3.0.dist-info → azure_quantum-3.4.0.dist-info}/METADATA +13 -20
- {azure_quantum-3.3.0.dist-info → azure_quantum-3.4.0.dist-info}/RECORD +15 -19
- azure/quantum/_authentication/__init__.py +0 -9
- azure/quantum/_authentication/_chained.py +0 -119
- azure/quantum/_authentication/_default.py +0 -153
- azure/quantum/_authentication/_token.py +0 -83
- {azure_quantum-3.3.0.dist-info → azure_quantum-3.4.0.dist-info}/WHEEL +0 -0
- {azure_quantum-3.3.0.dist-info → azure_quantum-3.4.0.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,7 @@ from typing import (
|
|
|
14
14
|
)
|
|
15
15
|
from azure.core.credentials import AzureKeyCredential
|
|
16
16
|
from azure.core.pipeline.policies import AzureKeyCredentialPolicy
|
|
17
|
-
from azure.
|
|
17
|
+
from azure.identity import DefaultAzureCredential
|
|
18
18
|
from azure.quantum._constants import (
|
|
19
19
|
EnvironmentKind,
|
|
20
20
|
EnvironmentVariables,
|
|
@@ -403,13 +403,10 @@ class WorkspaceConnectionParams:
|
|
|
403
403
|
def get_credential_or_default(self) -> Any:
|
|
404
404
|
"""
|
|
405
405
|
Get the credential if one was set,
|
|
406
|
-
or defaults to a new
|
|
406
|
+
or defaults to a new DefaultAzureCredential.
|
|
407
407
|
"""
|
|
408
408
|
return (self.credential
|
|
409
|
-
or
|
|
410
|
-
subscription_id=self.subscription_id,
|
|
411
|
-
arm_endpoint=self.arm_endpoint,
|
|
412
|
-
tenant_id=self.tenant_id))
|
|
409
|
+
or DefaultAzureCredential(exclude_interactive_browser_credential=False))
|
|
413
410
|
|
|
414
411
|
def get_auth_policy(self) -> Any:
|
|
415
412
|
"""
|
|
@@ -131,7 +131,7 @@ class IonQSimulatorQirBackend(IonQQirBackendBase):
|
|
|
131
131
|
|
|
132
132
|
|
|
133
133
|
class IonQAriaQirBackend(IonQQirBackendBase):
|
|
134
|
-
backend_names = ("ionq.qpu.aria-1",
|
|
134
|
+
backend_names = ("ionq.qpu.aria-1",)
|
|
135
135
|
|
|
136
136
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
137
137
|
"""Base class for interfacing with an IonQ Aria QPU backend"""
|
|
@@ -162,7 +162,7 @@ class IonQAriaQirBackend(IonQQirBackendBase):
|
|
|
162
162
|
|
|
163
163
|
|
|
164
164
|
class IonQForteQirBackend(IonQQirBackendBase):
|
|
165
|
-
backend_names = ("ionq.qpu.forte-1",)
|
|
165
|
+
backend_names = ("ionq.qpu.forte-1","ionq.qpu.forte-enterprise-1",)
|
|
166
166
|
|
|
167
167
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
168
168
|
"""Base class for interfacing with an IonQ Forte QPU backend"""
|
|
@@ -305,7 +305,7 @@ class IonQSimulatorNativeBackend(IonQSimulatorBackend):
|
|
|
305
305
|
|
|
306
306
|
|
|
307
307
|
class IonQAriaBackend(IonQBackend):
|
|
308
|
-
backend_names = ("ionq.qpu.aria-1",
|
|
308
|
+
backend_names = ("ionq.qpu.aria-1",)
|
|
309
309
|
|
|
310
310
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
311
311
|
"""Base class for interfacing with an IonQ Aria QPU backend"""
|
|
@@ -338,7 +338,7 @@ class IonQAriaBackend(IonQBackend):
|
|
|
338
338
|
|
|
339
339
|
|
|
340
340
|
class IonQForteBackend(IonQBackend):
|
|
341
|
-
backend_names = ("ionq.qpu.forte-1",)
|
|
341
|
+
backend_names = ("ionq.qpu.forte-1","ionq.qpu.forte-enterprise-1",)
|
|
342
342
|
|
|
343
343
|
def __init__(self, name: str, provider: "AzureQuantumProvider", **kwargs):
|
|
344
344
|
"""Base class for interfacing with an IonQ Forte QPU backend"""
|
|
@@ -57,8 +57,6 @@ QUANTINUUM_PROVIDER_NAME = "Quantinuum"
|
|
|
57
57
|
|
|
58
58
|
def _get_n_qubits(name):
|
|
59
59
|
name = name.lower()
|
|
60
|
-
if ".h1-" in name or "hqs-lt" in name:
|
|
61
|
-
return 20
|
|
62
60
|
if ".h2-" in name:
|
|
63
61
|
return 56
|
|
64
62
|
warnings.warn(
|
|
@@ -103,7 +101,6 @@ class QuantinuumQirBackendBase(AzureQirBackend):
|
|
|
103
101
|
class QuantinuumSyntaxCheckerQirBackend(QuantinuumQirBackendBase):
|
|
104
102
|
backend_names = (
|
|
105
103
|
# Note: Target names on the same line are equivalent.
|
|
106
|
-
"quantinuum.sim.h1-1sc",
|
|
107
104
|
"quantinuum.sim.h2-1sc",
|
|
108
105
|
"quantinuum.sim.h2-2sc"
|
|
109
106
|
)
|
|
@@ -141,7 +138,6 @@ class QuantinuumSyntaxCheckerQirBackend(QuantinuumQirBackendBase):
|
|
|
141
138
|
class QuantinuumEmulatorQirBackend(QuantinuumQirBackendBase):
|
|
142
139
|
backend_names = (
|
|
143
140
|
# Note: Target names on the same line are equivalent.
|
|
144
|
-
"quantinuum.sim.h1-1e",
|
|
145
141
|
"quantinuum.sim.h2-1e",
|
|
146
142
|
"quantinuum.sim.h2-2e"
|
|
147
143
|
)
|
|
@@ -179,7 +175,6 @@ class QuantinuumEmulatorQirBackend(QuantinuumQirBackendBase):
|
|
|
179
175
|
class QuantinuumQPUQirBackend(QuantinuumQirBackendBase):
|
|
180
176
|
backend_names = (
|
|
181
177
|
# Note: Target names on the same line are equivalent.
|
|
182
|
-
"quantinuum.qpu.h1-1",
|
|
183
178
|
"quantinuum.qpu.h2-1",
|
|
184
179
|
"quantinuum.qpu.h2-2"
|
|
185
180
|
)
|
|
@@ -254,7 +249,6 @@ class QuantinuumBackend(AzureBackend):
|
|
|
254
249
|
class QuantinuumSyntaxCheckerBackend(QuantinuumBackend):
|
|
255
250
|
backend_names = (
|
|
256
251
|
# Note: Target names on the same line are equivalent.
|
|
257
|
-
"quantinuum.sim.h1-1sc",
|
|
258
252
|
"quantinuum.sim.h2-1sc",
|
|
259
253
|
"quantinuum.sim.h2-2sc"
|
|
260
254
|
)
|
|
@@ -292,7 +286,6 @@ class QuantinuumSyntaxCheckerBackend(QuantinuumBackend):
|
|
|
292
286
|
class QuantinuumEmulatorBackend(QuantinuumBackend):
|
|
293
287
|
backend_names = (
|
|
294
288
|
# Note: Target names on the same line are equivalent.
|
|
295
|
-
"quantinuum.sim.h1-1e",
|
|
296
289
|
"quantinuum.sim.h2-1e",
|
|
297
290
|
"quantinuum.sim.h2-2e"
|
|
298
291
|
)
|
|
@@ -330,7 +323,6 @@ class QuantinuumEmulatorBackend(QuantinuumBackend):
|
|
|
330
323
|
class QuantinuumQPUBackend(QuantinuumBackend):
|
|
331
324
|
backend_names = (
|
|
332
325
|
# Note: Target names on the same line are equivalent.
|
|
333
|
-
"quantinuum.qpu.h1-1",
|
|
334
326
|
"quantinuum.qpu.h2-1",
|
|
335
327
|
"quantinuum.qpu.h2-2"
|
|
336
328
|
)
|
azure/quantum/target/ionq.py
CHANGED
|
@@ -5,35 +5,11 @@
|
|
|
5
5
|
from typing import Any, Dict, List
|
|
6
6
|
from warnings import warn
|
|
7
7
|
|
|
8
|
-
from azure.quantum.target.target import (
|
|
9
|
-
Target,
|
|
10
|
-
_determine_shots_or_deprecated_num_shots,
|
|
11
|
-
)
|
|
12
8
|
from azure.quantum.job.job import Job
|
|
9
|
+
from azure.quantum.target.target import Target
|
|
13
10
|
from azure.quantum.workspace import Workspace
|
|
14
|
-
from azure.quantum._client.models import CostEstimate, UsageEvent
|
|
15
11
|
from typing import Union
|
|
16
12
|
|
|
17
|
-
COST_1QUBIT_GATE_MAP = {
|
|
18
|
-
"ionq.simulator" : 0.0,
|
|
19
|
-
"ionq.qpu.aria-1" : 0.0002205,
|
|
20
|
-
"ionq.qpu.aria-2" : 0.0002205,
|
|
21
|
-
"ionq.qpu.forte-1" : 0.0002205
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
COST_2QUBIT_GATE_MAP = {
|
|
25
|
-
"ionq.simulator" : 0.0,
|
|
26
|
-
"ionq.qpu.aria-1" : 0.00098,
|
|
27
|
-
"ionq.qpu.aria-2" : 0.00098,
|
|
28
|
-
"ionq.qpu.forte-1" : 0.00098
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
MIN_PRICE_MAP = {
|
|
32
|
-
"ionq.simulator" : 0.0,
|
|
33
|
-
"ionq.qpu.aria-1" : 97.5,
|
|
34
|
-
"ionq.qpu.aria-2" : 97.5,
|
|
35
|
-
"ionq.qpu.forte-1" : 97.5
|
|
36
|
-
}
|
|
37
13
|
|
|
38
14
|
def int_to_bitstring(k: int, num_qubits: int, measured_qubit_ids: List[int]):
|
|
39
15
|
# flip bitstring to convert to little Endian
|
|
@@ -47,8 +23,8 @@ class IonQ(Target):
|
|
|
47
23
|
target_names = (
|
|
48
24
|
"ionq.simulator",
|
|
49
25
|
"ionq.qpu.aria-1",
|
|
50
|
-
"ionq.qpu.
|
|
51
|
-
"ionq.qpu.forte-1"
|
|
26
|
+
"ionq.qpu.forte-1",
|
|
27
|
+
"ionq.qpu.forte-enterprise-1",
|
|
52
28
|
)
|
|
53
29
|
|
|
54
30
|
_SHOTS_PARAM_NAME = "shots"
|
|
@@ -111,13 +87,6 @@ class IonQ(Target):
|
|
|
111
87
|
)
|
|
112
88
|
if input_params is None:
|
|
113
89
|
input_params = {}
|
|
114
|
-
|
|
115
|
-
num_shots = kwargs.pop("num_shots", None)
|
|
116
|
-
|
|
117
|
-
shots = _determine_shots_or_deprecated_num_shots(
|
|
118
|
-
shots=shots,
|
|
119
|
-
num_shots=num_shots,
|
|
120
|
-
)
|
|
121
90
|
|
|
122
91
|
return super().submit(
|
|
123
92
|
input_data=input_data,
|
|
@@ -5,22 +5,15 @@
|
|
|
5
5
|
from typing import Any, Dict, Union
|
|
6
6
|
from warnings import warn
|
|
7
7
|
|
|
8
|
-
from azure.quantum.target.target import (
|
|
9
|
-
Target,
|
|
10
|
-
_determine_shots_or_deprecated_num_shots,
|
|
11
|
-
)
|
|
12
8
|
from azure.quantum.job.job import Job
|
|
9
|
+
from azure.quantum.target.target import Target
|
|
13
10
|
from azure.quantum.workspace import Workspace
|
|
14
|
-
from azure.quantum._client.models import CostEstimate, UsageEvent
|
|
15
11
|
|
|
16
12
|
|
|
17
13
|
class Quantinuum(Target):
|
|
18
14
|
"""Quantinuum target."""
|
|
19
15
|
target_names = (
|
|
20
16
|
# Note: Target names on the same line are equivalent.
|
|
21
|
-
"quantinuum.qpu.h1-1",
|
|
22
|
-
"quantinuum.sim.h1-1sc",
|
|
23
|
-
"quantinuum.sim.h1-1e",
|
|
24
17
|
"quantinuum.qpu.h2-1",
|
|
25
18
|
"quantinuum.sim.h2-1sc",
|
|
26
19
|
"quantinuum.sim.h2-1e",
|
|
@@ -34,7 +27,7 @@ class Quantinuum(Target):
|
|
|
34
27
|
def __init__(
|
|
35
28
|
self,
|
|
36
29
|
workspace: Workspace,
|
|
37
|
-
name: str = "quantinuum.sim.
|
|
30
|
+
name: str = "quantinuum.sim.h2-1sc",
|
|
38
31
|
input_data_format: str = "honeywell.openqasm.v1",
|
|
39
32
|
output_data_format: str = "honeywell.quantum-results.v1",
|
|
40
33
|
capability: str = "",
|
|
@@ -89,13 +82,6 @@ class Quantinuum(Target):
|
|
|
89
82
|
if input_params is None:
|
|
90
83
|
input_params = {}
|
|
91
84
|
|
|
92
|
-
num_shots = kwargs.pop("num_shots", None)
|
|
93
|
-
|
|
94
|
-
shots = _determine_shots_or_deprecated_num_shots(
|
|
95
|
-
shots=shots,
|
|
96
|
-
num_shots=num_shots,
|
|
97
|
-
)
|
|
98
|
-
|
|
99
85
|
return super().submit(
|
|
100
86
|
input_data=input_data,
|
|
101
87
|
name=name,
|
azure/quantum/target/target.py
CHANGED
|
@@ -55,7 +55,7 @@ class Target(abc.ABC, SessionHost):
|
|
|
55
55
|
|
|
56
56
|
# Name of the provider's input parameter which specifies number of shots for a submitted job.
|
|
57
57
|
# If None, target will not pass this input parameter.
|
|
58
|
-
_SHOTS_PARAM_NAME =
|
|
58
|
+
_SHOTS_PARAM_NAME = "shots"
|
|
59
59
|
|
|
60
60
|
def __init__(
|
|
61
61
|
self,
|
|
@@ -117,7 +117,7 @@ avg. queue time={self._average_queue_time} s, {self._current_availability}>"
|
|
|
117
117
|
|
|
118
118
|
@classmethod
|
|
119
119
|
def from_target_status(
|
|
120
|
-
cls, workspace: "Workspace", status: TargetStatus, **kwargs
|
|
120
|
+
cls, workspace: "Workspace", provider_id: str, status: TargetStatus, **kwargs
|
|
121
121
|
):
|
|
122
122
|
"""Create a Target instance from a given workspace and target status.
|
|
123
123
|
|
|
@@ -131,6 +131,7 @@ avg. queue time={self._average_queue_time} s, {self._current_availability}>"
|
|
|
131
131
|
return cls(
|
|
132
132
|
workspace=workspace,
|
|
133
133
|
name=status.id,
|
|
134
|
+
provider_id=provider_id,
|
|
134
135
|
average_queue_time=status.average_queue_time,
|
|
135
136
|
current_availability=status.current_availability,
|
|
136
137
|
**kwargs
|
|
@@ -389,33 +390,3 @@ target '{self.name}' of provider '{self.provider_id}' not found."
|
|
|
389
390
|
multi_qubit_gates,
|
|
390
391
|
measurement_gates
|
|
391
392
|
)
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
def _determine_shots_or_deprecated_num_shots(
|
|
395
|
-
shots: int = None,
|
|
396
|
-
num_shots: int = None,
|
|
397
|
-
) -> int:
|
|
398
|
-
"""
|
|
399
|
-
This helper function checks if the deprecated 'num_shots' parameter is specified.
|
|
400
|
-
In earlier versions it was possible to pass this parameter to specify shots number for a job,
|
|
401
|
-
but now we only check for it for compatibility reasons.
|
|
402
|
-
"""
|
|
403
|
-
final_shots = None
|
|
404
|
-
if shots is not None and num_shots is not None:
|
|
405
|
-
warnings.warn(
|
|
406
|
-
"Both 'shots' and 'num_shots' parameters were specified. Defaulting to 'shots' parameter. "
|
|
407
|
-
"Please, use 'shots' since 'num_shots' will be deprecated.",
|
|
408
|
-
category=DeprecationWarning,
|
|
409
|
-
)
|
|
410
|
-
final_shots = shots
|
|
411
|
-
|
|
412
|
-
elif shots is not None:
|
|
413
|
-
final_shots = shots
|
|
414
|
-
elif num_shots is not None:
|
|
415
|
-
warnings.warn(
|
|
416
|
-
"The 'num_shots' parameter will be deprecated. Please, use 'shots' parameter instead.",
|
|
417
|
-
category=DeprecationWarning,
|
|
418
|
-
)
|
|
419
|
-
final_shots = num_shots
|
|
420
|
-
|
|
421
|
-
return final_shots
|
|
@@ -68,10 +68,6 @@ class TargetFactory:
|
|
|
68
68
|
if provider_id.lower() in self._default_targets:
|
|
69
69
|
return self._default_targets[provider_id.lower()]
|
|
70
70
|
|
|
71
|
-
warnings.warn(
|
|
72
|
-
f"No default target specified for provider {provider_id}. \
|
|
73
|
-
Please check the provider name and try again or create an issue here: \
|
|
74
|
-
https://github.com/microsoft/qdk-python/issues.")
|
|
75
71
|
return Target
|
|
76
72
|
|
|
77
73
|
def create_target(
|
|
@@ -105,7 +101,7 @@ https://github.com/microsoft/qdk-python/issues.")
|
|
|
105
101
|
):
|
|
106
102
|
cls = self._target_cls(provider_id, status.id)
|
|
107
103
|
if hasattr(cls, "from_target_status"):
|
|
108
|
-
return cls.from_target_status(self._workspace, status, **kwargs)
|
|
104
|
+
return cls.from_target_status(self._workspace, provider_id, status, **kwargs)
|
|
109
105
|
elif cls is not None:
|
|
110
106
|
return cls(name=status.id, **kwargs)
|
|
111
107
|
|
azure/quantum/version.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: azure-quantum
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.4.0
|
|
4
4
|
Summary: Python client for Azure Quantum
|
|
5
5
|
Home-page: https://github.com/microsoft/azure-quantum-python
|
|
6
6
|
Author: Microsoft
|
|
@@ -14,13 +14,15 @@ Requires-Dist: azure-core<2.0,>=1.30
|
|
|
14
14
|
Requires-Dist: azure-identity<2.0,>=1.17
|
|
15
15
|
Requires-Dist: azure-storage-blob==12.20
|
|
16
16
|
Requires-Dist: msrest<1.0,>=0.7.1
|
|
17
|
-
Requires-Dist: numpy
|
|
17
|
+
Requires-Dist: numpy>=1.21.0
|
|
18
18
|
Requires-Dist: deprecated<2.0,>=1.2.12
|
|
19
19
|
Requires-Dist: Markdown>=3.4.1
|
|
20
20
|
Requires-Dist: python-markdown-math>=0.8
|
|
21
21
|
Provides-Extra: all
|
|
22
22
|
Requires-Dist: cirq-core<=1.4.1,>=1.3.0; extra == "all"
|
|
23
23
|
Requires-Dist: cirq-ionq<=1.4.1,>=1.3.0; extra == "all"
|
|
24
|
+
Requires-Dist: pytest>=7.1.2; extra == "all"
|
|
25
|
+
Requires-Dist: pytest-xdist<4.0,>=3.8.0; extra == "all"
|
|
24
26
|
Requires-Dist: vcrpy>=4.3.1; extra == "all"
|
|
25
27
|
Requires-Dist: azure-devtools<2.0,>=1.2.0; extra == "all"
|
|
26
28
|
Requires-Dist: graphviz>=0.20.1; extra == "all"
|
|
@@ -36,6 +38,8 @@ Provides-Extra: cirq
|
|
|
36
38
|
Requires-Dist: cirq-core<=1.4.1,>=1.3.0; extra == "cirq"
|
|
37
39
|
Requires-Dist: cirq-ionq<=1.4.1,>=1.3.0; extra == "cirq"
|
|
38
40
|
Provides-Extra: dev
|
|
41
|
+
Requires-Dist: pytest>=7.1.2; extra == "dev"
|
|
42
|
+
Requires-Dist: pytest-xdist<4.0,>=3.8.0; extra == "dev"
|
|
39
43
|
Requires-Dist: vcrpy>=4.3.1; extra == "dev"
|
|
40
44
|
Requires-Dist: azure-devtools<2.0,>=1.2.0; extra == "dev"
|
|
41
45
|
Requires-Dist: graphviz>=0.20.1; extra == "dev"
|
|
@@ -56,9 +60,9 @@ Requires-Dist: pyquil==4.13.1; extra == "quil"
|
|
|
56
60
|
|
|
57
61
|
# Azure Quantum #
|
|
58
62
|
|
|
59
|
-
[](https://badge.fury.io/py/azure-quantum)
|
|
60
64
|
|
|
61
|
-
Azure Quantum is Microsoft's cloud service for running Quantum Computing programs
|
|
65
|
+
Azure Quantum is Microsoft's cloud service for running Quantum Computing programs. The `azure-quantum` package for Python provides functionality for interacting with Azure Quantum workspaces, including creating jobs, listing jobs, and retrieving job results. For more information, view the [Azure Quantum Documentation](https://learn.microsoft.com/en-us/azure/quantum/).
|
|
62
66
|
|
|
63
67
|
This package supports submitting quantum programs or circuits written with Python. To submit quantum programs written with Q#, Microsoft's Domain-specific language for Quantum Programming, view [Submit Q# Jobs to Azure Quantum](https://learn.microsoft.com/azure/quantum/how-to-submit-jobs).
|
|
64
68
|
|
|
@@ -66,21 +70,10 @@ This package supports submitting quantum programs or circuits written with Pytho
|
|
|
66
70
|
|
|
67
71
|
The package is released on PyPI and can be installed via `pip`:
|
|
68
72
|
|
|
69
|
-
|
|
70
|
-
pip install azure-quantum
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
To use `azure-quantum` for submitting quantum circuits expressed with [Qiskit](https://pypi.org/project/qiskit), install with optional dependencies:
|
|
74
|
-
|
|
75
|
-
```bash
|
|
76
|
-
pip install azure-quantum[qiskit]
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
To use `azure-quantum` for submitting quantum circuits expressed with [Cirq](https://pypi.org/project/cirq), install with optional dependencies:
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
pip install azure-quantum[cirq]
|
|
83
|
-
```
|
|
73
|
+
- For default installation - `pip install azure-quantum`
|
|
74
|
+
- Submit quantum circuits written in [Q#](https://pypi.org/project/qsharp) - `pip install azure-quantum[qsharp]`
|
|
75
|
+
- Submit quantum circuits written in [Qiskit](https://pypi.org/project/qiskit) - `pip install azure-quantum[qiskit]`
|
|
76
|
+
- Submit quantum circuits written in [Cirq](https://pypi.org/project/cirq) - `pip install azure-quantum[cirq]`
|
|
84
77
|
|
|
85
78
|
## Getting started and Quickstart guides ##
|
|
86
79
|
|
|
@@ -131,7 +124,7 @@ result = job.get_results()
|
|
|
131
124
|
|
|
132
125
|
## Examples ##
|
|
133
126
|
|
|
134
|
-
You can find example Python scripts that use the Azure Quantum Python API in the [
|
|
127
|
+
You can find example Python scripts that use the Azure Quantum Python API in the [samples](https://github.com/microsoft/azure-quantum-python/tree/main/samples) directory.
|
|
135
128
|
|
|
136
129
|
## Contributing ##
|
|
137
130
|
|
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
azure/quantum/__init__.py,sha256=Za8xZY4lzFkW8m4ero-bqrfN437D2NRukM77ukb4GPM,508
|
|
2
2
|
azure/quantum/_constants.py,sha256=nDL_QrGdI_Zz_cvTB9nVgfE7J6A_Boo1ollMYqsiEBs,3499
|
|
3
|
-
azure/quantum/_workspace_connection_params.py,sha256=
|
|
3
|
+
azure/quantum/_workspace_connection_params.py,sha256=70T6JHa72tPLM5i7IEIBMbqa3gxXXtDmu_uORWloo50,21553
|
|
4
4
|
azure/quantum/storage.py,sha256=_4bMniDk9LrB_K5CQwuCivJFZXdmhRvU2b6Z3xxXw9I,12556
|
|
5
|
-
azure/quantum/version.py,sha256=
|
|
5
|
+
azure/quantum/version.py,sha256=8RioQxN5yJQB653S6GG900WG350bbEYROaOyrk-08bE,235
|
|
6
6
|
azure/quantum/workspace.py,sha256=9oO4vjwIn1pdHFLZCQcEPQ_xjQjTNO4UIWSBQpj6Cgo,35574
|
|
7
|
-
azure/quantum/_authentication/__init__.py,sha256=bniNZlS0hMIjO_y7DevGBAS6MixyA5pbPHcdGipUWM4,236
|
|
8
|
-
azure/quantum/_authentication/_chained.py,sha256=0rdohB_fVGFHUhlly9sGxqQTBTZGpGxtlBqNHDFbAqE,4848
|
|
9
|
-
azure/quantum/_authentication/_default.py,sha256=RzhK5UNQb6TK95VQI4o8Gm2nxtOaz64yA0J9Tw9zpW4,6625
|
|
10
|
-
azure/quantum/_authentication/_token.py,sha256=mOrvibDiOgkDnqU1OBIw9PyIv-np6DkdxMNC4UtuGkc,3616
|
|
11
7
|
azure/quantum/_client/__init__.py,sha256=P3K9ffYchHhHjJhCEAEwutSD3xRW92XDUQNYDjwhYqI,1056
|
|
12
8
|
azure/quantum/_client/_client.py,sha256=Zjqakj3NvRXXlSMi7S1LtJuWkhsNRFhlmCaJGwB6K-U,6616
|
|
13
9
|
azure/quantum/_client/_configuration.py,sha256=FFUHJPp6X0015GpD-YLmYCoq8GI86nHCnDS_THTSHbg,4184
|
|
14
10
|
azure/quantum/_client/_model_base.py,sha256=hu7OdRS2Ra1igfBo-R3zS3dzO3YhFC4FGHJ_WiyZgNg,43736
|
|
15
11
|
azure/quantum/_client/_patch.py,sha256=YTV6yZ9bRfBBaw2z7v4MdzR-zeHkdtKkGb4SU8C25mE,694
|
|
16
12
|
azure/quantum/_client/_serialization.py,sha256=bBl0y0mh-0sDd-Z8_dj921jQQhqhYWTOl59qSLuk01M,86686
|
|
17
|
-
azure/quantum/_client/_version.py,sha256=
|
|
13
|
+
azure/quantum/_client/_version.py,sha256=ardTHQuCr6iDUR4DesXA096T5-p5QrvO3oPYE4cJMwI,495
|
|
18
14
|
azure/quantum/_client/py.typed,sha256=dcrsqJrcYfTX-ckLFJMTaj6mD8aDe2u0tkQG-ZYxnEg,26
|
|
19
15
|
azure/quantum/_client/models/__init__.py,sha256=MR2av7s_tCP66hicN9JXCmTngJ4_-ozM4cmblGjPwn8,1971
|
|
20
16
|
azure/quantum/_client/models/_enums.py,sha256=RNCPXxeae4d7wtPqcSxu5JZBLFLZZYVZbALjui_f1fM,4288
|
|
@@ -29,8 +25,8 @@ azure/quantum/cirq/__init__.py,sha256=AJT_qCdxfqgeiPmqmbxQgCzETCOIJyHYoy6pCs-Osk
|
|
|
29
25
|
azure/quantum/cirq/job.py,sha256=Wm52HFYel9Di6xGdzDdYfBLM3ZklaE1qOQ9YNg87eeY,3031
|
|
30
26
|
azure/quantum/cirq/service.py,sha256=5TcBvdULJJD_KsZna35dWYfkn7EY8Rge0ucnkwgJS3w,7833
|
|
31
27
|
azure/quantum/cirq/targets/__init__.py,sha256=cpb677Kg1V5cdI0kdgkLafI8xfwYZZYx0tc6qc024gE,574
|
|
32
|
-
azure/quantum/cirq/targets/ionq.py,sha256
|
|
33
|
-
azure/quantum/cirq/targets/quantinuum.py,sha256=
|
|
28
|
+
azure/quantum/cirq/targets/ionq.py,sha256=-ABeUU2sLZd-n0FwS50-ZOn5nfm-FRkGGlgBwpwIxiE,5994
|
|
29
|
+
azure/quantum/cirq/targets/quantinuum.py,sha256=50S-ByS0Oaoo8UH-eiXpkGJMI7u84ZTIhU4X9gz3u5s,3960
|
|
34
30
|
azure/quantum/cirq/targets/target.py,sha256=1EEog72dFZoiOTQP7obOrCuO3VH0yjXGAIMeO6bm22o,2184
|
|
35
31
|
azure/quantum/job/__init__.py,sha256=nFuOsG25a8WzYFLwA2fhA0JMNWtblfDjV5WRgB6UQbw,829
|
|
36
32
|
azure/quantum/job/base_job.py,sha256=GDwoJDxoaCwGz9TBuzlkuqXdU84L6FliO_2YWxnLnrE,14399
|
|
@@ -45,23 +41,23 @@ azure/quantum/qiskit/job.py,sha256=El1wyQ98WZaR96IICkDV8kToRtcA7yG24L2eE9Wdx2c,1
|
|
|
45
41
|
azure/quantum/qiskit/provider.py,sha256=kbZJgDX-hI-AD5rQfik4JHNnNd74oyfrKYauosqBRxw,10966
|
|
46
42
|
azure/quantum/qiskit/backends/__init__.py,sha256=Ygx3GwVHbIJ9Bi-_KqVQsL7B3QfeL-qUKIMIEfuStGw,994
|
|
47
43
|
azure/quantum/qiskit/backends/backend.py,sha256=pE5FSsLRy6JpfCGDWd7SFcnH8k8qz6-eJRwZ31fh58o,23361
|
|
48
|
-
azure/quantum/qiskit/backends/ionq.py,sha256=
|
|
44
|
+
azure/quantum/qiskit/backends/ionq.py,sha256=nu9pAduelXyj4iJlE9cpCnQMFDh1ZqVcApHEhogucr4,13832
|
|
49
45
|
azure/quantum/qiskit/backends/qci.py,sha256=c0YK-znG8TSAnFmeszo7mpKhM624QHszTQoapOqOvHg,4794
|
|
50
|
-
azure/quantum/qiskit/backends/quantinuum.py,sha256=
|
|
46
|
+
azure/quantum/qiskit/backends/quantinuum.py,sha256=E8DafizdkJSBAqV72iQO3ha2fFjVk-DgRKPWS9LICG0,12444
|
|
51
47
|
azure/quantum/qiskit/backends/rigetti.py,sha256=lTsa0UjPdsAZUvvfnBge7H22TvA7c-_3fU09ZTA4ARs,4146
|
|
52
48
|
azure/quantum/target/__init__.py,sha256=hK4OqAOkgwO8W52YvPp6QeoMTFj_eum3iexMmpCMEsI,606
|
|
53
|
-
azure/quantum/target/ionq.py,sha256=
|
|
49
|
+
azure/quantum/target/ionq.py,sha256=9SS5ahHcZ7IRHq9hZb6hX_q56BbCSIKs6dRqYtQv1tk,3160
|
|
54
50
|
azure/quantum/target/params.py,sha256=Txpq_AKdVV8OeZjK1z48CIym--_a6wncorj4LmmlQ_E,9128
|
|
55
|
-
azure/quantum/target/quantinuum.py,sha256=
|
|
56
|
-
azure/quantum/target/target.py,sha256=
|
|
57
|
-
azure/quantum/target/target_factory.py,sha256=
|
|
51
|
+
azure/quantum/target/quantinuum.py,sha256=6yn_ALIPgsRtrDi3PCckWOUuT-eeXC04e2D8GNuvuOc,2897
|
|
52
|
+
azure/quantum/target/target.py,sha256=421M1yq4SfBGyfNLnQW95HNVqN9jNx0YN1uZQychlTU,15638
|
|
53
|
+
azure/quantum/target/target_factory.py,sha256=XcZrbcj6X0JCivIh8x7nKwdBg9RH1FDlSncNUIrtQV0,5058
|
|
58
54
|
azure/quantum/target/pasqal/__init__.py,sha256=qbe6oWTQTsnTYwY3xZr32z4AWaYIchx71bYlqC2rQqw,348
|
|
59
55
|
azure/quantum/target/pasqal/result.py,sha256=SUvpnrtgvCGiepmNpyifW8b4p14-SZZ1ToCC0NAdIwg,1463
|
|
60
56
|
azure/quantum/target/pasqal/target.py,sha256=K_vqavov6gvS84voRKeBx9pO8g4LrtWrlZ5-RMLWozw,5139
|
|
61
57
|
azure/quantum/target/rigetti/__init__.py,sha256=I1vyzZBYGI540pauTqJd0RSSyTShGqkEL7Yjo25_RNY,378
|
|
62
58
|
azure/quantum/target/rigetti/result.py,sha256=65mtAZxfdNrTWNjWPqgfwt2BZN6Nllo4g_bls7-Nm68,2334
|
|
63
59
|
azure/quantum/target/rigetti/target.py,sha256=HFrng9SigibPn_2KWhD5qWHiP_RNz3CNRkv288tf-z8,7586
|
|
64
|
-
azure_quantum-3.
|
|
65
|
-
azure_quantum-3.
|
|
66
|
-
azure_quantum-3.
|
|
67
|
-
azure_quantum-3.
|
|
60
|
+
azure_quantum-3.4.0.dist-info/METADATA,sha256=gKZH9VbKCBZqh756nbRLW3LjH6iF0wpdX5EofG_6HyY,7198
|
|
61
|
+
azure_quantum-3.4.0.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
62
|
+
azure_quantum-3.4.0.dist-info/top_level.txt,sha256=S7DhWV9m80TBzAhOFjxDUiNbKszzoThbnrSz5MpbHSQ,6
|
|
63
|
+
azure_quantum-3.4.0.dist-info/RECORD,,
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
# ------------------------------------
|
|
2
|
-
# Copyright (c) Microsoft Corporation.
|
|
3
|
-
# Licensed under the MIT License.
|
|
4
|
-
# ------------------------------------
|
|
5
|
-
import logging
|
|
6
|
-
|
|
7
|
-
import sys
|
|
8
|
-
from azure.core.exceptions import ClientAuthenticationError
|
|
9
|
-
from azure.identity import CredentialUnavailableError
|
|
10
|
-
from azure.core.credentials import AccessToken, TokenCredential
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
_LOGGER = logging.getLogger(__name__)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def filter_credential_warnings(record):
|
|
18
|
-
"""Suppress warnings from credentials other than DefaultAzureCredential"""
|
|
19
|
-
if record.levelno == logging.WARNING:
|
|
20
|
-
message = record.getMessage()
|
|
21
|
-
return "DefaultAzureCredential" in message
|
|
22
|
-
return True
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
def _get_error_message(history):
|
|
26
|
-
attempts = []
|
|
27
|
-
for credential, error in history:
|
|
28
|
-
if error:
|
|
29
|
-
attempts.append(f"{credential.__class__.__name__}: {error}")
|
|
30
|
-
else:
|
|
31
|
-
attempts.append(credential.__class__.__name__)
|
|
32
|
-
return """
|
|
33
|
-
Attempted credentials:\n\t{}""".format(
|
|
34
|
-
"\n\t".join(attempts)
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
class _ChainedTokenCredential(object):
|
|
39
|
-
"""
|
|
40
|
-
Based on Azure.Identity.ChainedTokenCredential from:
|
|
41
|
-
https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/identity/azure-identity/azure/identity/_credentials/chained.py
|
|
42
|
-
|
|
43
|
-
The key difference is that we don't stop attempting all credentials
|
|
44
|
-
if some of then failed or raised an exception.
|
|
45
|
-
We also don't log a warning unless all credential attempts have failed.
|
|
46
|
-
"""
|
|
47
|
-
|
|
48
|
-
def __init__(self, *credentials: TokenCredential):
|
|
49
|
-
self._successful_credential = None
|
|
50
|
-
self.credentials = credentials
|
|
51
|
-
|
|
52
|
-
def get_token(self, *scopes: str, **kwargs) -> AccessToken: # pylint:disable=unused-argument
|
|
53
|
-
"""
|
|
54
|
-
Request a token from each chained credential, in order,
|
|
55
|
-
returning the first token received.
|
|
56
|
-
This method is called automatically by Azure SDK clients.
|
|
57
|
-
|
|
58
|
-
:param str scopes: desired scopes for the access token.
|
|
59
|
-
This method requires at least one scope.
|
|
60
|
-
|
|
61
|
-
:raises ~azure.core.exceptions.ClientAuthenticationError:
|
|
62
|
-
no credential in the chain provided a token
|
|
63
|
-
"""
|
|
64
|
-
history = []
|
|
65
|
-
|
|
66
|
-
# Suppress warnings from credentials in Azure.Identity
|
|
67
|
-
azure_identity_logger = logging.getLogger("azure.identity")
|
|
68
|
-
handler = logging.StreamHandler(stream=sys.stdout)
|
|
69
|
-
handler.addFilter(filter_credential_warnings)
|
|
70
|
-
azure_identity_logger.addHandler(handler)
|
|
71
|
-
try:
|
|
72
|
-
for credential in self.credentials:
|
|
73
|
-
try:
|
|
74
|
-
token = credential.get_token(*scopes, **kwargs)
|
|
75
|
-
_LOGGER.info(
|
|
76
|
-
"%s acquired a token from %s",
|
|
77
|
-
self.__class__.__name__,
|
|
78
|
-
credential.__class__.__name__,
|
|
79
|
-
)
|
|
80
|
-
self._successful_credential = credential
|
|
81
|
-
return token
|
|
82
|
-
except CredentialUnavailableError as ex:
|
|
83
|
-
# credential didn't attempt authentication because
|
|
84
|
-
# it lacks required data or state -> continue
|
|
85
|
-
history.append((credential, ex.message))
|
|
86
|
-
_LOGGER.info(
|
|
87
|
-
"%s - %s is unavailable",
|
|
88
|
-
self.__class__.__name__,
|
|
89
|
-
credential.__class__.__name__,
|
|
90
|
-
)
|
|
91
|
-
except Exception as ex: # pylint: disable=broad-except
|
|
92
|
-
# credential failed to authenticate,
|
|
93
|
-
# or something unexpectedly raised -> break
|
|
94
|
-
history.append((credential, str(ex)))
|
|
95
|
-
# instead of logging a warning, we just want to log an info
|
|
96
|
-
# since other credentials might succeed
|
|
97
|
-
_LOGGER.info(
|
|
98
|
-
'%s.get_token failed: %s raised unexpected error "%s"',
|
|
99
|
-
self.__class__.__name__,
|
|
100
|
-
credential.__class__.__name__,
|
|
101
|
-
ex,
|
|
102
|
-
exc_info=_LOGGER.isEnabledFor(logging.DEBUG),
|
|
103
|
-
)
|
|
104
|
-
# here we do NOT want break and
|
|
105
|
-
# will continue to try other credentials
|
|
106
|
-
|
|
107
|
-
finally:
|
|
108
|
-
# Re-enable warnings from credentials in Azure.Identity
|
|
109
|
-
azure_identity_logger.removeHandler(handler)
|
|
110
|
-
|
|
111
|
-
# if all attempts failed, only then we log a warning and raise an error
|
|
112
|
-
attempts = _get_error_message(history)
|
|
113
|
-
message = (
|
|
114
|
-
self.__class__.__name__
|
|
115
|
-
+ " failed to retrieve a token from the included credentials."
|
|
116
|
-
+ attempts
|
|
117
|
-
)
|
|
118
|
-
_LOGGER.warning(message)
|
|
119
|
-
raise ClientAuthenticationError(message=message)
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# ------------------------------------
|
|
2
|
-
# Copyright (c) Microsoft Corporation.
|
|
3
|
-
# Licensed under the MIT License.
|
|
4
|
-
# ------------------------------------
|
|
5
|
-
import logging
|
|
6
|
-
import re
|
|
7
|
-
from typing import Optional
|
|
8
|
-
import urllib3
|
|
9
|
-
from azure.core.credentials import AccessToken
|
|
10
|
-
from azure.identity import (
|
|
11
|
-
AzurePowerShellCredential,
|
|
12
|
-
EnvironmentCredential,
|
|
13
|
-
ManagedIdentityCredential,
|
|
14
|
-
AzureCliCredential,
|
|
15
|
-
VisualStudioCodeCredential,
|
|
16
|
-
InteractiveBrowserCredential,
|
|
17
|
-
DeviceCodeCredential,
|
|
18
|
-
_internal as AzureIdentityInternals,
|
|
19
|
-
)
|
|
20
|
-
from ._chained import _ChainedTokenCredential
|
|
21
|
-
from ._token import _TokenFileCredential
|
|
22
|
-
from azure.quantum._constants import ConnectionConstants
|
|
23
|
-
|
|
24
|
-
_LOGGER = logging.getLogger(__name__)
|
|
25
|
-
WWW_AUTHENTICATE_REGEX = re.compile(
|
|
26
|
-
r"""
|
|
27
|
-
^
|
|
28
|
-
Bearer\sauthorization_uri="
|
|
29
|
-
https://(?P<authority>[^/]*)/
|
|
30
|
-
(?P<tenant_id>[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})
|
|
31
|
-
"
|
|
32
|
-
""",
|
|
33
|
-
re.VERBOSE | re.IGNORECASE)
|
|
34
|
-
WWW_AUTHENTICATE_HEADER_NAME = "WWW-Authenticate"
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
class _DefaultAzureCredential(_ChainedTokenCredential):
|
|
38
|
-
"""
|
|
39
|
-
Based on Azure.Identity.DefaultAzureCredential from:
|
|
40
|
-
https://github.com/Azure/azure-sdk-for-python/blob/master/sdk/identity/azure-identity/azure/identity/_credentials/default.py
|
|
41
|
-
|
|
42
|
-
The three key differences are:
|
|
43
|
-
1) Inherit from _ChainedTokenCredential, which has
|
|
44
|
-
more aggressive error handling than ChainedTokenCredential
|
|
45
|
-
2) Instantiate the internal credentials the first time the get_token gets called
|
|
46
|
-
such that we can get the tenant_id if it was not passed by the user (but we don't
|
|
47
|
-
want to do that in the constructor).
|
|
48
|
-
We automatically identify the user's tenant_id for a given subscription
|
|
49
|
-
so that users with MSA accounts don't need to pass it.
|
|
50
|
-
This is a mitigation for bug https://github.com/Azure/azure-sdk-for-python/issues/18975
|
|
51
|
-
We need the following parameters to enable auto-detection of tenant_id
|
|
52
|
-
- subscription_id
|
|
53
|
-
- arm_endpoint (defaults to the production url "https://management.azure.com/")
|
|
54
|
-
3) Add custom TokenFileCredential as first method to attempt,
|
|
55
|
-
which will look for a local access token.
|
|
56
|
-
"""
|
|
57
|
-
def __init__(
|
|
58
|
-
self,
|
|
59
|
-
arm_endpoint: str,
|
|
60
|
-
subscription_id: str,
|
|
61
|
-
client_id: Optional[str] = None,
|
|
62
|
-
tenant_id: Optional[str] = None,
|
|
63
|
-
authority: Optional[str] = None,
|
|
64
|
-
):
|
|
65
|
-
if arm_endpoint is None:
|
|
66
|
-
raise ValueError("arm_endpoint is mandatory parameter")
|
|
67
|
-
if subscription_id is None:
|
|
68
|
-
raise ValueError("subscription_id is mandatory parameter")
|
|
69
|
-
|
|
70
|
-
self.authority = self._authority_or_default(
|
|
71
|
-
authority=authority,
|
|
72
|
-
arm_endpoint=arm_endpoint)
|
|
73
|
-
self.tenant_id = tenant_id
|
|
74
|
-
self.subscription_id = subscription_id
|
|
75
|
-
self.arm_endpoint = arm_endpoint
|
|
76
|
-
self.client_id = client_id
|
|
77
|
-
# credentials will be created lazy on the first call to get_token
|
|
78
|
-
super(_DefaultAzureCredential, self).__init__()
|
|
79
|
-
|
|
80
|
-
def _authority_or_default(self, authority: str, arm_endpoint: str):
|
|
81
|
-
if authority:
|
|
82
|
-
return AzureIdentityInternals.normalize_authority(authority)
|
|
83
|
-
if arm_endpoint == ConnectionConstants.ARM_DOGFOOD_ENDPOINT:
|
|
84
|
-
return ConnectionConstants.DOGFOOD_AUTHORITY
|
|
85
|
-
return ConnectionConstants.AUTHORITY
|
|
86
|
-
|
|
87
|
-
def _initialize_credentials(self):
|
|
88
|
-
self._discover_tenant_id_(
|
|
89
|
-
arm_endpoint=self.arm_endpoint,
|
|
90
|
-
subscription_id=self.subscription_id)
|
|
91
|
-
credentials = []
|
|
92
|
-
credentials.append(_TokenFileCredential())
|
|
93
|
-
credentials.append(EnvironmentCredential())
|
|
94
|
-
if self.client_id:
|
|
95
|
-
credentials.append(ManagedIdentityCredential(client_id=self.client_id))
|
|
96
|
-
if self.authority and self.tenant_id:
|
|
97
|
-
credentials.append(VisualStudioCodeCredential(authority=self.authority, tenant_id=self.tenant_id))
|
|
98
|
-
credentials.append(AzureCliCredential(tenant_id=self.tenant_id))
|
|
99
|
-
credentials.append(AzurePowerShellCredential(tenant_id=self.tenant_id))
|
|
100
|
-
credentials.append(InteractiveBrowserCredential(authority=self.authority, tenant_id=self.tenant_id))
|
|
101
|
-
if self.client_id:
|
|
102
|
-
credentials.append(DeviceCodeCredential(authority=self.authority, client_id=self.client_id, tenant_id=self.tenant_id))
|
|
103
|
-
self.credentials = credentials
|
|
104
|
-
|
|
105
|
-
def get_token(self, *scopes: str, **kwargs) -> AccessToken:
|
|
106
|
-
"""
|
|
107
|
-
Request an access token for `scopes`.
|
|
108
|
-
This method is called automatically by Azure SDK clients.
|
|
109
|
-
|
|
110
|
-
:param str scopes: desired scopes for the access token.
|
|
111
|
-
This method requires at least one scope.
|
|
112
|
-
|
|
113
|
-
:raises ~azure.core.exceptions.ClientAuthenticationError:authentication failed.
|
|
114
|
-
The exception has a `message` attribute listing each authentication
|
|
115
|
-
attempt and its error message.
|
|
116
|
-
"""
|
|
117
|
-
# lazy-initialize the credentials
|
|
118
|
-
if self.credentials is None or len(self.credentials) == 0:
|
|
119
|
-
self._initialize_credentials()
|
|
120
|
-
|
|
121
|
-
return super(_DefaultAzureCredential, self).get_token(*scopes, **kwargs)
|
|
122
|
-
|
|
123
|
-
def _discover_tenant_id_(self, arm_endpoint:str, subscription_id:str):
|
|
124
|
-
"""
|
|
125
|
-
If the tenant_id was not given, try to obtain it
|
|
126
|
-
by calling the management endpoint for the subscription_id,
|
|
127
|
-
or by applying default values.
|
|
128
|
-
"""
|
|
129
|
-
if self.tenant_id:
|
|
130
|
-
return
|
|
131
|
-
|
|
132
|
-
try:
|
|
133
|
-
url = (
|
|
134
|
-
f"{arm_endpoint.rstrip('/')}/subscriptions/"
|
|
135
|
-
+ f"{subscription_id}?api-version=2018-01-01"
|
|
136
|
-
+ "&discover-tenant-id" # used by the test recording infrastructure
|
|
137
|
-
)
|
|
138
|
-
http = urllib3.PoolManager()
|
|
139
|
-
response = http.request(
|
|
140
|
-
method="GET",
|
|
141
|
-
url=url,
|
|
142
|
-
)
|
|
143
|
-
if WWW_AUTHENTICATE_HEADER_NAME in response.headers:
|
|
144
|
-
www_authenticate = response.headers[WWW_AUTHENTICATE_HEADER_NAME]
|
|
145
|
-
match = re.search(WWW_AUTHENTICATE_REGEX, www_authenticate)
|
|
146
|
-
if match:
|
|
147
|
-
self.tenant_id = match.group("tenant_id")
|
|
148
|
-
# pylint: disable=broad-exception-caught
|
|
149
|
-
except Exception as ex:
|
|
150
|
-
_LOGGER.error(ex)
|
|
151
|
-
|
|
152
|
-
# apply default values
|
|
153
|
-
self.tenant_id = self.tenant_id or ConnectionConstants.MSA_TENANT_ID
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
##
|
|
2
|
-
# Copyright (c) Microsoft Corporation. All rights reserved.
|
|
3
|
-
# Licensed under the MIT License.
|
|
4
|
-
##
|
|
5
|
-
import json
|
|
6
|
-
from json.decoder import JSONDecodeError
|
|
7
|
-
import logging
|
|
8
|
-
import os
|
|
9
|
-
import time
|
|
10
|
-
|
|
11
|
-
from azure.identity import CredentialUnavailableError
|
|
12
|
-
from azure.core.credentials import AccessToken
|
|
13
|
-
from azure.quantum._constants import EnvironmentVariables
|
|
14
|
-
|
|
15
|
-
_LOGGER = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class _TokenFileCredential(object):
|
|
19
|
-
"""
|
|
20
|
-
Implements a custom TokenCredential to use a local file as
|
|
21
|
-
the source for an AzureQuantum token.
|
|
22
|
-
|
|
23
|
-
It will only use the local file if the AZURE_QUANTUM_TOKEN_FILE
|
|
24
|
-
environment variable is set, and references an existing json file
|
|
25
|
-
that contains the access_token and expires_on timestamp in milliseconds.
|
|
26
|
-
|
|
27
|
-
If the environment variable is not set, the file does not exist,
|
|
28
|
-
or the token is invalid in any way (expired, for example),
|
|
29
|
-
then the credential will throw CredentialUnavailableError,
|
|
30
|
-
so that _ChainedTokenCredential can fallback to other methods.
|
|
31
|
-
"""
|
|
32
|
-
def __init__(self):
|
|
33
|
-
self.token_file = os.environ.get(EnvironmentVariables.QUANTUM_TOKEN_FILE)
|
|
34
|
-
if self.token_file:
|
|
35
|
-
_LOGGER.debug("Using provided token file location: %s", self.token_file)
|
|
36
|
-
else:
|
|
37
|
-
_LOGGER.debug("No token file location provided for %s environment variable.",
|
|
38
|
-
EnvironmentVariables.QUANTUM_TOKEN_FILE)
|
|
39
|
-
|
|
40
|
-
def get_token(self, *scopes: str, **kwargs) -> AccessToken: # pylint:disable=unused-argument
|
|
41
|
-
"""Request an access token for `scopes`.
|
|
42
|
-
This method is called automatically by Azure SDK clients.
|
|
43
|
-
This method only returns tokens for the https://quantum.microsoft.com/.default scope.
|
|
44
|
-
|
|
45
|
-
:param str scopes: desired scopes for the access token.
|
|
46
|
-
|
|
47
|
-
:raises ~azure.identity.CredentialUnavailableError
|
|
48
|
-
when failing to get the token.
|
|
49
|
-
The exception has a `message` attribute with the error message.
|
|
50
|
-
"""
|
|
51
|
-
if not self.token_file:
|
|
52
|
-
raise CredentialUnavailableError(message="Token file location not set.")
|
|
53
|
-
|
|
54
|
-
if not os.path.isfile(self.token_file):
|
|
55
|
-
raise CredentialUnavailableError(
|
|
56
|
-
message=f"Token file at {self.token_file} does not exist.")
|
|
57
|
-
|
|
58
|
-
try:
|
|
59
|
-
token = self._parse_token_file(self.token_file)
|
|
60
|
-
except JSONDecodeError as exception:
|
|
61
|
-
raise CredentialUnavailableError(
|
|
62
|
-
message="Failed to parse token file: Invalid JSON.") from exception
|
|
63
|
-
except KeyError as exception:
|
|
64
|
-
raise CredentialUnavailableError(
|
|
65
|
-
message="Failed to parse token file: Missing expected value: "
|
|
66
|
-
+ str(exception)) from exception
|
|
67
|
-
except Exception as exception:
|
|
68
|
-
raise CredentialUnavailableError(
|
|
69
|
-
message="Failed to parse token file: " + str(exception)) from exception
|
|
70
|
-
|
|
71
|
-
if token.expires_on <= time.time():
|
|
72
|
-
raise CredentialUnavailableError(
|
|
73
|
-
message=f"Token already expired at {time.asctime(time.gmtime(token.expires_on))}")
|
|
74
|
-
|
|
75
|
-
return token
|
|
76
|
-
|
|
77
|
-
def _parse_token_file(self, path) -> AccessToken:
|
|
78
|
-
with open(path, mode="r", encoding="utf-8") as file:
|
|
79
|
-
data = json.load(file)
|
|
80
|
-
# Convert ms to seconds, since python time.time only handles epoch time in seconds
|
|
81
|
-
expires_on = int(data["expires_on"]) / 1000
|
|
82
|
-
token = AccessToken(data["access_token"], expires_on)
|
|
83
|
-
return token
|
|
File without changes
|
|
File without changes
|