aws-lambda-powertools 3.12.1a8__py3-none-any.whl → 3.12.1a9__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.
@@ -1,3 +1,3 @@
1
1
  """Exposes version constant to avoid circular dependencies."""
2
2
 
3
- VERSION = "3.12.1a8"
3
+ VERSION = "3.12.1a9"
@@ -0,0 +1,11 @@
1
+ from aws_lambda_powertools.utilities.idempotency.persistence.redis import (
2
+ CacheClientProtocol,
3
+ CacheConnection,
4
+ CachePersistenceLayer,
5
+ )
6
+
7
+ __all__ = [
8
+ "CacheClientProtocol",
9
+ "CachePersistenceLayer",
10
+ "CacheConnection",
11
+ ]
@@ -8,6 +8,7 @@ from datetime import timedelta
8
8
  from typing import Any, Literal, Protocol
9
9
 
10
10
  import redis
11
+ from typing_extensions import TypeAlias, deprecated
11
12
 
12
13
  from aws_lambda_powertools.utilities.idempotency import BasePersistenceLayer
13
14
  from aws_lambda_powertools.utilities.idempotency.exceptions import (
@@ -25,11 +26,12 @@ from aws_lambda_powertools.utilities.idempotency.persistence.base import (
25
26
  logger = logging.getLogger(__name__)
26
27
 
27
28
 
29
+ @deprecated("RedisPersistenceLayer will be removed in v4.0.0. Please use CacheProtocol instead.")
28
30
  class RedisClientProtocol(Protocol):
29
31
  """
30
- Protocol class defining the interface for a Redis client.
32
+ Protocol class defining the interface for a Cache client.
31
33
 
32
- This protocol outlines the expected behavior of a Redis client, allowing for
34
+ This protocol outlines the expected behavior of a Cache client, allowing for
33
35
  standardization among different implementations and allowing customers to extend it
34
36
  in their own implementation.
35
37
 
@@ -78,6 +80,7 @@ class RedisClientProtocol(Protocol):
78
80
  raise NotImplementedError
79
81
 
80
82
 
83
+ @deprecated("RedisConnection will be removed in v4.0.0. Please use CacheConnection instead.")
81
84
  class RedisConnection:
82
85
  def __init__(
83
86
  self,
@@ -85,32 +88,32 @@ class RedisConnection:
85
88
  host: str = "",
86
89
  port: int = 6379,
87
90
  username: str = "",
88
- password: str = "", # nosec - password for Redis connection
91
+ password: str = "", # nosec - password for Cache connection
89
92
  db_index: int = 0,
90
93
  mode: Literal["standalone", "cluster"] = "standalone",
91
94
  ssl: bool = True,
92
95
  ) -> None:
93
96
  """
94
- Initialize Redis connection which will be used in Redis persistence_store to support Idempotency
97
+ Initialize Cache connection which will be used in Cache persistence_store to support Idempotency
95
98
 
96
99
  Parameters
97
100
  ----------
98
101
  host: str, optional
99
- Redis host
102
+ Cache host
100
103
  port: int, optional: default 6379
101
- Redis port
104
+ Cache port
102
105
  username: str, optional
103
- Redis username
106
+ Cache username
104
107
  password: str, optional
105
- Redis password
108
+ Cache password
106
109
  url: str, optional
107
- Redis connection string, using url will override the host/port in the previous parameters
110
+ Cache connection string, using url will override the host/port in the previous parameters
108
111
  db_index: int, optional: default 0
109
- Redis db index
112
+ Cache db index
110
113
  mode: str, Literal["standalone","cluster"]
111
- set Redis client mode, choose from standalone/cluster. The default is standalone
114
+ set Cache client mode, choose from standalone/cluster. The default is standalone
112
115
  ssl: bool, optional: default True
113
- set whether to use ssl for Redis connection
116
+ set whether to use ssl for Cache connection
114
117
 
115
118
  Example
116
119
  --------
@@ -122,13 +125,13 @@ class RedisConnection:
122
125
  from aws_lambda_powertools.utilities.idempotency import (
123
126
  idempotent,
124
127
  )
125
- from aws_lambda_powertools.utilities.idempotency.persistence.redis import (
126
- RedisCachePersistenceLayer,
128
+ from aws_lambda_powertools.utilities.idempotency.persistence.cache import (
129
+ CachePersistenceLayer,
127
130
  )
128
131
 
129
132
  from aws_lambda_powertools.utilities.typing import LambdaContext
130
133
 
131
- persistence_layer = RedisCachePersistenceLayer(host="localhost", port=6379)
134
+ persistence_layer = CachePersistenceLayer(host="localhost", port=6379)
132
135
 
133
136
 
134
137
  @dataclass
@@ -181,15 +184,15 @@ class RedisConnection:
181
184
 
182
185
  try:
183
186
  if self.url:
184
- logger.debug(f"Using URL format to connect to Redis: {self.host}")
187
+ logger.debug(f"Using URL format to connect to Cache: {self.host}")
185
188
  return client.from_url(url=self.url)
186
189
  else:
187
- # Redis in cluster mode doesn't support db parameter
190
+ # Cache in cluster mode doesn't support db parameter
188
191
  extra_param_connection: dict[str, Any] = {}
189
192
  if self.mode != "cluster":
190
193
  extra_param_connection = {"db": self.db_index}
191
194
 
192
- logger.debug(f"Using arguments to connect to Redis: {self.host}")
195
+ logger.debug(f"Using arguments to connect to Cache: {self.host}")
193
196
  return client(
194
197
  host=self.host,
195
198
  port=self.port,
@@ -200,10 +203,11 @@ class RedisConnection:
200
203
  **extra_param_connection,
201
204
  )
202
205
  except redis.exceptions.ConnectionError as exc:
203
- logger.debug(f"Cannot connect in Redis: {self.host}")
204
- raise IdempotencyPersistenceConnectionError("Could not to connect to Redis", exc) from exc
206
+ logger.debug(f"Cannot connect to Cache endpoint: {self.host}")
207
+ raise IdempotencyPersistenceConnectionError("Could not to connect to Cache endpoint", exc) from exc
205
208
 
206
209
 
210
+ @deprecated("RedisCachePersistenceLayer will be removed in v4.0.0. Please use CachePersistenceLayer instead.")
207
211
  class RedisCachePersistenceLayer(BasePersistenceLayer):
208
212
  def __init__(
209
213
  self,
@@ -211,7 +215,7 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
211
215
  host: str = "",
212
216
  port: int = 6379,
213
217
  username: str = "",
214
- password: str = "", # nosec - password for Redis connection
218
+ password: str = "", # nosec - password for Cache connection
215
219
  db_index: int = 0,
216
220
  mode: Literal["standalone", "cluster"] = "standalone",
217
221
  ssl: bool = True,
@@ -223,39 +227,39 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
223
227
  validation_key_attr: str = "validation",
224
228
  ):
225
229
  """
226
- Initialize the Redis Persistence Layer
230
+ Initialize the Cache Persistence Layer
227
231
 
228
232
  Parameters
229
233
  ----------
230
234
  host: str, optional
231
- Redis host
235
+ Cache host
232
236
  port: int, optional: default 6379
233
- Redis port
237
+ Cache port
234
238
  username: str, optional
235
- Redis username
239
+ Cache username
236
240
  password: str, optional
237
- Redis password
241
+ Cache password
238
242
  url: str, optional
239
- Redis connection string, using url will override the host/port in the previous parameters
243
+ Cache connection string, using url will override the host/port in the previous parameters
240
244
  db_index: int, optional: default 0
241
- Redis db index
245
+ Cache db index
242
246
  mode: str, Literal["standalone","cluster"]
243
- set Redis client mode, choose from standalone/cluster
247
+ set Cache client mode, choose from standalone/cluster
244
248
  ssl: bool, optional: default True
245
- set whether to use ssl for Redis connection
246
- client: RedisClientProtocol, optional
247
- Bring your own Redis client that follows RedisClientProtocol.
249
+ set whether to use ssl for Cache connection
250
+ client: CacheClientProtocol, optional
251
+ Bring your own Cache client that follows CacheClientProtocol.
248
252
  If provided, all other connection configuration options will be ignored
249
253
  expiry_attr: str, optional
250
- Redis json attribute name for expiry timestamp, by default "expiration"
254
+ Cache json attribute name for expiry timestamp, by default "expiration"
251
255
  in_progress_expiry_attr: str, optional
252
- Redis json attribute name for in-progress expiry timestamp, by default "in_progress_expiration"
256
+ Cache json attribute name for in-progress expiry timestamp, by default "in_progress_expiration"
253
257
  status_attr: str, optional
254
- Redis json attribute name for status, by default "status"
258
+ Cache json attribute name for status, by default "status"
255
259
  data_attr: str, optional
256
- Redis json attribute name for response data, by default "data"
260
+ Cache json attribute name for response data, by default "data"
257
261
  validation_key_attr: str, optional
258
- Redis json attribute name for hashed representation of the parts of the event used for validation
262
+ Cache json attribute name for hashed representation of the parts of the event used for validation
259
263
 
260
264
  Examples
261
265
  --------
@@ -266,8 +270,8 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
266
270
  idempotent,
267
271
  )
268
272
 
269
- from aws_lambda_powertools.utilities.idempotency.persistence.redis import (
270
- RedisCachePersistenceLayer,
273
+ from aws_lambda_powertools.utilities.idempotency.persistence.cache import (
274
+ CachePersistenceLayer,
271
275
  )
272
276
 
273
277
  client = redis.Redis(
@@ -275,7 +279,7 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
275
279
  port="6379",
276
280
  decode_responses=True,
277
281
  )
278
- persistence_layer = RedisCachePersistenceLayer(client=client)
282
+ persistence_layer = CachePersistenceLayer(client=client)
279
283
 
280
284
  @idempotent(persistence_store=persistence_layer)
281
285
  def lambda_handler(event: dict, context: LambdaContext):
@@ -288,7 +292,7 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
288
292
  ```
289
293
  """
290
294
 
291
- # Initialize Redis client with Redis config if no client is passed in
295
+ # Initialize Cache client with cache config if no client is passed in
292
296
  if client is None:
293
297
  self.client = RedisConnection(
294
298
  host=host,
@@ -330,11 +334,11 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
330
334
  in_progress_expiry_timestamp=in_progress_expiry_timestamp,
331
335
  response_data=str(item.get(self.data_attr)),
332
336
  payload_hash=str(item.get(self.validation_key_attr)),
333
- expiry_timestamp=item.get("expiration", None),
337
+ expiry_timestamp=item.get("expiration"),
334
338
  )
335
339
 
336
340
  def _get_record(self, idempotency_key) -> DataRecord:
337
- # See: https://redis.io/commands/get/
341
+ # See: https://valkey.io/valkey-glide/python/core/#glide.async_commands.CoreCommands.set
338
342
  response = self.client.get(idempotency_key)
339
343
 
340
344
  # key not found
@@ -384,25 +388,25 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
384
388
  # The idempotency key does not exist:
385
389
  # - first time that this invocation key is used
386
390
  # - previous invocation with the same key was deleted due to TTL
387
- # - SET see https://redis.io/commands/set/
391
+ # - SET see https://valkey.io/valkey-glide/python/core/#glide.async_commands.CoreCommands.set
388
392
 
389
- logger.debug(f"Putting record on Redis for idempotency key: {data_record.idempotency_key}")
393
+ logger.debug(f"Putting record on Cache for idempotency key: {data_record.idempotency_key}")
390
394
  encoded_item = self._json_serializer(item["mapping"])
391
395
  ttl = self._get_expiry_second(expiry_timestamp=data_record.expiry_timestamp)
392
396
 
393
- redis_response = self.client.set(name=data_record.idempotency_key, value=encoded_item, ex=ttl, nx=True)
397
+ cache_response = self.client.set(name=data_record.idempotency_key, value=encoded_item, ex=ttl, nx=True)
394
398
 
395
- # If redis_response is True, the Redis SET operation was successful and the idempotency key was not
399
+ # If cache_response is True, the Cache SET operation was successful and the idempotency key was not
396
400
  # previously set. This indicates that we can safely proceed to the handler execution phase.
397
401
  # Most invocations should successfully proceed past this point.
398
- if redis_response:
402
+ if cache_response:
399
403
  return
400
404
 
401
- # If redis_response is None, it indicates an existing record in Redis for the given idempotency key.
405
+ # If cache_response is None, it indicates an existing record in Cache for the given idempotency key.
402
406
  # This could be due to:
403
407
  # - An active idempotency record from a previous invocation that has not yet expired.
404
408
  # - An orphan record where a previous invocation has timed out.
405
- # - An expired idempotency record that has not been deleted by Redis.
409
+ # - An expired idempotency record that has not been deleted by Cache.
406
410
  # In any case, we proceed to retrieve the record for further inspection.
407
411
 
408
412
  idempotency_record = self._get_record(data_record.idempotency_key)
@@ -427,7 +431,7 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
427
431
 
428
432
  # Reaching this point indicates that the idempotency record found is an orphan record. An orphan record is
429
433
  # one that is neither completed nor in-progress within its expected time frame. It may result from a
430
- # previous invocation that has timed out or an expired record that has yet to be cleaned up by Redis.
434
+ # previous invocation that has timed out or an expired record that has yet to be cleaned up by Cache.
431
435
  # We raise an error to handle this exceptional scenario appropriately.
432
436
  raise IdempotencyPersistenceConsistencyError
433
437
 
@@ -435,24 +439,22 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
435
439
  # Handle an orphan record by attempting to acquire a lock, which by default lasts for 10 seconds.
436
440
  # The purpose of acquiring the lock is to prevent race conditions with other processes that might
437
441
  # also be trying to handle the same orphan record. Once the lock is acquired, we set a new value
438
- # for the idempotency record in Redis with the appropriate time-to-live (TTL).
442
+ # for the idempotency record in Cache with the appropriate time-to-live (TTL).
439
443
  with self._acquire_lock(name=item["name"]):
440
444
  self.client.set(name=item["name"], value=encoded_item, ex=ttl)
441
445
 
442
446
  # Not removing the lock here serves as a safeguard against race conditions,
443
447
  # preventing another operation from mistakenly treating this record as an orphan while the
444
448
  # current operation is still in progress.
445
- except (redis.exceptions.RedisError, redis.exceptions.RedisClusterException) as e:
446
- raise e
447
449
  except Exception as e:
448
- logger.debug(f"encountered non-Redis exception: {e}")
449
- raise e
450
+ logger.debug(f"An error occurred: {e}")
451
+ raise
450
452
 
451
453
  @contextmanager
452
454
  def _acquire_lock(self, name: str):
453
455
  """
454
456
  Attempt to acquire a lock for a specified resource name, with a default timeout.
455
- This context manager attempts to set a lock using Redis to prevent concurrent
457
+ This context manager attempts to set a lock using Cache to prevent concurrent
456
458
  access to a resource identified by 'name'. It uses the 'nx' flag to ensure that
457
459
  the lock is only set if it does not already exist, thereby enforcing mutual exclusion.
458
460
  """
@@ -496,9 +498,9 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
496
498
 
497
499
  def _delete_record(self, data_record: DataRecord) -> None:
498
500
  """
499
- Deletes the idempotency record associated with a given DataRecord from Redis.
501
+ Deletes the idempotency record associated with a given DataRecord from Cache.
500
502
  This function is designed to be called after a Lambda handler invocation has completed processing.
501
- It ensures that the idempotency key associated with the DataRecord is removed from Redis to
503
+ It ensures that the idempotency key associated with the DataRecord is removed from Cache to
502
504
  prevent future conflicts and to maintain the idempotency integrity.
503
505
 
504
506
  Note: it is essential that the idempotency key is not empty, as that would indicate the Lambda
@@ -506,5 +508,10 @@ class RedisCachePersistenceLayer(BasePersistenceLayer):
506
508
  """
507
509
  logger.debug(f"Deleting record for idempotency key: {data_record.idempotency_key}")
508
510
 
509
- # See: https://redis.io/commands/del/
511
+ # See: https://valkey.io/valkey-glide/python/core/#glide.async_commands.CoreCommands.delete
510
512
  self.client.delete(data_record.idempotency_key)
513
+
514
+
515
+ CachePersistenceLayer: TypeAlias = RedisCachePersistenceLayer
516
+ CacheClientProtocol: TypeAlias = RedisClientProtocol
517
+ CacheConnection: TypeAlias = RedisConnection
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: aws_lambda_powertools
3
- Version: 3.12.1a8
3
+ Version: 3.12.1a9
4
4
  Summary: Powertools for AWS Lambda (Python) is a developer toolkit to implement Serverless best practices and increase developer velocity.
5
5
  License: MIT
6
6
  Keywords: aws_lambda_powertools,aws,tracing,logging,lambda,powertools,feature_flags,idempotency,middleware
@@ -25,6 +25,7 @@ Provides-Extra: parser
25
25
  Provides-Extra: redis
26
26
  Provides-Extra: tracer
27
27
  Provides-Extra: validation
28
+ Provides-Extra: valkey
28
29
  Requires-Dist: aws-encryption-sdk (>=3.1.1,<5.0.0) ; extra == "all" or extra == "datamasking"
29
30
  Requires-Dist: aws-xray-sdk (>=2.8.0,<3.0.0) ; extra == "tracer" or extra == "all"
30
31
  Requires-Dist: boto3 (>=1.34.32,<2.0.0) ; extra == "aws-sdk"
@@ -36,6 +37,7 @@ Requires-Dist: pydantic (>=2.4.0,<3.0.0) ; extra == "parser" or extra == "all"
36
37
  Requires-Dist: pydantic-settings (>=2.6.1,<3.0.0) ; extra == "all"
37
38
  Requires-Dist: redis (>=4.4,<7.0) ; extra == "redis"
38
39
  Requires-Dist: typing-extensions (>=4.11.0,<5.0.0)
40
+ Requires-Dist: valkey-glide (>=1.3.5,<2.0) ; extra == "valkey"
39
41
  Project-URL: Documentation, https://docs.powertools.aws.dev/lambda/python/
40
42
  Project-URL: Issue tracker, https://github.com/aws-powertools/powertools-lambda-python/issues
41
43
  Project-URL: Repository, https://github.com/aws-powertools/powertools-lambda-python
@@ -97,7 +97,7 @@ aws_lambda_powertools/shared/json_encoder.py,sha256=JQeWNu-4M7_xI_hqYExrxsb3OcEH
97
97
  aws_lambda_powertools/shared/lazy_import.py,sha256=TbXQm2bcwXdZrYdBaJJXIswyLlumM85RJ_A_0w-h-GU,2019
98
98
  aws_lambda_powertools/shared/types.py,sha256=EZ_tbX3F98LA4Zcra1hTEjzRacpZAtggK957Zcv1oKg,135
99
99
  aws_lambda_powertools/shared/user_agent.py,sha256=DrCMFQuT4a4iIrpcWpAIjY37EFqR9-QxlxDGD-Nn9Gg,7081
100
- aws_lambda_powertools/shared/version.py,sha256=bu4rkF3HQmnJsPfy-G3Y4DAkyVwhXd9KxIWdlMLqQo0,85
100
+ aws_lambda_powertools/shared/version.py,sha256=-vsxq2DhrkC6gFTyPBzHdPbaHnfg2-EwEu03tvXjNbQ,85
101
101
  aws_lambda_powertools/tracing/__init__.py,sha256=f4bMThOPBPWTPVcYqcAIErAJPerMsf3H_Z4gCXCsK9I,141
102
102
  aws_lambda_powertools/tracing/base.py,sha256=WSO986XGBOe9K0F2SnG6ustJokIrtO0m0mcL8N7mfno,4544
103
103
  aws_lambda_powertools/tracing/extensions.py,sha256=APOfXOq-hRBKaK5WyfIyrd_6M1_9SWJZ3zxLA9jDZzU,492
@@ -175,9 +175,10 @@ aws_lambda_powertools/utilities/idempotency/hook.py,sha256=XHj6q0dP65C8Cj-TPJ9a8
175
175
  aws_lambda_powertools/utilities/idempotency/idempotency.py,sha256=_t_OmCzL8vBcmrif_9PrAnoCkfBMnG3p2T3sVGHhUqw,7429
176
176
  aws_lambda_powertools/utilities/idempotency/persistence/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
177
177
  aws_lambda_powertools/utilities/idempotency/persistence/base.py,sha256=slYzih7C-tpAUugXdM2DMemNVA_HnxsbtpsvZKiH3gY,16022
178
+ aws_lambda_powertools/utilities/idempotency/persistence/cache.py,sha256=GgGmp-5HtbZoVKUxp70-8rw-lq1d3Fwq9HmGXiINQDk,245
178
179
  aws_lambda_powertools/utilities/idempotency/persistence/datarecord.py,sha256=brtO34whh_ioHkB6GfIjcIUyvzBI7BrEiwQVwFjEVXc,3644
179
180
  aws_lambda_powertools/utilities/idempotency/persistence/dynamodb.py,sha256=H0dm8U1dwNPzAHtmARBoPIq-ajI1ih62bgQFX1RyVxo,13908
180
- aws_lambda_powertools/utilities/idempotency/persistence/redis.py,sha256=KN3XfUZzGwEqkkXZB7OGadYGdf7IiwwChtVzZRuZUQE,21588
181
+ aws_lambda_powertools/utilities/idempotency/persistence/redis.py,sha256=dJZ2afGILfhy-ov2Nwim51s4q7zKyz2C6ZjuaLBwUT4,22126
181
182
  aws_lambda_powertools/utilities/idempotency/serialization/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
182
183
  aws_lambda_powertools/utilities/idempotency/serialization/base.py,sha256=E6ZO7uGOJnhepSkS4NkUbPhw9BNU1581HRmdgEaXe8k,1445
183
184
  aws_lambda_powertools/utilities/idempotency/serialization/custom_dict.py,sha256=QhPDPoa-MHAqo8APjSaQEENPeRiOnTovHWhQgCBkfIg,1018
@@ -268,7 +269,7 @@ aws_lambda_powertools/utilities/validation/envelopes.py,sha256=YD5HOFx6IClQgii0n
268
269
  aws_lambda_powertools/utilities/validation/exceptions.py,sha256=PKy_19zQMBJGCMMFl-sMkcm-cc0v3zZBn_bhGE4wKNo,2084
269
270
  aws_lambda_powertools/utilities/validation/validator.py,sha256=khCqFhACSdn0nKyYRRPiC5Exht956hTfSfhlV3IRmpg,10099
270
271
  aws_lambda_powertools/warnings/__init__.py,sha256=vqDVeZz8wGtD8WGYNSkQE7AHwqtIrPGRxuoJR_BBnSs,1193
271
- aws_lambda_powertools-3.12.1a8.dist-info/LICENSE,sha256=vMHS2eBgmwPUIMPb7LQ4p7ib_FPVQXarVjAasflrTwo,951
272
- aws_lambda_powertools-3.12.1a8.dist-info/METADATA,sha256=kbbxlZPoQd66od3VYsZquoN7rpWOUnWH5X-MaKtdK_A,11187
273
- aws_lambda_powertools-3.12.1a8.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
274
- aws_lambda_powertools-3.12.1a8.dist-info/RECORD,,
272
+ aws_lambda_powertools-3.12.1a9.dist-info/LICENSE,sha256=vMHS2eBgmwPUIMPb7LQ4p7ib_FPVQXarVjAasflrTwo,951
273
+ aws_lambda_powertools-3.12.1a9.dist-info/METADATA,sha256=Hoz_WlCMO7qqUjQ9nK3nDvfM0gE41MiEQmUaEpBWL1I,11273
274
+ aws_lambda_powertools-3.12.1a9.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
275
+ aws_lambda_powertools-3.12.1a9.dist-info/RECORD,,