-
Notifications
You must be signed in to change notification settings - Fork 0
Support tuple items in map #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |||||
|
|
||||||
| import asyncio | ||||||
| import logging | ||||||
| from collections.abc import Callable, Iterable | ||||||
| from datetime import timedelta | ||||||
| from typing import Any | ||||||
|
|
||||||
|
|
@@ -74,6 +75,18 @@ def normalize_tasks( | |||||
| return normalized | ||||||
|
|
||||||
|
|
||||||
| def build_map_tasks( | ||||||
| func: Callable[..., Any], | ||||||
| items: Iterable[Any], | ||||||
| args: tuple[Any, ...], | ||||||
| kwargs: dict[str, Any], | ||||||
| ) -> list[Any]: | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The return type is list[Any], which loses information. Consider using list[tuple[Callable[..., Any], tuple[Any, ...], dict[str, Any]]] for better type safety.
Suggested change
|
||||||
| return [ | ||||||
| (func, (*item, *args) if isinstance(item, tuple) else (item, *args), kwargs) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The new logic in build_map_tasks unpacks any tuple item with *item, treating each element as a separate positional argument. This silently breaks callers who previously used map with tuple items that were meant to be passed as a single argument. The old behavior treated item as a single argument regardless of type. Consider documenting this as a breaking change or adding a parameter to control unpacking. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Namedtuples are instances of tuple, so isinstance(item, tuple) is True. Passing a list of namedtuples will unpack each field as separate arguments, which is probably not the intended behavior. Users might expect namedtuples to be treated as single items. Either document this as intentional or change the check to isinstance(item, tuple) and not namedtuple. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If an empty tuple () is passed as an item, *item expands to nothing, so the resulting task will have only the extra *args and kwargs, likely causing TypeError for functions that require at least one positional argument. The previous behavior passed the empty tuple as a single argument. This edge case may cause unexpected errors. |
||||||
| for item in items | ||||||
| ] | ||||||
|
|
||||||
|
|
||||||
| def build_deps( | ||||||
| keys: list[BatchKey], | ||||||
| normalized: list[NormalizedTask], | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -27,6 +27,10 @@ def greet(name: str, greeting: str = "hello") -> str: | |
| return f"{greeting} {name}" | ||
|
|
||
|
|
||
| def add_pair(x: int, y: int) -> int: | ||
| return x + y | ||
|
|
||
|
|
||
| class TestSyncMap: | ||
| def test_map_basic(self, client: Client) -> None: | ||
| refs = client.map(double, [1, 2, 3]) | ||
|
|
@@ -38,6 +42,10 @@ def test_map_with_extra_args(self, client: Client) -> None: | |
| refs = client.map(add, [1, 2, 3], 10) | ||
| assert [r.load() for r in refs] == [11, 12, 13] | ||
|
|
||
| def test_map_with_tuple_items(self, client: Client) -> None: | ||
| refs = client.map(add_pair, [(1, 2), (3, 4)]) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| assert [r.load() for r in refs] == [3, 7] | ||
|
|
||
| def test_map_with_kwargs(self, client: Client) -> None: | ||
| refs = client.map(greet, ["alice", "bob"], greeting="hi") | ||
| assert [r.load() for r in refs] == ["hi alice", "hi bob"] | ||
|
|
@@ -74,6 +82,10 @@ async def test_map_with_extra_args(self, async_client: AsyncClient) -> None: | |
| refs = await async_client.map(add, [1, 2, 3], 10) | ||
| assert [await r.load() for r in refs] == [11, 12, 13] | ||
|
|
||
| async def test_map_with_tuple_items(self, async_client: AsyncClient) -> None: | ||
| refs = await async_client.map(add_pair, [(1, 2), (3, 4)]) | ||
| assert [await r.load() for r in refs] == [3, 7] | ||
|
|
||
| async def test_map_caching(self, async_client: AsyncClient) -> None: | ||
| call_count = 0 | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function should include a docstring explaining its behavior, especially the tuple unpacking logic, to help maintainability.