py2docfx 0.1.22.dev2259826__py3-none-any.whl → 0.1.22rc2268964__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.
- py2docfx/venv/basevenv/Lib/site-packages/certifi/__init__.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/default.py +8 -9
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/imds.py +7 -3
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/managed_identity.py +7 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_credentials/shared_cache.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/interactive.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/msal_managed_identity_client.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/_version.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/default.py +8 -9
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/imds.py +7 -3
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/managed_identity.py +7 -1
- py2docfx/venv/venv1/Lib/site-packages/azure/identity/aio/_credentials/shared_cache.py +2 -2
- py2docfx/venv/venv1/Lib/site-packages/cachetools/__init__.py +96 -122
- py2docfx/venv/venv1/Lib/site-packages/cachetools/{_decorators.py → _cached.py} +106 -13
- py2docfx/venv/venv1/Lib/site-packages/cachetools/_cachedmethod.py +128 -0
- py2docfx/venv/venv1/Lib/site-packages/cachetools/func.py +5 -25
- py2docfx/venv/venv1/Lib/site-packages/certifi/__init__.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/cryptography/__about__.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/client_options.py +9 -2
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/general_helpers.py +36 -0
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/grpc_helpers.py +10 -7
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/grpc_helpers_async.py +8 -3
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/operations_v1/transports/base.py +13 -7
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/operations_v1/transports/rest.py +19 -12
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/operations_v1/transports/rest_asyncio.py +21 -0
- py2docfx/venv/venv1/Lib/site-packages/google/api_core/version.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/google/auth/_default.py +66 -12
- py2docfx/venv/venv1/Lib/site-packages/google/auth/_default_async.py +16 -10
- py2docfx/venv/venv1/Lib/site-packages/google/auth/_helpers.py +41 -0
- py2docfx/venv/venv1/Lib/site-packages/google/auth/compute_engine/credentials.py +67 -6
- py2docfx/venv/venv1/Lib/site-packages/google/auth/credentials.py +161 -18
- py2docfx/venv/venv1/Lib/site-packages/google/auth/environment_vars.py +4 -0
- py2docfx/venv/venv1/Lib/site-packages/google/auth/external_account.py +33 -10
- py2docfx/venv/venv1/Lib/site-packages/google/auth/external_account_authorized_user.py +24 -1
- py2docfx/venv/venv1/Lib/site-packages/google/auth/identity_pool.py +25 -1
- py2docfx/venv/venv1/Lib/site-packages/google/auth/impersonated_credentials.py +57 -9
- py2docfx/venv/venv1/Lib/site-packages/google/auth/pluggable.py +25 -1
- py2docfx/venv/venv1/Lib/site-packages/google/auth/version.py +1 -1
- py2docfx/venv/venv1/Lib/site-packages/google/oauth2/_client.py +117 -0
- py2docfx/venv/venv1/Lib/site-packages/google/oauth2/service_account.py +39 -4
- {py2docfx-0.1.22.dev2259826.dist-info → py2docfx-0.1.22rc2268964.dist-info}/METADATA +1 -1
- {py2docfx-0.1.22.dev2259826.dist-info → py2docfx-0.1.22rc2268964.dist-info}/RECORD +44 -43
- {py2docfx-0.1.22.dev2259826.dist-info → py2docfx-0.1.22rc2268964.dist-info}/WHEEL +0 -0
- {py2docfx-0.1.22.dev2259826.dist-info → py2docfx-0.1.22rc2268964.dist-info}/top_level.txt +0 -0
@@ -172,7 +172,8 @@ class DefaultAzureCredential(ChainedTokenCredential):
|
|
172
172
|
|
173
173
|
process_timeout = kwargs.pop("process_timeout", 10)
|
174
174
|
require_envvar = kwargs.pop("require_envvar", False)
|
175
|
-
|
175
|
+
token_credentials_env = os.environ.get(EnvironmentVariables.AZURE_TOKEN_CREDENTIALS, "").strip().lower()
|
176
|
+
if require_envvar and not token_credentials_env:
|
176
177
|
raise ValueError(
|
177
178
|
"AZURE_TOKEN_CREDENTIALS environment variable is required but is not set or is empty. "
|
178
179
|
"Set it to 'dev', 'prod', or a specific credential name."
|
@@ -274,18 +275,16 @@ class DefaultAzureCredential(ChainedTokenCredential):
|
|
274
275
|
ManagedIdentityCredential(
|
275
276
|
client_id=managed_identity_client_id,
|
276
277
|
_exclude_workload_identity_credential=exclude_workload_identity_credential,
|
278
|
+
_enable_imds_probe=token_credentials_env != "managedidentitycredential",
|
277
279
|
**kwargs,
|
278
280
|
)
|
279
281
|
)
|
280
282
|
if not exclude_shared_token_cache_credential and SharedTokenCacheCredential.supported():
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
credentials.append(shared_cache)
|
287
|
-
except Exception as ex: # pylint:disable=broad-except
|
288
|
-
_LOGGER.info("Shared token cache is unavailable: '%s'", ex)
|
283
|
+
# username and/or tenant_id are only required when the cache contains tokens for multiple identities
|
284
|
+
shared_cache = SharedTokenCacheCredential(
|
285
|
+
username=shared_cache_username, tenant_id=shared_cache_tenant_id, authority=authority, **kwargs
|
286
|
+
)
|
287
|
+
credentials.append(shared_cache)
|
289
288
|
if not exclude_visual_studio_code_credential:
|
290
289
|
credentials.append(VisualStudioCodeCredential(tenant_id=vscode_tenant_id))
|
291
290
|
if not exclude_cli_credential:
|
@@ -82,6 +82,10 @@ def _check_forbidden_response(ex: HttpResponseError) -> None:
|
|
82
82
|
|
83
83
|
class ImdsCredential(MsalManagedIdentityClient):
|
84
84
|
def __init__(self, **kwargs: Any) -> None:
|
85
|
+
# If set to True/False, _enable_imds_probe forces whether or not the credential
|
86
|
+
# probes for the IMDS endpoint before attempting to get a token. If None (the default),
|
87
|
+
# the credential probes only if it's part of a ChainedTokenCredential chain.
|
88
|
+
self._enable_imds_probe = kwargs.pop("_enable_imds_probe", None)
|
85
89
|
super().__init__(retry_policy_class=ImdsRetryPolicy, **dict(PIPELINE_SETTINGS, **kwargs))
|
86
90
|
self._config = kwargs
|
87
91
|
|
@@ -102,9 +106,9 @@ class ImdsCredential(MsalManagedIdentityClient):
|
|
102
106
|
|
103
107
|
def _request_token(self, *scopes: str, **kwargs: Any) -> AccessTokenInfo:
|
104
108
|
|
105
|
-
if
|
106
|
-
|
107
|
-
# is available to avoid hanging for a long time if
|
109
|
+
do_probe = self._enable_imds_probe if self._enable_imds_probe is not None else within_credential_chain.get()
|
110
|
+
if do_probe and not self._endpoint_available:
|
111
|
+
# Probe to see if the IMDS endpoint is available to avoid hanging for a long time if it's not.
|
108
112
|
try:
|
109
113
|
client = ManagedIdentityClient(_get_request, **dict(PIPELINE_SETTINGS, **self._config))
|
110
114
|
client.request_token(*scopes, connection_timeout=1, retry_total=0)
|
@@ -76,6 +76,7 @@ class ManagedIdentityCredential:
|
|
76
76
|
user_identity_info = validate_identity_config(client_id, identity_config)
|
77
77
|
self._credential: Optional[SupportsTokenInfo] = None
|
78
78
|
exclude_workload_identity = kwargs.pop("_exclude_workload_identity_credential", False)
|
79
|
+
self._enable_imds_probe = kwargs.pop("_enable_imds_probe", None)
|
79
80
|
managed_identity_type = None
|
80
81
|
|
81
82
|
if os.environ.get(EnvironmentVariables.IDENTITY_ENDPOINT):
|
@@ -136,7 +137,12 @@ class ManagedIdentityCredential:
|
|
136
137
|
managed_identity_type = "IMDS"
|
137
138
|
from .imds import ImdsCredential
|
138
139
|
|
139
|
-
self._credential = ImdsCredential(
|
140
|
+
self._credential = ImdsCredential(
|
141
|
+
client_id=client_id,
|
142
|
+
identity_config=identity_config,
|
143
|
+
_enable_imds_probe=self._enable_imds_probe,
|
144
|
+
**kwargs,
|
145
|
+
)
|
140
146
|
|
141
147
|
if managed_identity_type:
|
142
148
|
log_msg = f"{self.__class__.__name__} will use {managed_identity_type}"
|
@@ -198,9 +198,9 @@ class _SharedTokenCacheCredential(SharedTokenCacheBase):
|
|
198
198
|
return token
|
199
199
|
except Exception as e: # pylint: disable=broad-except
|
200
200
|
if within_dac.get():
|
201
|
-
raise CredentialUnavailableError(
|
201
|
+
raise CredentialUnavailableError(
|
202
202
|
message=getattr(e, "message", str(e)), response=getattr(e, "response", None)
|
203
|
-
)
|
203
|
+
) from e
|
204
204
|
raise
|
205
205
|
|
206
206
|
raise CredentialUnavailableError(message=NO_TOKEN.format(account.get("username")))
|
@@ -203,7 +203,7 @@ class InteractiveCredential(MsalCredential, ABC):
|
|
203
203
|
return token
|
204
204
|
except Exception as ex: # pylint:disable=broad-except
|
205
205
|
if not (isinstance(ex, AuthenticationRequiredError) and allow_prompt):
|
206
|
-
_LOGGER.warning(
|
206
|
+
_LOGGER.warning( # pylint: disable=do-not-log-raised-errors
|
207
207
|
"%s.%s failed: %s",
|
208
208
|
self.__class__.__name__,
|
209
209
|
base_method_name,
|
@@ -225,7 +225,7 @@ class InteractiveCredential(MsalCredential, ABC):
|
|
225
225
|
# this may be the first authentication, or the user may have authenticated a different identity
|
226
226
|
self._auth_record = _build_auth_record(result)
|
227
227
|
except Exception as ex:
|
228
|
-
_LOGGER.warning(
|
228
|
+
_LOGGER.warning( # pylint: disable=do-not-log-raised-errors
|
229
229
|
"%s.%s failed: %s",
|
230
230
|
self.__class__.__name__,
|
231
231
|
base_method_name,
|
py2docfx/venv/venv1/Lib/site-packages/azure/identity/_internal/msal_managed_identity_client.py
CHANGED
@@ -61,7 +61,7 @@ class MsalManagedIdentityClient(abc.ABC): # pylint:disable=client-accepts-api-v
|
|
61
61
|
)
|
62
62
|
error_desc = ""
|
63
63
|
if result and "error" in result:
|
64
|
-
error_desc =
|
64
|
+
error_desc = f"Token request error: ({result['error']}) {result.get('error_description', '')}"
|
65
65
|
error_message = self.get_unavailable_message(error_desc)
|
66
66
|
raise CredentialUnavailableError(error_message)
|
67
67
|
|
@@ -144,7 +144,8 @@ class DefaultAzureCredential(ChainedTokenCredential):
|
|
144
144
|
|
145
145
|
process_timeout = kwargs.pop("process_timeout", 10)
|
146
146
|
require_envvar = kwargs.pop("require_envvar", False)
|
147
|
-
|
147
|
+
token_credentials_env = os.environ.get(EnvironmentVariables.AZURE_TOKEN_CREDENTIALS, "").strip().lower()
|
148
|
+
if require_envvar and not token_credentials_env:
|
148
149
|
raise ValueError(
|
149
150
|
"AZURE_TOKEN_CREDENTIALS environment variable is required but is not set or is empty. "
|
150
151
|
"Set it to 'dev', 'prod', or a specific credential name."
|
@@ -235,18 +236,16 @@ class DefaultAzureCredential(ChainedTokenCredential):
|
|
235
236
|
ManagedIdentityCredential(
|
236
237
|
client_id=managed_identity_client_id,
|
237
238
|
_exclude_workload_identity_credential=exclude_workload_identity_credential,
|
239
|
+
_enable_imds_probe=token_credentials_env != "managedidentitycredential",
|
238
240
|
**kwargs,
|
239
241
|
)
|
240
242
|
)
|
241
243
|
if not exclude_shared_token_cache_credential and SharedTokenCacheCredential.supported():
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
credentials.append(shared_cache)
|
248
|
-
except Exception as ex: # pylint:disable=broad-except
|
249
|
-
_LOGGER.info("Shared token cache is unavailable: '%s'", ex)
|
244
|
+
# username and/or tenant_id are only required when the cache contains tokens for multiple identities
|
245
|
+
shared_cache = SharedTokenCacheCredential(
|
246
|
+
username=shared_cache_username, tenant_id=shared_cache_tenant_id, authority=authority, **kwargs
|
247
|
+
)
|
248
|
+
credentials.append(shared_cache)
|
250
249
|
if not exclude_visual_studio_code_credential:
|
251
250
|
credentials.append(VisualStudioCodeCredential(tenant_id=vscode_tenant_id))
|
252
251
|
if not exclude_cli_credential:
|
@@ -45,6 +45,10 @@ class ImdsCredential(AsyncContextManager, GetTokenMixin):
|
|
45
45
|
def __init__(self, **kwargs: Any) -> None:
|
46
46
|
super().__init__()
|
47
47
|
|
48
|
+
# If set to True/False, _enable_imds_probe forces whether or not the credential
|
49
|
+
# probes for the IMDS endpoint before attempting to get a token. If None (the default),
|
50
|
+
# the credential probes only if it's part of a ChainedTokenCredential chain.
|
51
|
+
self._enable_imds_probe = kwargs.pop("_enable_imds_probe", None)
|
48
52
|
kwargs["retry_policy_class"] = AsyncImdsRetryPolicy
|
49
53
|
self._client = AsyncManagedIdentityClient(_get_request, **dict(PIPELINE_SETTINGS, **kwargs))
|
50
54
|
if EnvironmentVariables.AZURE_POD_IDENTITY_AUTHORITY_HOST in os.environ:
|
@@ -65,9 +69,9 @@ class ImdsCredential(AsyncContextManager, GetTokenMixin):
|
|
65
69
|
|
66
70
|
async def _request_token(self, *scopes: str, **kwargs: Any) -> AccessTokenInfo:
|
67
71
|
|
68
|
-
if
|
69
|
-
|
70
|
-
# is available to avoid hanging for a long time if
|
72
|
+
do_probe = self._enable_imds_probe if self._enable_imds_probe is not None else within_credential_chain.get()
|
73
|
+
if do_probe and not self._endpoint_available:
|
74
|
+
# Probe to see if the IMDS endpoint is available to avoid hanging for a long time if it's not.
|
71
75
|
try:
|
72
76
|
await self._client.request_token(*scopes, connection_timeout=1, retry_total=0)
|
73
77
|
self._endpoint_available = True
|
@@ -49,6 +49,7 @@ class ManagedIdentityCredential(AsyncContextManager):
|
|
49
49
|
user_identity_info = validate_identity_config(client_id, identity_config)
|
50
50
|
self._credential: Optional[AsyncSupportsTokenInfo] = None
|
51
51
|
exclude_workload_identity = kwargs.pop("_exclude_workload_identity_credential", False)
|
52
|
+
self._enable_imds_probe = kwargs.pop("_enable_imds_probe", None)
|
52
53
|
managed_identity_type = None
|
53
54
|
if os.environ.get(EnvironmentVariables.IDENTITY_ENDPOINT):
|
54
55
|
if os.environ.get(EnvironmentVariables.IDENTITY_HEADER):
|
@@ -108,7 +109,12 @@ class ManagedIdentityCredential(AsyncContextManager):
|
|
108
109
|
managed_identity_type = "IMDS"
|
109
110
|
from .imds import ImdsCredential
|
110
111
|
|
111
|
-
self._credential = ImdsCredential(
|
112
|
+
self._credential = ImdsCredential(
|
113
|
+
client_id=client_id,
|
114
|
+
identity_config=identity_config,
|
115
|
+
_enable_imds_probe=self._enable_imds_probe,
|
116
|
+
**kwargs,
|
117
|
+
)
|
112
118
|
|
113
119
|
if managed_identity_type:
|
114
120
|
log_msg = f"{self.__class__.__name__} will use {managed_identity_type}"
|
@@ -151,9 +151,9 @@ class SharedTokenCacheCredential(SharedTokenCacheBase, AsyncContextManager):
|
|
151
151
|
return token
|
152
152
|
except Exception as e: # pylint: disable=broad-except
|
153
153
|
if within_dac.get():
|
154
|
-
raise CredentialUnavailableError(
|
154
|
+
raise CredentialUnavailableError(
|
155
155
|
message=getattr(e, "message", str(e)), response=getattr(e, "response", None)
|
156
|
-
)
|
156
|
+
) from e
|
157
157
|
raise
|
158
158
|
raise CredentialUnavailableError(message=NO_TOKEN.format(account.get("username")))
|
159
159
|
|
@@ -5,7 +5,6 @@ __all__ = (
|
|
5
5
|
"FIFOCache",
|
6
6
|
"LFUCache",
|
7
7
|
"LRUCache",
|
8
|
-
"MRUCache",
|
9
8
|
"RRCache",
|
10
9
|
"TLRUCache",
|
11
10
|
"TTLCache",
|
@@ -13,7 +12,7 @@ __all__ = (
|
|
13
12
|
"cachedmethod",
|
14
13
|
)
|
15
14
|
|
16
|
-
__version__ = "
|
15
|
+
__version__ = "6.2.0"
|
17
16
|
|
18
17
|
import collections
|
19
18
|
import collections.abc
|
@@ -23,7 +22,6 @@ import random
|
|
23
22
|
import time
|
24
23
|
|
25
24
|
from . import keys
|
26
|
-
from ._decorators import _cached_wrapper
|
27
25
|
|
28
26
|
|
29
27
|
class _DefaultSize:
|
@@ -172,32 +170,78 @@ class FIFOCache(Cache):
|
|
172
170
|
class LFUCache(Cache):
|
173
171
|
"""Least Frequently Used (LFU) cache implementation."""
|
174
172
|
|
173
|
+
class _Link:
|
174
|
+
__slots__ = ("count", "keys", "next", "prev")
|
175
|
+
|
176
|
+
def __init__(self, count):
|
177
|
+
self.count = count
|
178
|
+
self.keys = set()
|
179
|
+
|
180
|
+
def unlink(self):
|
181
|
+
next = self.next
|
182
|
+
prev = self.prev
|
183
|
+
prev.next = next
|
184
|
+
next.prev = prev
|
185
|
+
|
175
186
|
def __init__(self, maxsize, getsizeof=None):
|
176
187
|
Cache.__init__(self, maxsize, getsizeof)
|
177
|
-
self.
|
188
|
+
self.__root = root = LFUCache._Link(0) # sentinel
|
189
|
+
root.prev = root.next = root
|
190
|
+
self.__links = {}
|
178
191
|
|
179
192
|
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
|
180
193
|
value = cache_getitem(self, key)
|
181
194
|
if key in self: # __missing__ may not store item
|
182
|
-
self.
|
195
|
+
self.__touch(key)
|
183
196
|
return value
|
184
197
|
|
185
198
|
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
|
186
199
|
cache_setitem(self, key, value)
|
187
|
-
|
200
|
+
if key in self.__links:
|
201
|
+
return self.__touch(key)
|
202
|
+
root = self.__root
|
203
|
+
link = root.next
|
204
|
+
if link.count != 1:
|
205
|
+
link = LFUCache._Link(1)
|
206
|
+
link.next = root.next
|
207
|
+
root.next = link.next.prev = link
|
208
|
+
link.prev = root
|
209
|
+
link.keys.add(key)
|
210
|
+
self.__links[key] = link
|
188
211
|
|
189
212
|
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
|
190
213
|
cache_delitem(self, key)
|
191
|
-
|
214
|
+
link = self.__links.pop(key)
|
215
|
+
link.keys.remove(key)
|
216
|
+
if not link.keys:
|
217
|
+
link.unlink()
|
192
218
|
|
193
219
|
def popitem(self):
|
194
220
|
"""Remove and return the `(key, value)` pair least frequently used."""
|
195
|
-
|
196
|
-
|
197
|
-
|
221
|
+
root = self.__root
|
222
|
+
curr = root.next
|
223
|
+
if curr is root:
|
198
224
|
raise KeyError("%s is empty" % type(self).__name__) from None
|
199
|
-
|
200
|
-
|
225
|
+
key = next(iter(curr.keys)) # remove an arbitrary element
|
226
|
+
return (key, self.pop(key))
|
227
|
+
|
228
|
+
def __touch(self, key):
|
229
|
+
"""Increment use count"""
|
230
|
+
link = self.__links[key]
|
231
|
+
curr = link.next
|
232
|
+
if curr.count != link.count + 1:
|
233
|
+
if len(link.keys) == 1:
|
234
|
+
link.count += 1
|
235
|
+
return
|
236
|
+
curr = LFUCache._Link(link.count + 1)
|
237
|
+
curr.next = link.next
|
238
|
+
link.next = curr.next.prev = curr
|
239
|
+
curr.prev = link
|
240
|
+
curr.keys.add(key)
|
241
|
+
link.keys.remove(key)
|
242
|
+
if not link.keys:
|
243
|
+
link.unlink()
|
244
|
+
self.__links[key] = curr
|
201
245
|
|
202
246
|
|
203
247
|
class LRUCache(Cache):
|
@@ -210,12 +254,12 @@ class LRUCache(Cache):
|
|
210
254
|
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
|
211
255
|
value = cache_getitem(self, key)
|
212
256
|
if key in self: # __missing__ may not store item
|
213
|
-
self.
|
257
|
+
self.__touch(key)
|
214
258
|
return value
|
215
259
|
|
216
260
|
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
|
217
261
|
cache_setitem(self, key, value)
|
218
|
-
self.
|
262
|
+
self.__touch(key)
|
219
263
|
|
220
264
|
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
|
221
265
|
cache_delitem(self, key)
|
@@ -230,70 +274,47 @@ class LRUCache(Cache):
|
|
230
274
|
else:
|
231
275
|
return (key, self.pop(key))
|
232
276
|
|
233
|
-
def
|
277
|
+
def __touch(self, key):
|
278
|
+
"""Mark as recently used"""
|
234
279
|
try:
|
235
280
|
self.__order.move_to_end(key)
|
236
281
|
except KeyError:
|
237
282
|
self.__order[key] = None
|
238
283
|
|
239
284
|
|
240
|
-
class MRUCache(Cache):
|
241
|
-
"""Most Recently Used (MRU) cache implementation."""
|
242
|
-
|
243
|
-
def __init__(self, maxsize, getsizeof=None):
|
244
|
-
from warnings import warn
|
245
|
-
|
246
|
-
warn("MRUCache is deprecated", DeprecationWarning, stacklevel=2)
|
247
|
-
|
248
|
-
Cache.__init__(self, maxsize, getsizeof)
|
249
|
-
self.__order = collections.OrderedDict()
|
250
|
-
|
251
|
-
def __getitem__(self, key, cache_getitem=Cache.__getitem__):
|
252
|
-
value = cache_getitem(self, key)
|
253
|
-
if key in self: # __missing__ may not store item
|
254
|
-
self.__update(key)
|
255
|
-
return value
|
256
|
-
|
257
|
-
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
|
258
|
-
cache_setitem(self, key, value)
|
259
|
-
self.__update(key)
|
260
|
-
|
261
|
-
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
|
262
|
-
cache_delitem(self, key)
|
263
|
-
del self.__order[key]
|
264
|
-
|
265
|
-
def popitem(self):
|
266
|
-
"""Remove and return the `(key, value)` pair most recently used."""
|
267
|
-
try:
|
268
|
-
key = next(iter(self.__order))
|
269
|
-
except StopIteration:
|
270
|
-
raise KeyError("%s is empty" % type(self).__name__) from None
|
271
|
-
else:
|
272
|
-
return (key, self.pop(key))
|
273
|
-
|
274
|
-
def __update(self, key):
|
275
|
-
try:
|
276
|
-
self.__order.move_to_end(key, last=False)
|
277
|
-
except KeyError:
|
278
|
-
self.__order[key] = None
|
279
|
-
|
280
|
-
|
281
285
|
class RRCache(Cache):
|
282
286
|
"""Random Replacement (RR) cache implementation."""
|
283
287
|
|
284
288
|
def __init__(self, maxsize, choice=random.choice, getsizeof=None):
|
285
289
|
Cache.__init__(self, maxsize, getsizeof)
|
286
290
|
self.__choice = choice
|
291
|
+
self.__index = {}
|
292
|
+
self.__keys = []
|
287
293
|
|
288
294
|
@property
|
289
295
|
def choice(self):
|
290
296
|
"""The `choice` function used by the cache."""
|
291
297
|
return self.__choice
|
292
298
|
|
299
|
+
def __setitem__(self, key, value, cache_setitem=Cache.__setitem__):
|
300
|
+
cache_setitem(self, key, value)
|
301
|
+
if key not in self.__index:
|
302
|
+
self.__index[key] = len(self.__keys)
|
303
|
+
self.__keys.append(key)
|
304
|
+
|
305
|
+
def __delitem__(self, key, cache_delitem=Cache.__delitem__):
|
306
|
+
cache_delitem(self, key)
|
307
|
+
index = self.__index.pop(key)
|
308
|
+
if index != len(self.__keys) - 1:
|
309
|
+
last = self.__keys[-1]
|
310
|
+
self.__keys[index] = last
|
311
|
+
self.__index[last] = index
|
312
|
+
self.__keys.pop()
|
313
|
+
|
293
314
|
def popitem(self):
|
294
315
|
"""Remove and return a random `(key, value)` pair."""
|
295
316
|
try:
|
296
|
-
key = self.__choice(
|
317
|
+
key = self.__choice(self.__keys)
|
297
318
|
except IndexError:
|
298
319
|
raise KeyError("%s is empty" % type(self).__name__) from None
|
299
320
|
else:
|
@@ -636,11 +657,23 @@ _CacheInfo = collections.namedtuple(
|
|
636
657
|
)
|
637
658
|
|
638
659
|
|
639
|
-
def cached(cache, key=keys.hashkey, lock=None, info=False):
|
660
|
+
def cached(cache, key=keys.hashkey, lock=None, condition=None, info=False):
|
640
661
|
"""Decorator to wrap a function with a memoizing callable that saves
|
641
662
|
results in a cache.
|
642
663
|
|
643
664
|
"""
|
665
|
+
from ._cached import _wrapper
|
666
|
+
|
667
|
+
if isinstance(condition, bool):
|
668
|
+
from warnings import warn
|
669
|
+
|
670
|
+
warn(
|
671
|
+
"passing `info` as positional parameter is deprecated",
|
672
|
+
DeprecationWarning,
|
673
|
+
stacklevel=2,
|
674
|
+
)
|
675
|
+
info = condition
|
676
|
+
condition = None
|
644
677
|
|
645
678
|
def decorator(func):
|
646
679
|
if info:
|
@@ -659,80 +692,21 @@ def cached(cache, key=keys.hashkey, lock=None, info=False):
|
|
659
692
|
def make_info(hits, misses):
|
660
693
|
return _CacheInfo(hits, misses, 0, 0)
|
661
694
|
|
662
|
-
|
695
|
+
return _wrapper(func, cache, key, lock, condition, info=make_info)
|
663
696
|
else:
|
664
|
-
|
665
|
-
|
666
|
-
wrapper.cache = cache
|
667
|
-
wrapper.cache_key = key
|
668
|
-
wrapper.cache_lock = lock
|
669
|
-
|
670
|
-
return functools.update_wrapper(wrapper, func)
|
697
|
+
return _wrapper(func, cache, key, lock, condition)
|
671
698
|
|
672
699
|
return decorator
|
673
700
|
|
674
701
|
|
675
|
-
def cachedmethod(cache, key=keys.methodkey, lock=None):
|
702
|
+
def cachedmethod(cache, key=keys.methodkey, lock=None, condition=None):
|
676
703
|
"""Decorator to wrap a class or instance method with a memoizing
|
677
704
|
callable that saves results in a cache.
|
678
705
|
|
679
706
|
"""
|
707
|
+
from ._cachedmethod import _wrapper
|
680
708
|
|
681
709
|
def decorator(method):
|
682
|
-
|
683
|
-
|
684
|
-
def wrapper(self, *args, **kwargs):
|
685
|
-
c = cache(self)
|
686
|
-
if c is None:
|
687
|
-
return method(self, *args, **kwargs)
|
688
|
-
k = key(self, *args, **kwargs)
|
689
|
-
try:
|
690
|
-
return c[k]
|
691
|
-
except KeyError:
|
692
|
-
pass # key not found
|
693
|
-
v = method(self, *args, **kwargs)
|
694
|
-
try:
|
695
|
-
c[k] = v
|
696
|
-
except ValueError:
|
697
|
-
pass # value too large
|
698
|
-
return v
|
699
|
-
|
700
|
-
def clear(self):
|
701
|
-
c = cache(self)
|
702
|
-
if c is not None:
|
703
|
-
c.clear()
|
704
|
-
|
705
|
-
else:
|
706
|
-
|
707
|
-
def wrapper(self, *args, **kwargs):
|
708
|
-
c = cache(self)
|
709
|
-
if c is None:
|
710
|
-
return method(self, *args, **kwargs)
|
711
|
-
k = key(self, *args, **kwargs)
|
712
|
-
try:
|
713
|
-
with lock(self):
|
714
|
-
return c[k]
|
715
|
-
except KeyError:
|
716
|
-
pass # key not found
|
717
|
-
v = method(self, *args, **kwargs)
|
718
|
-
# in case of a race, prefer the item already in the cache
|
719
|
-
try:
|
720
|
-
with lock(self):
|
721
|
-
return c.setdefault(k, v)
|
722
|
-
except ValueError:
|
723
|
-
return v # value too large
|
724
|
-
|
725
|
-
def clear(self):
|
726
|
-
c = cache(self)
|
727
|
-
if c is not None:
|
728
|
-
with lock(self):
|
729
|
-
c.clear()
|
730
|
-
|
731
|
-
wrapper.cache = cache
|
732
|
-
wrapper.cache_key = key
|
733
|
-
wrapper.cache_lock = lock
|
734
|
-
wrapper.cache_clear = clear
|
735
|
-
|
736
|
-
return functools.update_wrapper(wrapper, method)
|
710
|
+
return _wrapper(method, cache, key, lock, condition)
|
737
711
|
|
738
712
|
return decorator
|