azure-quantum 1.1.1__tar.gz → 1.1.2.dev0__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.
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/PKG-INFO +1 -1
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_authentication/_chained.py +21 -23
- azure-quantum-1.1.2.dev0/azure/quantum/_authentication/_default.py +153 -0
- azure-quantum-1.1.2.dev0/azure/quantum/_authentication/_token.py +83 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_client.py +35 -9
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_configuration.py +12 -12
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_serialization.py +46 -37
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_version.py +1 -1
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/models/_models.py +8 -8
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/operations/_operations.py +261 -167
- azure-quantum-1.1.2.dev0/azure/quantum/_constants.py +91 -0
- azure-quantum-1.1.2.dev0/azure/quantum/_workspace_connection_params.py +544 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/version.py +1 -1
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/workspace.py +119 -147
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure_quantum.egg-info/PKG-INFO +1 -1
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure_quantum.egg-info/SOURCES.txt +2 -0
- azure-quantum-1.1.1/azure/quantum/_authentication/_default.py +0 -215
- azure-quantum-1.1.1/azure/quantum/_authentication/_token.py +0 -80
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/README.md +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_authentication/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_patch.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_vendor.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/models/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/models/_enums.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/models/_patch.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/operations/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/operations/_patch.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/argument_types/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/argument_types/types.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/chemistry/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/job.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/service.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/targets/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/targets/ionq.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/targets/quantinuum.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/targets/target.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/base_job.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/filtered_job.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/job.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/job_failed_with_results_error.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/session.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/workspace_item.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/workspace_item_factory.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/backend.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/ionq.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/microsoft.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/qci.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/quantinuum.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/rigetti.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/job.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/provider.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/results/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/results/resource_estimator.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/storage.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/ionq.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/elements/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/elements/dft/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/elements/dft/job.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/elements/dft/target.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/job.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/result.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/target.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/params.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/pasqal/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/pasqal/result.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/pasqal/target.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/quantinuum.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/rigetti/__init__.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/rigetti/result.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/rigetti/target.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/target.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/target_factory.py +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure_quantum.egg-info/dependency_links.txt +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure_quantum.egg-info/requires.txt +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure_quantum.egg-info/top_level.txt +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements-cirq.txt +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements-dev.txt +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements-qiskit.txt +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements-qsharp.txt +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements-quil.txt +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements.txt +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/setup.cfg +0 -0
- {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/setup.py +0 -0
|
@@ -4,23 +4,14 @@
|
|
|
4
4
|
# ------------------------------------
|
|
5
5
|
import logging
|
|
6
6
|
|
|
7
|
+
import sys
|
|
7
8
|
from azure.core.exceptions import ClientAuthenticationError
|
|
8
|
-
|
|
9
9
|
from azure.identity import CredentialUnavailableError
|
|
10
|
+
from azure.core.credentials import AccessToken, TokenCredential
|
|
10
11
|
|
|
11
|
-
try:
|
|
12
|
-
from typing import TYPE_CHECKING
|
|
13
|
-
except ImportError:
|
|
14
|
-
TYPE_CHECKING = False
|
|
15
|
-
|
|
16
|
-
if TYPE_CHECKING:
|
|
17
|
-
# pylint:disable=unused-import,ungrouped-imports
|
|
18
|
-
from typing import Any, Optional
|
|
19
|
-
from azure.core.credentials import AccessToken, TokenCredential
|
|
20
12
|
|
|
21
13
|
_LOGGER = logging.getLogger(__name__)
|
|
22
14
|
|
|
23
|
-
import sys
|
|
24
15
|
|
|
25
16
|
|
|
26
17
|
def filter_credential_warnings(record):
|
|
@@ -35,7 +26,7 @@ def _get_error_message(history):
|
|
|
35
26
|
attempts = []
|
|
36
27
|
for credential, error in history:
|
|
37
28
|
if error:
|
|
38
|
-
attempts.append("{
|
|
29
|
+
attempts.append(f"{credential.__class__.__name__}: {error}")
|
|
39
30
|
else:
|
|
40
31
|
attempts.append(credential.__class__.__name__)
|
|
41
32
|
return """
|
|
@@ -54,17 +45,21 @@ class _ChainedTokenCredential(object):
|
|
|
54
45
|
We also don't log a warning unless all credential attempts have failed.
|
|
55
46
|
"""
|
|
56
47
|
|
|
57
|
-
def __init__(self, *credentials):
|
|
58
|
-
|
|
59
|
-
self._successful_credential = None # type: Optional[TokenCredential]
|
|
48
|
+
def __init__(self, *credentials: TokenCredential):
|
|
49
|
+
self._successful_credential = None
|
|
60
50
|
self.credentials = credentials
|
|
61
51
|
|
|
62
|
-
def get_token(self, *scopes, **kwargs): # pylint:disable=unused-argument
|
|
63
|
-
|
|
64
|
-
|
|
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.
|
|
65
56
|
This method is called automatically by Azure SDK clients.
|
|
66
|
-
|
|
67
|
-
:
|
|
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
|
|
68
63
|
"""
|
|
69
64
|
history = []
|
|
70
65
|
|
|
@@ -85,7 +80,8 @@ class _ChainedTokenCredential(object):
|
|
|
85
80
|
self._successful_credential = credential
|
|
86
81
|
return token
|
|
87
82
|
except CredentialUnavailableError as ex:
|
|
88
|
-
# credential didn't attempt authentication because
|
|
83
|
+
# credential didn't attempt authentication because
|
|
84
|
+
# it lacks required data or state -> continue
|
|
89
85
|
history.append((credential, ex.message))
|
|
90
86
|
_LOGGER.info(
|
|
91
87
|
"%s - %s is unavailable",
|
|
@@ -93,7 +89,8 @@ class _ChainedTokenCredential(object):
|
|
|
93
89
|
credential.__class__.__name__,
|
|
94
90
|
)
|
|
95
91
|
except Exception as ex: # pylint: disable=broad-except
|
|
96
|
-
# credential failed to authenticate,
|
|
92
|
+
# credential failed to authenticate,
|
|
93
|
+
# or something unexpectedly raised -> break
|
|
97
94
|
history.append((credential, str(ex)))
|
|
98
95
|
# instead of logging a warning, we just want to log an info
|
|
99
96
|
# since other credentials might succeed
|
|
@@ -104,7 +101,8 @@ class _ChainedTokenCredential(object):
|
|
|
104
101
|
ex,
|
|
105
102
|
exc_info=_LOGGER.isEnabledFor(logging.DEBUG),
|
|
106
103
|
)
|
|
107
|
-
# here we do NOT want break and
|
|
104
|
+
# here we do NOT want break and
|
|
105
|
+
# will continue to try other credentials
|
|
108
106
|
|
|
109
107
|
finally:
|
|
110
108
|
# Re-enable warnings from credentials in Azure.Identity
|
|
@@ -0,0 +1,153 @@
|
|
|
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
|
|
@@ -0,0 +1,83 @@
|
|
|
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
|
|
@@ -10,6 +10,7 @@ from copy import deepcopy
|
|
|
10
10
|
from typing import Any, TYPE_CHECKING
|
|
11
11
|
|
|
12
12
|
from azure.core import PipelineClient
|
|
13
|
+
from azure.core.pipeline import policies
|
|
13
14
|
from azure.core.rest import HttpRequest, HttpResponse
|
|
14
15
|
|
|
15
16
|
from . import models as _models
|
|
@@ -44,6 +45,9 @@ class QuantumClient: # pylint: disable=client-accepts-api-version-keyword
|
|
|
44
45
|
:vartype sessions: azure.quantum._client.operations.SessionsOperations
|
|
45
46
|
:ivar top_level_items: TopLevelItemsOperations operations
|
|
46
47
|
:vartype top_level_items: azure.quantum._client.operations.TopLevelItemsOperations
|
|
48
|
+
:param azure_region: Supported Azure regions for Azure Quantum Services. For example, "eastus".
|
|
49
|
+
Required.
|
|
50
|
+
:type azure_region: str
|
|
47
51
|
:param subscription_id: The Azure subscription ID. This is a GUID-formatted string (e.g.
|
|
48
52
|
00000000-0000-0000-0000-000000000000). Required.
|
|
49
53
|
:type subscription_id: str
|
|
@@ -53,31 +57,47 @@ class QuantumClient: # pylint: disable=client-accepts-api-version-keyword
|
|
|
53
57
|
:type workspace_name: str
|
|
54
58
|
:param credential: Credential needed for the client to connect to Azure. Required.
|
|
55
59
|
:type credential: ~azure.core.credentials.TokenCredential
|
|
56
|
-
:keyword
|
|
57
|
-
:paramtype endpoint: str
|
|
58
|
-
:keyword api_version: Api Version. Default value is "2022-09-12-preview". Note that overriding
|
|
60
|
+
:keyword api_version: Api Version. Default value is "2023-11-13-preview". Note that overriding
|
|
59
61
|
this default value may result in unsupported behavior.
|
|
60
62
|
:paramtype api_version: str
|
|
61
63
|
"""
|
|
62
64
|
|
|
63
65
|
def __init__(
|
|
64
66
|
self,
|
|
67
|
+
azure_region: str,
|
|
65
68
|
subscription_id: str,
|
|
66
69
|
resource_group_name: str,
|
|
67
70
|
workspace_name: str,
|
|
68
71
|
credential: "TokenCredential",
|
|
69
|
-
*,
|
|
70
|
-
endpoint: str = "https://quantum.azure.com",
|
|
71
72
|
**kwargs: Any
|
|
72
73
|
) -> None:
|
|
74
|
+
_endpoint = kwargs.pop("endpoint", f"https://{azure_region}.quantum.azure.com")
|
|
73
75
|
self._config = QuantumClientConfiguration(
|
|
76
|
+
azure_region=azure_region,
|
|
74
77
|
subscription_id=subscription_id,
|
|
75
78
|
resource_group_name=resource_group_name,
|
|
76
79
|
workspace_name=workspace_name,
|
|
77
80
|
credential=credential,
|
|
78
81
|
**kwargs
|
|
79
82
|
)
|
|
80
|
-
|
|
83
|
+
_policies = kwargs.pop("policies", None)
|
|
84
|
+
if _policies is None:
|
|
85
|
+
_policies = [
|
|
86
|
+
policies.RequestIdPolicy(**kwargs),
|
|
87
|
+
self._config.headers_policy,
|
|
88
|
+
self._config.user_agent_policy,
|
|
89
|
+
self._config.proxy_policy,
|
|
90
|
+
policies.ContentDecodePolicy(**kwargs),
|
|
91
|
+
self._config.redirect_policy,
|
|
92
|
+
self._config.retry_policy,
|
|
93
|
+
self._config.authentication_policy,
|
|
94
|
+
self._config.custom_hook_policy,
|
|
95
|
+
self._config.logging_policy,
|
|
96
|
+
policies.DistributedTracingPolicy(**kwargs),
|
|
97
|
+
policies.SensitiveHeaderCleanupPolicy(**kwargs) if self._config.redirect_policy else None,
|
|
98
|
+
self._config.http_logging_policy,
|
|
99
|
+
]
|
|
100
|
+
self._client: PipelineClient = PipelineClient(base_url=_endpoint, policies=_policies, **kwargs)
|
|
81
101
|
|
|
82
102
|
client_models = {k: v for k, v in _models._models.__dict__.items() if isinstance(v, type)}
|
|
83
103
|
client_models.update({k: v for k, v in _models.__dict__.items() if isinstance(v, type)})
|
|
@@ -91,7 +111,7 @@ class QuantumClient: # pylint: disable=client-accepts-api-version-keyword
|
|
|
91
111
|
self.sessions = SessionsOperations(self._client, self._config, self._serialize, self._deserialize)
|
|
92
112
|
self.top_level_items = TopLevelItemsOperations(self._client, self._config, self._serialize, self._deserialize)
|
|
93
113
|
|
|
94
|
-
def send_request(self, request: HttpRequest, **kwargs: Any) -> HttpResponse:
|
|
114
|
+
def send_request(self, request: HttpRequest, *, stream: bool = False, **kwargs: Any) -> HttpResponse:
|
|
95
115
|
"""Runs the network request through the client's chained policies.
|
|
96
116
|
|
|
97
117
|
>>> from azure.core.rest import HttpRequest
|
|
@@ -110,8 +130,14 @@ class QuantumClient: # pylint: disable=client-accepts-api-version-keyword
|
|
|
110
130
|
"""
|
|
111
131
|
|
|
112
132
|
request_copy = deepcopy(request)
|
|
113
|
-
|
|
114
|
-
|
|
133
|
+
path_format_arguments = {
|
|
134
|
+
"azureRegion": self._serialize.url(
|
|
135
|
+
"self._config.azure_region", self._config.azure_region, "str", skip_quote=True
|
|
136
|
+
),
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
request_copy.url = self._client.format_url(request_copy.url, **path_format_arguments)
|
|
140
|
+
return self._client.send_request(request_copy, stream=stream, **kwargs) # type: ignore
|
|
115
141
|
|
|
116
142
|
def close(self) -> None:
|
|
117
143
|
self._client.close()
|
|
@@ -6,30 +6,26 @@
|
|
|
6
6
|
# Changes may cause incorrect behavior and will be lost if the code is regenerated.
|
|
7
7
|
# --------------------------------------------------------------------------
|
|
8
8
|
|
|
9
|
-
import sys
|
|
10
9
|
from typing import Any, TYPE_CHECKING
|
|
11
10
|
|
|
12
|
-
from azure.core.configuration import Configuration
|
|
13
11
|
from azure.core.pipeline import policies
|
|
14
12
|
|
|
15
13
|
from ._version import VERSION
|
|
16
14
|
|
|
17
|
-
if sys.version_info >= (3, 8):
|
|
18
|
-
from typing import Literal # pylint: disable=no-name-in-module, ungrouped-imports
|
|
19
|
-
else:
|
|
20
|
-
from typing_extensions import Literal # type: ignore # pylint: disable=ungrouped-imports
|
|
21
|
-
|
|
22
15
|
if TYPE_CHECKING:
|
|
23
16
|
# pylint: disable=unused-import,ungrouped-imports
|
|
24
17
|
from azure.core.credentials import TokenCredential
|
|
25
18
|
|
|
26
19
|
|
|
27
|
-
class QuantumClientConfiguration
|
|
20
|
+
class QuantumClientConfiguration: # pylint: disable=too-many-instance-attributes
|
|
28
21
|
"""Configuration for QuantumClient.
|
|
29
22
|
|
|
30
23
|
Note that all parameters used to create this instance are saved as instance
|
|
31
24
|
attributes.
|
|
32
25
|
|
|
26
|
+
:param azure_region: Supported Azure regions for Azure Quantum Services. For example, "eastus".
|
|
27
|
+
Required.
|
|
28
|
+
:type azure_region: str
|
|
33
29
|
:param subscription_id: The Azure subscription ID. This is a GUID-formatted string (e.g.
|
|
34
30
|
00000000-0000-0000-0000-000000000000). Required.
|
|
35
31
|
:type subscription_id: str
|
|
@@ -39,22 +35,24 @@ class QuantumClientConfiguration(Configuration): # pylint: disable=too-many-ins
|
|
|
39
35
|
:type workspace_name: str
|
|
40
36
|
:param credential: Credential needed for the client to connect to Azure. Required.
|
|
41
37
|
:type credential: ~azure.core.credentials.TokenCredential
|
|
42
|
-
:keyword api_version: Api Version. Default value is "
|
|
38
|
+
:keyword api_version: Api Version. Default value is "2023-11-13-preview". Note that overriding
|
|
43
39
|
this default value may result in unsupported behavior.
|
|
44
40
|
:paramtype api_version: str
|
|
45
41
|
"""
|
|
46
42
|
|
|
47
43
|
def __init__(
|
|
48
44
|
self,
|
|
45
|
+
azure_region: str,
|
|
49
46
|
subscription_id: str,
|
|
50
47
|
resource_group_name: str,
|
|
51
48
|
workspace_name: str,
|
|
52
49
|
credential: "TokenCredential",
|
|
53
50
|
**kwargs: Any
|
|
54
51
|
) -> None:
|
|
55
|
-
|
|
56
|
-
api_version: Literal["2022-09-12-preview"] = kwargs.pop("api_version", "2022-09-12-preview")
|
|
52
|
+
api_version: str = kwargs.pop("api_version", "2022-09-12-preview")
|
|
57
53
|
|
|
54
|
+
if azure_region is None:
|
|
55
|
+
raise ValueError("Parameter 'azure_region' must not be None.")
|
|
58
56
|
if subscription_id is None:
|
|
59
57
|
raise ValueError("Parameter 'subscription_id' must not be None.")
|
|
60
58
|
if resource_group_name is None:
|
|
@@ -64,6 +62,7 @@ class QuantumClientConfiguration(Configuration): # pylint: disable=too-many-ins
|
|
|
64
62
|
if credential is None:
|
|
65
63
|
raise ValueError("Parameter 'credential' must not be None.")
|
|
66
64
|
|
|
65
|
+
self.azure_region = azure_region
|
|
67
66
|
self.subscription_id = subscription_id
|
|
68
67
|
self.resource_group_name = resource_group_name
|
|
69
68
|
self.workspace_name = workspace_name
|
|
@@ -71,6 +70,7 @@ class QuantumClientConfiguration(Configuration): # pylint: disable=too-many-ins
|
|
|
71
70
|
self.api_version = api_version
|
|
72
71
|
self.credential_scopes = kwargs.pop("credential_scopes", ["https://quantum.microsoft.com/.default"])
|
|
73
72
|
kwargs.setdefault("sdk_moniker", "quantum/{}".format(VERSION))
|
|
73
|
+
self.polling_interval = kwargs.get("polling_interval", 30)
|
|
74
74
|
self._configure(**kwargs)
|
|
75
75
|
|
|
76
76
|
def _configure(self, **kwargs: Any) -> None:
|
|
@@ -79,9 +79,9 @@ class QuantumClientConfiguration(Configuration): # pylint: disable=too-many-ins
|
|
|
79
79
|
self.proxy_policy = kwargs.get("proxy_policy") or policies.ProxyPolicy(**kwargs)
|
|
80
80
|
self.logging_policy = kwargs.get("logging_policy") or policies.NetworkTraceLoggingPolicy(**kwargs)
|
|
81
81
|
self.http_logging_policy = kwargs.get("http_logging_policy") or policies.HttpLoggingPolicy(**kwargs)
|
|
82
|
-
self.retry_policy = kwargs.get("retry_policy") or policies.RetryPolicy(**kwargs)
|
|
83
82
|
self.custom_hook_policy = kwargs.get("custom_hook_policy") or policies.CustomHookPolicy(**kwargs)
|
|
84
83
|
self.redirect_policy = kwargs.get("redirect_policy") or policies.RedirectPolicy(**kwargs)
|
|
84
|
+
self.retry_policy = kwargs.get("retry_policy") or policies.RetryPolicy(**kwargs)
|
|
85
85
|
self.authentication_policy = kwargs.get("authentication_policy")
|
|
86
86
|
if self.credential and not self.authentication_policy:
|
|
87
87
|
self.authentication_policy = policies.BearerTokenCredentialPolicy(
|