Repository: dask/dask

Issue:
`TypeError` when using `from_map` on `enumerate` object
<!-- Please include a self-contained copy-pastable example that generates the issue if possible.

Please be concise with code posted. See guidelines below on how to provide a good bug report:

- Craft Minimal Bug Reports http://matthewrocklin.com/blog/work/2018/02/28/minimal-bug-reports
- Minimal Complete Verifiable Examples https://stackoverflow.com/help/mcve

Bug reports that follow these guidelines are easier to diagnose, and so are often handled much more quickly.
-->

**What happened**:
When attempting to use `from_map` with an `enumerate` object as the input iterable, I run into an uncaught `TypeError` when trying to compute the length of the iterable:

```python
TypeError                                 Traceback (most recent call last)
Input In [1], in <cell line: 10>()
      6     x = t[1]
      8     return pd.Series([x] * size)
---> 10 dd.from_map(func, enumerate(["A", "B"]))

File ~/dask/dask/dataframe/io/io.py:953, in from_map(func, args, meta, divisions, label, token, enforce_metadata, *iterables, **kwargs)
    949     raise ValueError(
    950         f"All elements of `iterables` must be Iterable, got {type(iterable)}"
    951     )
    952 try:
--> 953     lengths.add(len(iterable))
    954 except AttributeError:
    955     iterables[i] = list(iterable)

TypeError: object of type 'enumerate' has no len()

```

**What you expected to happen**:
I would expect this error to be caught, as it seems like the `except` case would've resolved this by creating a list from the `enumerate` object.

**Minimal Complete Verifiable Example**:
This fails:

```python
import pandas as pd
import dask.dataframe as dd

def func(t):
    size = t[0] + 1
    x = t[1]

    return pd.Series([x] * size)

dd.from_map(func, enumerate(["A", "B"]))
```

Creating a list from the `enumerate` object before calling `from_map` works:

```python
import pandas as pd
import dask.dataframe as dd

def func(t):
    size = t[0] + 1
    x = t[1]

    return pd.Series([x] * size)

dd.from_map(func, list(enumerate(["A", "B"])))
```

**Environment**:

- Dask version: latest `main`
- Python version: 3.9
- Operating System: ubuntu 20.04
- Install method (conda, pip, source): source


Discussion:
Looks like a little bug. Do you or @rjzamora have time to open a PR to fix?

Resolving patch:
diff --git a/dask/dataframe/io/io.py b/dask/dataframe/io/io.py
--- a/dask/dataframe/io/io.py
+++ b/dask/dataframe/io/io.py
@@ -944,6 +944,7 @@ def from_map(
     if not callable(func):
         raise ValueError("`func` argument must be `callable`")
     lengths = set()
+    iterables = list(iterables)
     for i, iterable in enumerate(iterables):
         if not isinstance(iterable, Iterable):
             raise ValueError(
@@ -951,7 +952,7 @@ def from_map(
             )
         try:
             lengths.add(len(iterable))
-        except AttributeError:
+        except (AttributeError, TypeError):
             iterables[i] = list(iterable)
             lengths.add(len(iterables[i]))
     if len(lengths) == 0:


Test patch:
diff --git a/dask/dataframe/io/tests/test_io.py b/dask/dataframe/io/tests/test_io.py
--- a/dask/dataframe/io/tests/test_io.py
+++ b/dask/dataframe/io/tests/test_io.py
@@ -956,3 +956,34 @@ def test_from_map_custom_name():
     ddf = dd.from_map(func, iterable, label=label, token=token)
     assert ddf._name == label + "-" + token
     assert_eq(ddf, expect)
+
+
+def _generator():
+    # Simple generator for test_from_map_other_iterables
+    yield from enumerate(["A", "B", "C"])
+
+
+@pytest.mark.parametrize(
+    "iterable",
+    [
+        enumerate(["A", "B", "C"]),
+        ((0, "A"), (1, "B"), (2, "C")),
+        _generator(),
+    ],
+)
+def test_from_map_other_iterables(iterable):
+    # Test that iterable arguments to `from_map`
+    # can be enumerate and generator
+    # See: https://github.com/dask/dask/issues/9064
+
+    def func(t):
+        size = t[0] + 1
+        x = t[1]
+        return pd.Series([x] * size)
+
+    ddf = dd.from_map(func, iterable)
+    expect = pd.Series(
+        ["A", "B", "B", "C", "C", "C"],
+        index=[0, 0, 1, 0, 1, 2],
+    )
+    assert_eq(ddf.compute(), expect)