1
1
# -*- coding: utf-8 -*-
2
-
3
2
"""
4
3
Batch processing utilities
5
4
"""
5
+ from __future__ import annotations
6
+
6
7
import asyncio
7
8
import copy
8
9
import inspect
11
12
import sys
12
13
from abc import ABC , abstractmethod
13
14
from enum import Enum
14
- from typing import Any , Callable , List , Optional , Tuple , Union , overload
15
+ from typing import TYPE_CHECKING , Any , Callable , Tuple , Union , overload
15
16
16
17
from aws_lambda_powertools .shared import constants
17
18
from aws_lambda_powertools .utilities .batch .exceptions import (
18
19
BatchProcessingError ,
19
20
ExceptionInfo ,
20
21
)
21
- from aws_lambda_powertools .utilities .batch .types import BatchTypeModels , PartialItemFailureResponse , PartialItemFailures
22
+ from aws_lambda_powertools .utilities .batch .types import BatchTypeModels
22
23
from aws_lambda_powertools .utilities .data_classes .dynamo_db_stream_event import (
23
24
DynamoDBRecord ,
24
25
)
25
26
from aws_lambda_powertools .utilities .data_classes .kinesis_stream_event import (
26
27
KinesisStreamRecord ,
27
28
)
28
29
from aws_lambda_powertools .utilities .data_classes .sqs_event import SQSRecord
29
- from aws_lambda_powertools .utilities .typing import LambdaContext
30
+
31
+ if TYPE_CHECKING :
32
+ from aws_lambda_powertools .utilities .batch .types import (
33
+ PartialItemFailureResponse ,
34
+ PartialItemFailures ,
35
+ )
36
+ from aws_lambda_powertools .utilities .typing import LambdaContext
30
37
31
38
logger = logging .getLogger (__name__ )
32
39
@@ -41,7 +48,7 @@ class EventType(Enum):
41
48
# and depending on what EventType it's passed it'll correctly map to the right record
42
49
# When using Pydantic Models, it'll accept any subclass from SQS, DynamoDB and Kinesis
43
50
EventSourceDataClassTypes = Union [SQSRecord , KinesisStreamRecord , DynamoDBRecord ]
44
- BatchEventTypes = Union [EventSourceDataClassTypes , " BatchTypeModels" ]
51
+ BatchEventTypes = Union [EventSourceDataClassTypes , BatchTypeModels ]
45
52
SuccessResponse = Tuple [str , Any , BatchEventTypes ]
46
53
FailureResponse = Tuple [str , str , BatchEventTypes ]
47
54
@@ -54,9 +61,9 @@ class BasePartialProcessor(ABC):
54
61
lambda_context : LambdaContext
55
62
56
63
def __init__ (self ):
57
- self .success_messages : List [BatchEventTypes ] = []
58
- self .fail_messages : List [BatchEventTypes ] = []
59
- self .exceptions : List [ExceptionInfo ] = []
64
+ self .success_messages : list [BatchEventTypes ] = []
65
+ self .fail_messages : list [BatchEventTypes ] = []
66
+ self .exceptions : list [ExceptionInfo ] = []
60
67
61
68
@abstractmethod
62
69
def _prepare (self ):
@@ -79,7 +86,7 @@ def _process_record(self, record: dict):
79
86
"""
80
87
raise NotImplementedError ()
81
88
82
- def process (self ) -> List [ Tuple ]:
89
+ def process (self ) -> list [ tuple ]:
83
90
"""
84
91
Call instance's handler for each record.
85
92
"""
@@ -92,7 +99,7 @@ async def _async_process_record(self, record: dict):
92
99
"""
93
100
raise NotImplementedError ()
94
101
95
- def async_process (self ) -> List [ Tuple ]:
102
+ def async_process (self ) -> list [ tuple ]:
96
103
"""
97
104
Async call instance's handler for each record.
98
105
@@ -135,13 +142,13 @@ def __enter__(self):
135
142
def __exit__ (self , exception_type , exception_value , traceback ):
136
143
self ._clean ()
137
144
138
- def __call__ (self , records : List [dict ], handler : Callable , lambda_context : Optional [ LambdaContext ] = None ):
145
+ def __call__ (self , records : list [dict ], handler : Callable , lambda_context : LambdaContext | None = None ):
139
146
"""
140
147
Set instance attributes before execution
141
148
142
149
Parameters
143
150
----------
144
- records: List [dict]
151
+ records: list [dict]
145
152
List with objects to be processed.
146
153
handler: Callable
147
154
Callable to process "records" entries.
@@ -222,14 +229,14 @@ def failure_handler(self, record, exception: ExceptionInfo) -> FailureResponse:
222
229
class BasePartialBatchProcessor (BasePartialProcessor ): # noqa
223
230
DEFAULT_RESPONSE : PartialItemFailureResponse = {"batchItemFailures" : []}
224
231
225
- def __init__ (self , event_type : EventType , model : Optional [ " BatchTypeModels" ] = None ):
232
+ def __init__ (self , event_type : EventType , model : BatchTypeModels | None = None ):
226
233
"""Process batch and partially report failed items
227
234
228
235
Parameters
229
236
----------
230
237
event_type: EventType
231
238
Whether this is a SQS, DynamoDB Streams, or Kinesis Data Stream event
232
- model: Optional[" BatchTypeModels"]
239
+ model: BatchTypeModels | None
233
240
Parser's data model using either SqsRecordModel, DynamoDBStreamRecordModel, KinesisDataStreamRecord
234
241
235
242
Exceptions
@@ -294,7 +301,7 @@ def _has_messages_to_report(self) -> bool:
294
301
def _entire_batch_failed (self ) -> bool :
295
302
return len (self .exceptions ) == len (self .records )
296
303
297
- def _get_messages_to_report (self ) -> List [PartialItemFailures ]:
304
+ def _get_messages_to_report (self ) -> list [PartialItemFailures ]:
298
305
"""
299
306
Format messages to use in batch deletion
300
307
"""
@@ -343,13 +350,13 @@ def _to_batch_type(
343
350
self ,
344
351
record : dict ,
345
352
event_type : EventType ,
346
- model : " BatchTypeModels" ,
347
- ) -> " BatchTypeModels" : ... # pragma: no cover
353
+ model : BatchTypeModels ,
354
+ ) -> BatchTypeModels : ... # pragma: no cover
348
355
349
356
@overload
350
357
def _to_batch_type (self , record : dict , event_type : EventType ) -> EventSourceDataClassTypes : ... # pragma: no cover
351
358
352
- def _to_batch_type (self , record : dict , event_type : EventType , model : Optional [ " BatchTypeModels" ] = None ):
359
+ def _to_batch_type (self , record : dict , event_type : EventType , model : BatchTypeModels | None = None ):
353
360
if model is not None :
354
361
# If a model is provided, we assume Pydantic is installed and we need to disable v2 warnings
355
362
return model .model_validate (record )
@@ -363,7 +370,7 @@ def _register_model_validation_error_record(self, record: dict):
363
370
# and downstream we can correctly collect the correct message id identifier and make the failed record available
364
371
# see https://github.com/aws-powertools/powertools-lambda-python/issues/2091
365
372
logger .debug ("Record cannot be converted to customer's model; converting without model" )
366
- failed_record : " EventSourceDataClassTypes" = self ._to_batch_type (record = record , event_type = self .event_type )
373
+ failed_record : EventSourceDataClassTypes = self ._to_batch_type (record = record , event_type = self .event_type )
367
374
return self .failure_handler (record = failed_record , exception = sys .exc_info ())
368
375
369
376
@@ -453,7 +460,7 @@ def record_handler(record: DynamoDBRecord):
453
460
logger.info(record.dynamodb.new_image)
454
461
payload: dict = json.loads(record.dynamodb.new_image.get("item"))
455
462
# alternatively:
456
- # changes: Dict [str, Any] = record.dynamodb.new_image # noqa: ERA001
463
+ # changes: dict [str, Any] = record.dynamodb.new_image # noqa: ERA001
457
464
# payload = change.get("Message") -> "<payload>"
458
465
...
459
466
@@ -481,7 +488,7 @@ def lambda_handler(event, context: LambdaContext):
481
488
async def _async_process_record (self , record : dict ):
482
489
raise NotImplementedError ()
483
490
484
- def _process_record (self , record : dict ) -> Union [ SuccessResponse , FailureResponse ] :
491
+ def _process_record (self , record : dict ) -> SuccessResponse | FailureResponse :
485
492
"""
486
493
Process a record with instance's handler
487
494
@@ -490,7 +497,7 @@ def _process_record(self, record: dict) -> Union[SuccessResponse, FailureRespons
490
497
record: dict
491
498
A batch record to be processed.
492
499
"""
493
- data : Optional [ " BatchTypeModels" ] = None
500
+ data : BatchTypeModels | None = None
494
501
try :
495
502
data = self ._to_batch_type (record = record , event_type = self .event_type , model = self .model )
496
503
if self ._handler_accepts_lambda_context :
@@ -602,7 +609,7 @@ async def record_handler(record: DynamoDBRecord):
602
609
logger.info(record.dynamodb.new_image)
603
610
payload: dict = json.loads(record.dynamodb.new_image.get("item"))
604
611
# alternatively:
605
- # changes: Dict [str, Any] = record.dynamodb.new_image # noqa: ERA001
612
+ # changes: dict [str, Any] = record.dynamodb.new_image # noqa: ERA001
606
613
# payload = change.get("Message") -> "<payload>"
607
614
...
608
615
@@ -630,7 +637,7 @@ def lambda_handler(event, context: LambdaContext):
630
637
def _process_record (self , record : dict ):
631
638
raise NotImplementedError ()
632
639
633
- async def _async_process_record (self , record : dict ) -> Union [ SuccessResponse , FailureResponse ] :
640
+ async def _async_process_record (self , record : dict ) -> SuccessResponse | FailureResponse :
634
641
"""
635
642
Process a record with instance's handler
636
643
@@ -639,7 +646,7 @@ async def _async_process_record(self, record: dict) -> Union[SuccessResponse, Fa
639
646
record: dict
640
647
A batch record to be processed.
641
648
"""
642
- data : Optional [ " BatchTypeModels" ] = None
649
+ data : BatchTypeModels | None = None
643
650
try :
644
651
data = self ._to_batch_type (record = record , event_type = self .event_type , model = self .model )
645
652
if self ._handler_accepts_lambda_context :
0 commit comments