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.
Files changed (91) hide show
  1. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/PKG-INFO +1 -1
  2. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_authentication/_chained.py +21 -23
  3. azure-quantum-1.1.2.dev0/azure/quantum/_authentication/_default.py +153 -0
  4. azure-quantum-1.1.2.dev0/azure/quantum/_authentication/_token.py +83 -0
  5. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_client.py +35 -9
  6. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_configuration.py +12 -12
  7. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_serialization.py +46 -37
  8. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_version.py +1 -1
  9. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/models/_models.py +8 -8
  10. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/operations/_operations.py +261 -167
  11. azure-quantum-1.1.2.dev0/azure/quantum/_constants.py +91 -0
  12. azure-quantum-1.1.2.dev0/azure/quantum/_workspace_connection_params.py +544 -0
  13. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/version.py +1 -1
  14. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/workspace.py +119 -147
  15. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure_quantum.egg-info/PKG-INFO +1 -1
  16. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure_quantum.egg-info/SOURCES.txt +2 -0
  17. azure-quantum-1.1.1/azure/quantum/_authentication/_default.py +0 -215
  18. azure-quantum-1.1.1/azure/quantum/_authentication/_token.py +0 -80
  19. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/README.md +0 -0
  20. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/__init__.py +0 -0
  21. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_authentication/__init__.py +0 -0
  22. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/__init__.py +0 -0
  23. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_patch.py +0 -0
  24. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/_vendor.py +0 -0
  25. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/models/__init__.py +0 -0
  26. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/models/_enums.py +0 -0
  27. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/models/_patch.py +0 -0
  28. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/operations/__init__.py +0 -0
  29. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/_client/operations/_patch.py +0 -0
  30. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/argument_types/__init__.py +0 -0
  31. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/argument_types/types.py +0 -0
  32. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/chemistry/__init__.py +0 -0
  33. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/__init__.py +0 -0
  34. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/job.py +0 -0
  35. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/service.py +0 -0
  36. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/targets/__init__.py +0 -0
  37. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/targets/ionq.py +0 -0
  38. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/targets/quantinuum.py +0 -0
  39. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/cirq/targets/target.py +0 -0
  40. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/__init__.py +0 -0
  41. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/base_job.py +0 -0
  42. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/filtered_job.py +0 -0
  43. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/job.py +0 -0
  44. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/job_failed_with_results_error.py +0 -0
  45. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/session.py +0 -0
  46. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/workspace_item.py +0 -0
  47. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/job/workspace_item_factory.py +0 -0
  48. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/__init__.py +0 -0
  49. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/__init__.py +0 -0
  50. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/backend.py +0 -0
  51. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/ionq.py +0 -0
  52. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/microsoft.py +0 -0
  53. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/qci.py +0 -0
  54. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/quantinuum.py +0 -0
  55. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/backends/rigetti.py +0 -0
  56. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/job.py +0 -0
  57. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/provider.py +0 -0
  58. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/results/__init__.py +0 -0
  59. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/qiskit/results/resource_estimator.py +0 -0
  60. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/storage.py +0 -0
  61. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/__init__.py +0 -0
  62. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/ionq.py +0 -0
  63. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/__init__.py +0 -0
  64. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/elements/__init__.py +0 -0
  65. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/elements/dft/__init__.py +0 -0
  66. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/elements/dft/job.py +0 -0
  67. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/elements/dft/target.py +0 -0
  68. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/job.py +0 -0
  69. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/result.py +0 -0
  70. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/microsoft/target.py +0 -0
  71. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/params.py +0 -0
  72. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/pasqal/__init__.py +0 -0
  73. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/pasqal/result.py +0 -0
  74. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/pasqal/target.py +0 -0
  75. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/quantinuum.py +0 -0
  76. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/rigetti/__init__.py +0 -0
  77. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/rigetti/result.py +0 -0
  78. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/rigetti/target.py +0 -0
  79. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/target.py +0 -0
  80. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure/quantum/target/target_factory.py +0 -0
  81. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure_quantum.egg-info/dependency_links.txt +0 -0
  82. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure_quantum.egg-info/requires.txt +0 -0
  83. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/azure_quantum.egg-info/top_level.txt +0 -0
  84. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements-cirq.txt +0 -0
  85. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements-dev.txt +0 -0
  86. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements-qiskit.txt +0 -0
  87. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements-qsharp.txt +0 -0
  88. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements-quil.txt +0 -0
  89. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/requirements.txt +0 -0
  90. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/setup.cfg +0 -0
  91. {azure-quantum-1.1.1 → azure-quantum-1.1.2.dev0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: azure-quantum
3
- Version: 1.1.1
3
+ Version: 1.1.2.dev0
4
4
  Summary: Python client for Azure Quantum
5
5
  Home-page: https://github.com/microsoft/azure-quantum-python
6
6
  Author: Microsoft
@@ -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("{}: {}".format(credential.__class__.__name__, error))
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
- # type: (*TokenCredential) -> None
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
- # type: (*str, **Any) -> AccessToken
64
- """Request a token from each chained credential, in order, returning the first token received.
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
- :param str scopes: desired scopes for the access token. This method requires at least one scope.
67
- :raises ~azure.core.exceptions.ClientAuthenticationError: no credential in the chain provided a token
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 it lacks required data or state -> continue
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, or something unexpectedly raised -> break
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 will continue to try other credentials
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 endpoint: Service URL. Default value is "https://quantum.azure.com".
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
- self._client: PipelineClient = PipelineClient(base_url=endpoint, config=self._config, **kwargs)
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
- request_copy.url = self._client.format_url(request_copy.url)
114
- return self._client.send_request(request_copy, **kwargs)
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(Configuration): # pylint: disable=too-many-instance-attributes
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 "2022-09-12-preview". Note that overriding
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
- super(QuantumClientConfiguration, self).__init__(**kwargs)
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(