ContextThreadPoolExecutor.map() calls len(iterables[0]) to preallocate contexts, which raises TypeError for generators and other unsized iterables, even though the standard ThreadPoolExecutor.map() accepts them. The proposed fix delegates directly to super().map(), relying on the existing submit() override for context propagation.
The override preallocates one context per item using range(len(iterables[0])), assuming the first iterable is sized. This duplicates responsibility already handled by ContextThreadPoolExecutor.submit() and incorrectly rejects valid unsized iterables.
Run the following Python code:
```python
from concurrent.futures import ThreadPoolExecutor
from langchain_core.runnables.config import ContextThreadPoolExecutor
def values():
yield from range(3)
with ContextThreadPoolExecutor(max_workers=2) as executor:
print(list(executor.map(lambda value: value * 2, values())))
```
Observe TypeError: object of type 'generator' has no len().
The method delegates to the standard ThreadPoolExecutor.map(). The standard implementation internally calls self.submit() for each item. Because ContextThreadPoolExecutor overrides submit() to wrap the function with copy_context().run(...), context propagation is preserved automatically. This removes the problematic len() call and restores support for generators, lazy iterables, multiple iterables, and standard map keyword arguments.
Edge Case Audit
This fix relies on the standard library's ThreadPoolExecutor.map() using self.submit() internally. In CPython 3.12 and typical versions this is true, but if a future Python version changes the implementation to bypass dynamic dispatch, context propagation could silently break. Additionally, removing eager preallocation may slightly change timing for context creation but should not affect correctness. If issues arise, rollback to the previous approach and instead guard len() by converting the first iterable to a list only when needed, or use itertools.tee to preserve laziness.