Skip to content

Commit c16cd40

Browse files
committed
fix: exit earily on empty list in map and parallel
Fix crash when calling context.map() or context.parallel() with empty list. Previously, empty executables caused ThreadPoolExecutor to be initialized with max_workers=0, raising ValueError. Add early return in ConcurrentExecutor.execute() to immediately return empty BatchResult when no items need processing. This avoids creating ThreadPoolExecutor with invalid worker count and matches expected behavior where empty input produces empty output. Add regression test to verify empty list handling works correctly. closes aws#323
1 parent 9d5ab8d commit c16cd40

File tree

2 files changed

+41
-0
lines changed

2 files changed

+41
-0
lines changed

src/aws_durable_execution_sdk_python/concurrency/executor.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
from aws_durable_execution_sdk_python.lambda_service import ErrorObject
3131
from aws_durable_execution_sdk_python.operation.child import child_handler
3232

33+
3334
if TYPE_CHECKING:
3435
from collections.abc import Callable
3536

@@ -196,6 +197,11 @@ def execute(
196197
"▶️ Executing concurrent operation, items: %d", len(self.executables)
197198
)
198199

200+
# Early return for empty executables
201+
if not self.executables:
202+
logger.debug("No items to execute, returning empty result")
203+
return self._create_result()
204+
199205
max_workers = self.max_concurrency or len(self.executables)
200206

201207
self.executables_with_state = [

tests/operation/map_test.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,3 +1110,38 @@ def create_id(self, i):
11101110
assert parent_call[1]["serdes"] is custom_serdes
11111111
assert isinstance(parent_call[1]["value"], BatchResult)
11121112
assert parent_call[1]["value"] is result
1113+
1114+
1115+
def test_map_with_empty_list_should_exit_early():
1116+
"""Test that map with empty list completes without crashing."""
1117+
items = []
1118+
1119+
def map_func(ctx, item, idx, items):
1120+
return f"processed_{item}"
1121+
1122+
mock_state = Mock()
1123+
mock_state.durable_execution_arn = "arn:test"
1124+
1125+
parent_checkpoint = Mock()
1126+
parent_checkpoint.is_succeeded.return_value = False
1127+
parent_checkpoint.is_failed.return_value = False
1128+
parent_checkpoint.is_existent.return_value = False
1129+
1130+
mock_state.get_checkpoint_result = Mock(return_value=parent_checkpoint)
1131+
mock_state.create_checkpoint = Mock()
1132+
1133+
context = create_test_context(state=mock_state)
1134+
1135+
# This should complete immediately without crashing
1136+
result = context.map(
1137+
items,
1138+
map_func,
1139+
name="EmptyMap",
1140+
)
1141+
1142+
# Should return empty BatchResult
1143+
assert isinstance(result, BatchResult)
1144+
assert len(result.all) == 0
1145+
assert result.total_count == 0
1146+
assert result.success_count == 0
1147+
assert result.failure_count == 0

0 commit comments

Comments
 (0)