py2docfx 0.1.20rc2195074__py3-none-any.whl → 0.1.20rc2245107__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.
Files changed (107) hide show
  1. py2docfx/venv/basevenv/Lib/site-packages/charset_normalizer/api.py +3 -2
  2. py2docfx/venv/basevenv/Lib/site-packages/charset_normalizer/legacy.py +17 -1
  3. py2docfx/venv/basevenv/Lib/site-packages/charset_normalizer/version.py +1 -1
  4. py2docfx/venv/basevenv/Lib/site-packages/requests/__version__.py +2 -2
  5. py2docfx/venv/basevenv/Lib/site-packages/requests/adapters.py +17 -40
  6. py2docfx/venv/basevenv/Lib/site-packages/requests/sessions.py +1 -1
  7. py2docfx/venv/venv1/Lib/site-packages/azure/core/_version.py +1 -1
  8. py2docfx/venv/venv1/Lib/site-packages/azure/core/pipeline/policies/_authentication.py +21 -9
  9. py2docfx/venv/venv1/Lib/site-packages/azure/core/pipeline/policies/_authentication_async.py +21 -9
  10. py2docfx/venv/venv1/Lib/site-packages/azure/core/pipeline/policies/_retry.py +1 -1
  11. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_bearer_token_provider.py +1 -1
  12. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/authorization_code.py +1 -1
  13. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/azd_cli.py +82 -17
  14. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/azure_cli.py +28 -5
  15. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/azure_powershell.py +28 -4
  16. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/broker.py +79 -0
  17. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/chained.py +9 -3
  18. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/default.py +153 -53
  19. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/imds.py +25 -1
  20. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/shared_cache.py +12 -5
  21. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/vscode.py +163 -144
  22. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/workload_identity.py +23 -12
  23. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/__init__.py +4 -0
  24. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/interactive.py +14 -2
  25. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/pipeline.py +4 -2
  26. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/utils.py +96 -0
  27. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_version.py +1 -1
  28. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_bearer_token_provider.py +3 -3
  29. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/authorization_code.py +1 -1
  30. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/azd_cli.py +32 -13
  31. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/azure_cli.py +26 -5
  32. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/azure_powershell.py +13 -2
  33. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/chained.py +1 -1
  34. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/default.py +120 -55
  35. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/imds.py +27 -1
  36. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/on_behalf_of.py +1 -1
  37. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/shared_cache.py +12 -5
  38. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/vscode.py +15 -67
  39. py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/workload_identity.py +17 -13
  40. py2docfx/venv/venv1/Lib/site-packages/cffi/__init__.py +2 -2
  41. py2docfx/venv/venv1/Lib/site-packages/cffi/cparser.py +1 -1
  42. py2docfx/venv/venv1/Lib/site-packages/cffi/recompiler.py +5 -5
  43. py2docfx/venv/venv1/Lib/site-packages/cffi/setuptools_ext.py +13 -0
  44. py2docfx/venv/venv1/Lib/site-packages/cffi/vengine_cpy.py +3 -0
  45. py2docfx/venv/venv1/Lib/site-packages/charset_normalizer/api.py +3 -2
  46. py2docfx/venv/venv1/Lib/site-packages/charset_normalizer/legacy.py +17 -1
  47. py2docfx/venv/venv1/Lib/site-packages/charset_normalizer/version.py +1 -1
  48. py2docfx/venv/venv1/Lib/site-packages/cryptography/__about__.py +1 -1
  49. py2docfx/venv/venv1/Lib/site-packages/cryptography/__init__.py +0 -13
  50. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/_oid.py +8 -0
  51. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/asn1/__init__.py +10 -0
  52. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/asn1/asn1.py +116 -0
  53. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/backends/openssl/backend.py +3 -9
  54. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi +32 -0
  55. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/_rust/openssl/kdf.pyi +23 -0
  56. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/_rust/x509.pyi +1 -13
  57. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/openssl/_conditional.py +16 -0
  58. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/bindings/openssl/binding.py +16 -1
  59. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/padding.py +0 -2
  60. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/asymmetric/rsa.py +8 -0
  61. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/ciphers/algorithms.py +0 -47
  62. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/kdf/hkdf.py +6 -91
  63. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/kdf/kbkdf.py +1 -3
  64. py2docfx/venv/venv1/Lib/site-packages/cryptography/hazmat/primitives/serialization/ssh.py +1 -1
  65. py2docfx/venv/venv1/Lib/site-packages/cryptography/utils.py +0 -2
  66. py2docfx/venv/venv1/Lib/site-packages/cryptography/x509/name.py +2 -3
  67. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/__init__.py +1 -1
  68. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/any_pb2.py +2 -2
  69. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/api_pb2.py +12 -8
  70. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/compiler/plugin_pb2.py +2 -2
  71. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/descriptor.py +398 -246
  72. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/descriptor_pb2.py +74 -72
  73. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/descriptor_pool.py +5 -4
  74. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/duration_pb2.py +2 -2
  75. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/empty_pb2.py +2 -2
  76. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/field_mask_pb2.py +2 -2
  77. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/api_implementation.py +0 -6
  78. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/extension_dict.py +3 -3
  79. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/field_mask.py +3 -3
  80. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/python_edition_defaults.py +1 -1
  81. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/python_message.py +10 -2
  82. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/internal/type_checkers.py +47 -5
  83. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/json_format.py +55 -32
  84. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/runtime_version.py +6 -26
  85. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/source_context_pb2.py +2 -2
  86. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/struct_pb2.py +2 -2
  87. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/text_format.py +30 -19
  88. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/timestamp_pb2.py +2 -2
  89. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/type_pb2.py +2 -2
  90. py2docfx/venv/venv1/Lib/site-packages/google/protobuf/wrappers_pb2.py +2 -2
  91. py2docfx/venv/venv1/Lib/site-packages/pycparser/__init__.py +1 -1
  92. py2docfx/venv/venv1/Lib/site-packages/pycparser/c_generator.py +1 -1
  93. py2docfx/venv/venv1/Lib/site-packages/pycparser/c_lexer.py +14 -0
  94. py2docfx/venv/venv1/Lib/site-packages/pycparser/c_parser.py +30 -7
  95. py2docfx/venv/venv1/Lib/site-packages/pycparser/lextab.py +1 -1
  96. py2docfx/venv/venv1/Lib/site-packages/pycparser/yacctab.py +132 -127
  97. py2docfx/venv/venv1/Lib/site-packages/requests/__version__.py +2 -2
  98. py2docfx/venv/venv1/Lib/site-packages/requests/adapters.py +17 -40
  99. py2docfx/venv/venv1/Lib/site-packages/requests/sessions.py +1 -1
  100. py2docfx/venv/venv1/Lib/site-packages/typing_extensions.py +91 -18
  101. {py2docfx-0.1.20rc2195074.dist-info → py2docfx-0.1.20rc2245107.dist-info}/METADATA +1 -1
  102. {py2docfx-0.1.20rc2195074.dist-info → py2docfx-0.1.20rc2245107.dist-info}/RECORD +104 -103
  103. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/linux_vscode_adapter.py +0 -100
  104. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/macos_vscode_adapter.py +0 -34
  105. py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/win_vscode_adapter.py +0 -77
  106. {py2docfx-0.1.20rc2195074.dist-info → py2docfx-0.1.20rc2245107.dist-info}/WHEEL +0 -0
  107. {py2docfx-0.1.20rc2195074.dist-info → py2docfx-0.1.20rc2245107.dist-info}/top_level.txt +0 -0
@@ -369,14 +369,15 @@ def from_bytes(
369
369
  # Preparing those fallbacks in case we got nothing.
370
370
  if (
371
371
  enable_fallback
372
- and encoding_iana in ["ascii", "utf_8", specified_encoding]
372
+ and encoding_iana
373
+ in ["ascii", "utf_8", specified_encoding, "utf_16", "utf_32"]
373
374
  and not lazy_str_hard_failure
374
375
  ):
375
376
  fallback_entry = CharsetMatch(
376
377
  sequences,
377
378
  encoding_iana,
378
379
  threshold,
379
- False,
380
+ bom_or_sig_available,
380
381
  [],
381
382
  decoded_payload,
382
383
  preemptive_declaration=specified_encoding,
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any
4
4
  from warnings import warn
5
5
 
6
6
  from .api import from_bytes
7
- from .constant import CHARDET_CORRESPONDENCE
7
+ from .constant import CHARDET_CORRESPONDENCE, TOO_SMALL_SEQUENCE
8
8
 
9
9
  # TODO: remove this check when dropping Python 3.7 support
10
10
  if TYPE_CHECKING:
@@ -49,6 +49,22 @@ def detect(
49
49
  language = r.language if r is not None and r.language != "Unknown" else ""
50
50
  confidence = 1.0 - r.chaos if r is not None else None
51
51
 
52
+ # automatically lower confidence
53
+ # on small bytes samples.
54
+ # https://github.com/jawah/charset_normalizer/issues/391
55
+ if (
56
+ confidence is not None
57
+ and confidence >= 0.9
58
+ and encoding
59
+ not in {
60
+ "utf_8",
61
+ "ascii",
62
+ }
63
+ and r.bom is False # type: ignore[union-attr]
64
+ and len(byte_str) < TOO_SMALL_SEQUENCE
65
+ ):
66
+ confidence -= 0.2
67
+
52
68
  # Note: CharsetNormalizer does not return 'UTF-8-SIG' as the sig get stripped in the detection/normalization process
53
69
  # but chardet does return 'utf-8-sig' and it is a valid codec name.
54
70
  if r is not None and encoding == "utf_8" and r.bom:
@@ -4,5 +4,5 @@ Expose version
4
4
 
5
5
  from __future__ import annotations
6
6
 
7
- __version__ = "3.4.2"
7
+ __version__ = "3.4.3"
8
8
  VERSION = __version__.split(".")
@@ -5,8 +5,8 @@
5
5
  __title__ = "requests"
6
6
  __description__ = "Python HTTP for Humans."
7
7
  __url__ = "https://requests.readthedocs.io"
8
- __version__ = "2.32.4"
9
- __build__ = 0x023204
8
+ __version__ = "2.32.5"
9
+ __build__ = 0x023205
10
10
  __author__ = "Kenneth Reitz"
11
11
  __author_email__ = "me@kennethreitz.org"
12
12
  __license__ = "Apache-2.0"
@@ -27,7 +27,6 @@ from urllib3.poolmanager import PoolManager, proxy_from_url
27
27
  from urllib3.util import Timeout as TimeoutSauce
28
28
  from urllib3.util import parse_url
29
29
  from urllib3.util.retry import Retry
30
- from urllib3.util.ssl_ import create_urllib3_context
31
30
 
32
31
  from .auth import _basic_auth_str
33
32
  from .compat import basestring, urlparse
@@ -74,19 +73,6 @@ DEFAULT_RETRIES = 0
74
73
  DEFAULT_POOL_TIMEOUT = None
75
74
 
76
75
 
77
- try:
78
- import ssl # noqa: F401
79
-
80
- _preloaded_ssl_context = create_urllib3_context()
81
- _preloaded_ssl_context.load_verify_locations(
82
- extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
83
- )
84
- except ImportError:
85
- # Bypass default SSLContext creation when Python
86
- # interpreter isn't built with the ssl module.
87
- _preloaded_ssl_context = None
88
-
89
-
90
76
  def _urllib3_request_context(
91
77
  request: "PreparedRequest",
92
78
  verify: "bool | str | None",
@@ -99,19 +85,9 @@ def _urllib3_request_context(
99
85
  scheme = parsed_request_url.scheme.lower()
100
86
  port = parsed_request_url.port
101
87
 
102
- # Determine if we have and should use our default SSLContext
103
- # to optimize performance on standard requests.
104
- poolmanager_kwargs = getattr(poolmanager, "connection_pool_kw", {})
105
- has_poolmanager_ssl_context = poolmanager_kwargs.get("ssl_context")
106
- should_use_default_ssl_context = (
107
- _preloaded_ssl_context is not None and not has_poolmanager_ssl_context
108
- )
109
-
110
88
  cert_reqs = "CERT_REQUIRED"
111
89
  if verify is False:
112
90
  cert_reqs = "CERT_NONE"
113
- elif verify is True and should_use_default_ssl_context:
114
- pool_kwargs["ssl_context"] = _preloaded_ssl_context
115
91
  elif isinstance(verify, str):
116
92
  if not os.path.isdir(verify):
117
93
  pool_kwargs["ca_certs"] = verify
@@ -314,26 +290,27 @@ class HTTPAdapter(BaseAdapter):
314
290
  :param cert: The SSL certificate to verify.
315
291
  """
316
292
  if url.lower().startswith("https") and verify:
317
- conn.cert_reqs = "CERT_REQUIRED"
293
+ cert_loc = None
318
294
 
319
- # Only load the CA certificates if 'verify' is a string indicating the CA bundle to use.
320
- # Otherwise, if verify is a boolean, we don't load anything since
321
- # the connection will be using a context with the default certificates already loaded,
322
- # and this avoids a call to the slow load_verify_locations()
295
+ # Allow self-specified cert location.
323
296
  if verify is not True:
324
- # `verify` must be a str with a path then
325
297
  cert_loc = verify
326
298
 
327
- if not os.path.exists(cert_loc):
328
- raise OSError(
329
- f"Could not find a suitable TLS CA certificate bundle, "
330
- f"invalid path: {cert_loc}"
331
- )
299
+ if not cert_loc:
300
+ cert_loc = extract_zipped_paths(DEFAULT_CA_BUNDLE_PATH)
332
301
 
333
- if not os.path.isdir(cert_loc):
334
- conn.ca_certs = cert_loc
335
- else:
336
- conn.ca_cert_dir = cert_loc
302
+ if not cert_loc or not os.path.exists(cert_loc):
303
+ raise OSError(
304
+ f"Could not find a suitable TLS CA certificate bundle, "
305
+ f"invalid path: {cert_loc}"
306
+ )
307
+
308
+ conn.cert_reqs = "CERT_REQUIRED"
309
+
310
+ if not os.path.isdir(cert_loc):
311
+ conn.ca_certs = cert_loc
312
+ else:
313
+ conn.ca_cert_dir = cert_loc
337
314
  else:
338
315
  conn.cert_reqs = "CERT_NONE"
339
316
  conn.ca_certs = None
@@ -410,7 +387,7 @@ class HTTPAdapter(BaseAdapter):
410
387
  ``"cert_reqs"`` will be set
411
388
  * If ``verify`` is a string, (i.e., it is a user-specified trust bundle)
412
389
  ``"ca_certs"`` will be set if the string is not a directory recognized
413
- by :py:func:`os.path.isdir`, otherwise ``"ca_certs_dir"`` will be
390
+ by :py:func:`os.path.isdir`, otherwise ``"ca_cert_dir"`` will be
414
391
  set.
415
392
  * If ``"cert"`` is specified, ``"cert_file"`` will always be set. If
416
393
  ``"cert"`` is a tuple with a second item, ``"key_file"`` will also
@@ -535,7 +535,7 @@ class Session(SessionRedirectMixin):
535
535
  for multipart encoding upload.
536
536
  :param auth: (optional) Auth tuple or callable to enable
537
537
  Basic/Digest/Custom HTTP Auth.
538
- :param timeout: (optional) How long to wait for the server to send
538
+ :param timeout: (optional) How many seconds to wait for the server to send
539
539
  data before giving up, as a float, or a :ref:`(connect timeout,
540
540
  read timeout) <timeouts>` tuple.
541
541
  :type timeout: float or tuple
@@ -9,4 +9,4 @@
9
9
  # regenerated.
10
10
  # --------------------------------------------------------------------------
11
11
 
12
- VERSION = "1.35.0"
12
+ VERSION = "1.35.1"
@@ -6,12 +6,14 @@
6
6
  import time
7
7
  import base64
8
8
  from typing import TYPE_CHECKING, Optional, TypeVar, MutableMapping, Any, Union, cast
9
+
9
10
  from azure.core.credentials import (
10
11
  TokenCredential,
11
12
  SupportsTokenInfo,
12
13
  TokenRequestOptions,
13
14
  TokenProvider,
14
15
  )
16
+ from azure.core.exceptions import HttpResponseError
15
17
  from azure.core.pipeline import PipelineRequest, PipelineResponse
16
18
  from azure.core.pipeline.transport import (
17
19
  HttpResponse as LegacyHttpResponse,
@@ -165,7 +167,20 @@ class BearerTokenCredentialPolicy(_BearerTokenCredentialPolicyBase, HTTPPolicy[H
165
167
  if response.http_response.status_code == 401:
166
168
  self._token = None # any cached token is invalid
167
169
  if "WWW-Authenticate" in response.http_response.headers:
168
- request_authorized = self.on_challenge(request, response)
170
+ try:
171
+ request_authorized = self.on_challenge(request, response)
172
+ except Exception as ex:
173
+ # If the response is streamed, read it so the error message is immediately available to the user.
174
+ # Otherwise, a generic error message will be given and the user will have to read the response
175
+ # body to see the actual error.
176
+ if response.context.options.get("stream"):
177
+ try:
178
+ response.http_response.read() # type: ignore
179
+ except Exception: # pylint:disable=broad-except
180
+ pass
181
+ # Raise the exception from the token request with the original 401 response
182
+ raise ex from HttpResponseError(response=response.http_response)
183
+
169
184
  if request_authorized:
170
185
  # if we receive a challenge response, we retrieve a new token
171
186
  # which matches the new target. In this case, we don't want to remove
@@ -200,14 +215,11 @@ class BearerTokenCredentialPolicy(_BearerTokenCredentialPolicyBase, HTTPPolicy[H
200
215
  encoded_claims = get_challenge_parameter(headers, "Bearer", "claims")
201
216
  if not encoded_claims:
202
217
  return False
203
- try:
204
- padding_needed = -len(encoded_claims) % 4
205
- claims = base64.urlsafe_b64decode(encoded_claims + "=" * padding_needed).decode("utf-8")
206
- if claims:
207
- self.authorize_request(request, *self._scopes, claims=claims)
208
- return True
209
- except Exception: # pylint:disable=broad-except
210
- return False
218
+ padding_needed = -len(encoded_claims) % 4
219
+ claims = base64.urlsafe_b64decode(encoded_claims + "=" * padding_needed).decode("utf-8")
220
+ if claims:
221
+ self.authorize_request(request, *self._scopes, claims=claims)
222
+ return True
211
223
  return False
212
224
 
213
225
  def on_response(
@@ -13,6 +13,7 @@ from azure.core.credentials_async import (
13
13
  AsyncSupportsTokenInfo,
14
14
  AsyncTokenProvider,
15
15
  )
16
+ from azure.core.exceptions import HttpResponseError
16
17
  from azure.core.pipeline import PipelineRequest, PipelineResponse
17
18
  from azure.core.pipeline.policies import AsyncHTTPPolicy
18
19
  from azure.core.pipeline.policies._authentication import (
@@ -110,7 +111,21 @@ class AsyncBearerTokenCredentialPolicy(AsyncHTTPPolicy[HTTPRequestType, AsyncHTT
110
111
  if response.http_response.status_code == 401:
111
112
  self._token = None # any cached token is invalid
112
113
  if "WWW-Authenticate" in response.http_response.headers:
113
- request_authorized = await self.on_challenge(request, response)
114
+ try:
115
+ request_authorized = await self.on_challenge(request, response)
116
+ except Exception as ex:
117
+ # If the response is streamed, read it so the error message is immediately available to the user.
118
+ # Otherwise, a generic error message will be given and the user will have to read the response
119
+ # body to see the actual error.
120
+ if response.context.options.get("stream"):
121
+ try:
122
+ await response.http_response.read() # type: ignore
123
+ except Exception: # pylint:disable=broad-except
124
+ pass
125
+
126
+ # Raise the exception from the token request with the original 401 response
127
+ raise ex from HttpResponseError(response=response.http_response)
128
+
114
129
  if request_authorized:
115
130
  # if we receive a challenge response, we retrieve a new token
116
131
  # which matches the new target. In this case, we don't want to remove
@@ -145,14 +160,11 @@ class AsyncBearerTokenCredentialPolicy(AsyncHTTPPolicy[HTTPRequestType, AsyncHTT
145
160
  encoded_claims = get_challenge_parameter(headers, "Bearer", "claims")
146
161
  if not encoded_claims:
147
162
  return False
148
- try:
149
- padding_needed = -len(encoded_claims) % 4
150
- claims = base64.urlsafe_b64decode(encoded_claims + "=" * padding_needed).decode("utf-8")
151
- if claims:
152
- await self.authorize_request(request, *self._scopes, claims=claims)
153
- return True
154
- except Exception: # pylint:disable=broad-except
155
- return False
163
+ padding_needed = -len(encoded_claims) % 4
164
+ claims = base64.urlsafe_b64decode(encoded_claims + "=" * padding_needed).decode("utf-8")
165
+ if claims:
166
+ await self.authorize_request(request, *self._scopes, claims=claims)
167
+ return True
156
168
  return False
157
169
 
158
170
  def on_response(
@@ -118,7 +118,7 @@ class RetryPolicyBase:
118
118
  "read": options.pop("retry_read", self.read_retries),
119
119
  "status": options.pop("retry_status", self.status_retries),
120
120
  "backoff": options.pop("retry_backoff_factor", self.backoff_factor),
121
- "max_backoff": options.pop("retry_backoff_max", self.BACKOFF_MAX),
121
+ "max_backoff": options.pop("retry_backoff_max", self.backoff_max),
122
122
  "methods": options.pop("retry_on_methods", self._method_whitelist),
123
123
  "timeout": options.pop("timeout", self.timeout),
124
124
  "history": [],
@@ -30,7 +30,7 @@ def get_bearer_token_provider(credential: TokenProvider, *scopes: str) -> Callab
30
30
  request.headers["Authorization"] = "Bearer " + bearer_token_provider()
31
31
 
32
32
  :param credential: The credential used to authenticate the request.
33
- :type credential: ~azure.core.credentials.TokenCredential
33
+ :type credential: ~azure.core.credentials.TokenProvider
34
34
  :param str scopes: The scopes required for the bearer token.
35
35
  :rtype: callable
36
36
  :return: A callable that returns a bearer token.
@@ -127,7 +127,7 @@ class AuthorizationCodeCredential(GetTokenMixin):
127
127
  return token
128
128
 
129
129
  token = None
130
- for refresh_token in self._client.get_cached_refresh_tokens(scopes):
130
+ for refresh_token in self._client.get_cached_refresh_tokens(scopes, **kwargs):
131
131
  if "secret" in refresh_token:
132
132
  token = self._client.obtain_token_by_refresh_token(scopes, refresh_token["secret"], **kwargs)
133
133
  if token:
@@ -17,7 +17,7 @@ from azure.core.credentials import AccessToken, AccessTokenInfo, TokenRequestOpt
17
17
  from azure.core.exceptions import ClientAuthenticationError
18
18
 
19
19
  from .. import CredentialUnavailableError
20
- from .._internal import resolve_tenant, within_dac, validate_tenant_id, validate_scope
20
+ from .._internal import encode_base64, resolve_tenant, within_dac, validate_tenant_id, validate_scope
21
21
  from .._internal.decorators import log_get_token
22
22
 
23
23
 
@@ -28,7 +28,11 @@ CLI_NOT_FOUND = (
28
28
  "Please visit https://aka.ms/azure-dev for installation instructions and then,"
29
29
  "once installed, authenticate to your Azure account using 'azd auth login'."
30
30
  )
31
- COMMAND_LINE = ["auth", "token", "--output", "json"]
31
+ UNKNOWN_CLAIMS_FLAG = (
32
+ "Claims challenges are not supported by the Azure Developer CLI version you are using. "
33
+ "Please update to version 1.18.1 or later."
34
+ )
35
+ COMMAND_LINE = ["auth", "token", "--output", "json", "--no-prompt"]
32
36
  EXECUTABLE_NAME = "azd"
33
37
  NOT_LOGGED_IN = "Please run 'azd auth login' from a command prompt to authenticate before using this credential."
34
38
 
@@ -99,7 +103,7 @@ class AzureDeveloperCliCredential:
99
103
  def get_token(
100
104
  self,
101
105
  *scopes: str,
102
- claims: Optional[str] = None, # pylint:disable=unused-argument
106
+ claims: Optional[str] = None,
103
107
  tenant_id: Optional[str] = None,
104
108
  **kwargs: Any,
105
109
  ) -> AccessToken:
@@ -111,7 +115,8 @@ class AzureDeveloperCliCredential:
111
115
  :param str scopes: desired scope for the access token. This credential allows only one scope per request.
112
116
  For more information about scopes, see
113
117
  https://learn.microsoft.com/entra/identity-platform/scopes-oidc.
114
- :keyword str claims: not used by this credential; any value provided will be ignored.
118
+ :keyword str claims: additional claims required in the token, such as those returned in a resource provider's
119
+ claims challenge following an authorization failure.
115
120
  :keyword str tenant_id: optional tenant to include in the token request.
116
121
 
117
122
  :return: An access token with the desired scopes.
@@ -125,6 +130,8 @@ class AzureDeveloperCliCredential:
125
130
  options: TokenRequestOptions = {}
126
131
  if tenant_id:
127
132
  options["tenant_id"] = tenant_id
133
+ if claims:
134
+ options["claims"] = claims
128
135
 
129
136
  token_info = self._get_token_base(*scopes, options=options, **kwargs)
130
137
  return AccessToken(token_info.token, token_info.expires_on)
@@ -159,6 +166,7 @@ class AzureDeveloperCliCredential:
159
166
  raise ValueError("Missing scope in request. \n")
160
167
 
161
168
  tenant_id = options.get("tenant_id") if options else None
169
+ claims = options.get("claims") if options else None
162
170
  if tenant_id:
163
171
  validate_tenant_id(tenant_id)
164
172
  for scope in scopes:
@@ -175,16 +183,23 @@ class AzureDeveloperCliCredential:
175
183
  )
176
184
  if tenant:
177
185
  command_args += ["--tenant-id", tenant]
186
+ if claims:
187
+ command_args += ["--claims", encode_base64(claims)]
178
188
  output = _run_command(command_args, self._process_timeout)
179
189
 
180
190
  token = parse_token(output)
181
191
  if not token:
182
- sanitized_output = sanitize_output(output)
183
- message = (
184
- f"Unexpected output from Azure Developer CLI: '{sanitized_output}'. \n"
185
- f"To mitigate this issue, please refer to the troubleshooting guidelines here at "
186
- f"https://aka.ms/azsdk/python/identity/azdevclicredential/troubleshoot."
187
- )
192
+ # Try to extract a meaningful error from azd consoleMessage JSON lines
193
+ extracted = extract_cli_error_message(output)
194
+ if extracted:
195
+ message = extracted
196
+ else:
197
+ sanitized_output = sanitize_output(output)
198
+ message = (
199
+ f"Unexpected output from Azure Developer CLI: '{sanitized_output}'. \n"
200
+ f"To mitigate this issue, please refer to the troubleshooting guidelines here at "
201
+ f"https://aka.ms/azsdk/python/identity/azdevclicredential/troubleshoot."
202
+ )
188
203
  if within_dac.get():
189
204
  raise CredentialUnavailableError(message=message)
190
205
  raise ClientAuthenticationError(message=message)
@@ -241,6 +256,54 @@ def sanitize_output(output: str) -> str:
241
256
  return re.sub(r"\"token\": \"(.*?)(\"|$)", "****", output)
242
257
 
243
258
 
259
+ def extract_cli_error_message(output: str) -> Optional[str]:
260
+ """
261
+ Extract a single, user-friendly message from azd consoleMessage JSON output.
262
+
263
+ :param str output: The output from the Azure Developer CLI command.
264
+ :return: A user-friendly error message if found, otherwise None.
265
+ :rtype: Optional[str]
266
+
267
+ Preference order:
268
+ 1) A message containing "Suggestion" (case-insensitive)
269
+ 2) The second message if multiple are present
270
+ 3) The first message if only one exists
271
+ Returns None if no messages can be parsed.
272
+ """
273
+ messages: List[str] = []
274
+ for line in output.splitlines():
275
+ line = line.strip()
276
+ if not line:
277
+ continue
278
+ try:
279
+ obj = json.loads(line)
280
+ except json.JSONDecodeError: # not JSON -> ignore
281
+ continue
282
+ if isinstance(obj, dict):
283
+ data = obj.get("data")
284
+ if isinstance(data, dict):
285
+ msg = data.get("message")
286
+ if isinstance(msg, str) and msg.strip():
287
+ messages.append(msg.strip())
288
+ continue
289
+ msg = obj.get("message")
290
+ if isinstance(msg, str) and msg.strip():
291
+ messages.append(msg.strip())
292
+
293
+ if not messages:
294
+ return None
295
+
296
+ # Prefer the suggestion line if present
297
+ for msg in messages:
298
+ if "suggestion" in msg.lower():
299
+ return sanitize_output(msg)
300
+
301
+ # If more than one message exists, return the last one
302
+ if len(messages) > 1:
303
+ return sanitize_output(messages[-1])
304
+ return sanitize_output(messages[0])
305
+
306
+
244
307
  def _run_command(command_args: List[str], timeout: int) -> str:
245
308
  # Ensure executable exists in PATH first. This avoids a subprocess call that would fail anyway.
246
309
  azd_path = shutil.which(EXECUTABLE_NAME)
@@ -267,16 +330,18 @@ def _run_command(command_args: List[str], timeout: int) -> str:
267
330
  # Fallback check in case the executable is not found while executing subprocess.
268
331
  if ex.returncode == 127 or (ex.stderr is not None and ex.stderr.startswith("'azd' is not recognized")):
269
332
  raise CredentialUnavailableError(message=CLI_NOT_FOUND) from ex
270
- if ex.stderr is not None and (
271
- "not logged in, run `azd auth login` to login" in ex.stderr and "AADSTS" not in ex.stderr
272
- ):
333
+ combined_text = "{}\n{}".format(ex.output or "", ex.stderr or "")
334
+ if "not logged in, run `azd auth login` to login" in combined_text and "AADSTS" not in combined_text:
273
335
  raise CredentialUnavailableError(message=NOT_LOGGED_IN) from ex
336
+ if "unknown flag: --claims" in combined_text:
337
+ raise CredentialUnavailableError(message=UNKNOWN_CLAIMS_FLAG) from ex
274
338
 
275
339
  # return code is from the CLI -> propagate its output
276
- if ex.stderr:
277
- message = sanitize_output(ex.stderr)
278
- else:
279
- message = "Failed to invoke Azure Developer CLI"
340
+ message = (
341
+ extract_cli_error_message(ex.output or "")
342
+ or extract_cli_error_message(ex.stderr or "")
343
+ or (sanitize_output(ex.stderr) if ex.stderr else "Failed to invoke Azure Developer CLI")
344
+ )
280
345
  if within_dac.get():
281
346
  raise CredentialUnavailableError(message=message) from ex
282
347
  raise ClientAuthenticationError(message=message) from ex
@@ -18,6 +18,7 @@ from azure.core.exceptions import ClientAuthenticationError
18
18
  from .. import CredentialUnavailableError
19
19
  from .._internal import (
20
20
  _scopes_to_resource,
21
+ encode_base64,
21
22
  resolve_tenant,
22
23
  within_dac,
23
24
  validate_tenant_id,
@@ -34,6 +35,11 @@ CLI_NOT_FOUND = "Azure CLI not found on path"
34
35
  COMMAND_LINE = ["account", "get-access-token", "--output", "json"]
35
36
  EXECUTABLE_NAME = "az"
36
37
  NOT_LOGGED_IN = "Please run 'az login' to set up an account"
38
+ CLAIMS_UNSUPPORTED_ERROR = (
39
+ "This credential doesn't support claims challenges. To authenticate with the required "
40
+ "claims, please run the following command (requires Azure CLI version 2.76.0 or later): "
41
+ "az login --claims-challenge {claims_value}"
42
+ )
37
43
 
38
44
 
39
45
  class AzureCliCredential:
@@ -90,7 +96,7 @@ class AzureCliCredential:
90
96
  def get_token(
91
97
  self,
92
98
  *scopes: str,
93
- claims: Optional[str] = None, # pylint:disable=unused-argument
99
+ claims: Optional[str] = None,
94
100
  tenant_id: Optional[str] = None,
95
101
  **kwargs: Any,
96
102
  ) -> AccessToken:
@@ -102,20 +108,23 @@ class AzureCliCredential:
102
108
  :param str scopes: desired scope for the access token. This credential allows only one scope per request.
103
109
  For more information about scopes, see
104
110
  https://learn.microsoft.com/entra/identity-platform/scopes-oidc.
105
- :keyword str claims: not used by this credential; any value provided will be ignored.
111
+ :keyword str claims: additional claims required in the token. This credential does not support claims
112
+ challenges.
106
113
  :keyword str tenant_id: optional tenant to include in the token request.
107
114
 
108
115
  :return: An access token with the desired scopes.
109
116
  :rtype: ~azure.core.credentials.AccessToken
110
117
 
111
- :raises ~azure.identity.CredentialUnavailableError: the credential was unable to invoke the Azure CLI.
118
+ :raises ~azure.identity.CredentialUnavailableError: the credential was either unable to invoke the Azure CLI
119
+ or a claims challenge was provided.
112
120
  :raises ~azure.core.exceptions.ClientAuthenticationError: the credential invoked the Azure CLI but didn't
113
121
  receive an access token.
114
122
  """
115
-
116
123
  options: TokenRequestOptions = {}
117
124
  if tenant_id:
118
125
  options["tenant_id"] = tenant_id
126
+ if claims:
127
+ options["claims"] = claims
119
128
 
120
129
  token_info = self._get_token_base(*scopes, options=options, **kwargs)
121
130
  return AccessToken(token_info.token, token_info.expires_on)
@@ -136,7 +145,8 @@ class AzureCliCredential:
136
145
  :rtype: ~azure.core.credentials.AccessTokenInfo
137
146
  :return: An AccessTokenInfo instance containing information about the token.
138
147
 
139
- :raises ~azure.identity.CredentialUnavailableError: the credential was unable to invoke the Azure CLI.
148
+ :raises ~azure.identity.CredentialUnavailableError: the credential was either unable to invoke the Azure CLI
149
+ or a claims challenge was provided.
140
150
  :raises ~azure.core.exceptions.ClientAuthenticationError: the credential invoked the Azure CLI but didn't
141
151
  receive an access token.
142
152
  """
@@ -145,6 +155,19 @@ class AzureCliCredential:
145
155
  def _get_token_base(
146
156
  self, *scopes: str, options: Optional[TokenRequestOptions] = None, **kwargs: Any
147
157
  ) -> AccessTokenInfo:
158
+ # Check for claims challenge first
159
+ if options and "claims" in options and options["claims"]:
160
+ error_message = CLAIMS_UNSUPPORTED_ERROR.format(claims_value=encode_base64(options["claims"]))
161
+
162
+ # Add tenant if provided in options
163
+ if options.get("tenant_id"):
164
+ error_message += f" --tenant {options.get('tenant_id')}"
165
+
166
+ # Add scope if provided
167
+ if scopes:
168
+ error_message += f" --scope {scopes[0]}"
169
+
170
+ raise CredentialUnavailableError(message=error_message)
148
171
 
149
172
  tenant_id = options.get("tenant_id") if options else None
150
173
  if tenant_id:
@@ -13,7 +13,14 @@ from azure.core.exceptions import ClientAuthenticationError
13
13
 
14
14
  from .azure_cli import get_safe_working_dir
15
15
  from .. import CredentialUnavailableError
16
- from .._internal import _scopes_to_resource, resolve_tenant, within_dac, validate_tenant_id, validate_scope
16
+ from .._internal import (
17
+ _scopes_to_resource,
18
+ encode_base64,
19
+ resolve_tenant,
20
+ within_dac,
21
+ validate_tenant_id,
22
+ validate_scope,
23
+ )
17
24
  from .._internal.decorators import log_get_token
18
25
 
19
26
 
@@ -24,6 +31,11 @@ BLOCKED_BY_EXECUTION_POLICY = "Execution policy prevented invoking Azure PowerSh
24
31
  NO_AZ_ACCOUNT_MODULE = "NO_AZ_ACCOUNT_MODULE"
25
32
  POWERSHELL_NOT_INSTALLED = "PowerShell is not installed"
26
33
  RUN_CONNECT_AZ_ACCOUNT = 'Please run "Connect-AzAccount" to set up account'
34
+ CLAIMS_UNSUPPORTED_ERROR = (
35
+ "This credential doesn't support claims challenges. To authenticate with the required "
36
+ "claims, please run the following command (requires Az.Accounts module version 5.2.0 or later): "
37
+ "Connect-AzAccount -ClaimsChallenge {claims_value}"
38
+ )
27
39
  SCRIPT = """$ErrorActionPreference = 'Stop'
28
40
  [version]$minimumVersion = '2.2.0'
29
41
 
@@ -112,7 +124,7 @@ class AzurePowerShellCredential:
112
124
  def get_token(
113
125
  self,
114
126
  *scopes: str,
115
- claims: Optional[str] = None, # pylint:disable=unused-argument
127
+ claims: Optional[str] = None,
116
128
  tenant_id: Optional[str] = None,
117
129
  **kwargs: Any,
118
130
  ) -> AccessToken:
@@ -135,10 +147,11 @@ class AzurePowerShellCredential:
135
147
  :raises ~azure.core.exceptions.ClientAuthenticationError: the credential invoked Azure PowerShell but didn't
136
148
  receive an access token
137
149
  """
138
-
139
150
  options: TokenRequestOptions = {}
140
151
  if tenant_id:
141
152
  options["tenant_id"] = tenant_id
153
+ if claims:
154
+ options["claims"] = claims
142
155
 
143
156
  token_info = self._get_token_base(*scopes, options=options, **kwargs)
144
157
  return AccessToken(token_info.token, token_info.expires_on)
@@ -170,6 +183,13 @@ class AzurePowerShellCredential:
170
183
  self, *scopes: str, options: Optional[TokenRequestOptions] = None, **kwargs: Any
171
184
  ) -> AccessTokenInfo:
172
185
 
186
+ # Check if claims challenge is provided
187
+ if options and "claims" in options and options["claims"]:
188
+ error_message = CLAIMS_UNSUPPORTED_ERROR.format(claims_value=encode_base64(options["claims"]))
189
+ if options.get("tenant_id"):
190
+ error_message += f" -Tenant {options.get('tenant_id')}"
191
+ raise CredentialUnavailableError(message=error_message)
192
+
173
193
  tenant_id = options.get("tenant_id") if options else None
174
194
  if tenant_id:
175
195
  validate_tenant_id(tenant_id)
@@ -269,7 +289,11 @@ def raise_for_error(return_code: int, stdout: str, stderr: str) -> None:
269
289
 
270
290
  if stderr:
271
291
  # stderr is too noisy to include with an exception but may be useful for debugging
272
- _LOGGER.debug('%s received an error from Azure PowerShell: "%s"', AzurePowerShellCredential.__name__, stderr)
292
+ _LOGGER.debug(
293
+ '%s received an error from Azure PowerShell: "%s"',
294
+ AzurePowerShellCredential.__name__,
295
+ stderr,
296
+ )
273
297
  raise CredentialUnavailableError(
274
298
  message="Failed to invoke PowerShell. Enable debug logging for additional information."
275
299
  )