Repository: dask/dask

Issue:
Simple code is reaching a maximum recursion depth exception
**What happened**:

The same numpy code sample is not working for Dask properly.
It is throwing `maximum recursion depth exceeded in comparison` exception.

**What you expected to happen**: No errors or exceptions after executing the code pasted below.

**Minimal Complete Verifiable Example**:

For further example, I pasted both codes to verify this strange behavior of Dask array module.
You can change the content of `r` variable if necessary to increase the range of `bins` array, but it does not take any effect.

```python
import numpy as np
import dask.array as da

r = 8

x = np.array([0.2, 6.4, 3.0, 1.6])
bins = np.linspace(np.amin(x), np.amax(x)+1, r)
inds = np.digitize(x, bins)

x = da.array([0.2, 6.4, 3.0, 1.6])
bins = da.linspace(da.argmin(x), da.argmax(x)+1, r)
inds = da.digitize(x, bins)
```

**Environment**: Fedora release 34 (Thirty Four) (containerized)

- Dask version: dask-2021.4.1
- Python version: 3.9.2
- Operating System: Fedora 34
- Install method: pip3 and source



Discussion:
Thanks for raising an issue @jcfaracco, I'm able to reproduce. It looks like `dask.array.utils.asarray_safe` can end up resulting in us a recursion error related to NumPy's `__array_function__` (see the full traceback below). 

cc @pentschev if you get a chance to look at this 

```python
RecursionError                            Traceback (most recent call last)
<ipython-input-1-7e3a9236c4dc> in <module>
     10 x = da.array([0.2, 6.4, 3.0, 1.6])
     11 bins = da.linspace(np.argmin(x), np.argmax(x)+1, r)
---> 12 inds = da.digitize(x, bins)

~/projects/dask/dask/dask/array/routines.py in digitize(a, bins, right)
    708 @derived_from(np)
    709 def digitize(a, bins, right=False):
--> 710     bins = asarray_safe(bins, like=meta_from_array(a))
    711     dtype = np.digitize(asarray_safe([0], like=bins), bins, right=False).dtype
    712     return a.map_blocks(np.digitize, dtype=dtype, bins=bins, right=right)

~/projects/dask/dask/dask/array/utils.py in asarray_safe(a, like, **kwargs)
    492     from .core import asarray
    493
--> 494     return _array_like_safe(np.asarray, asarray, a, like, **kwargs)
    495
    496

~/projects/dask/dask/dask/array/utils.py in _array_like_safe(np_func, da_func, a, like, **kwargs)
    460
    461     try:
--> 462         return np_func(a, like=meta_from_array(like), **kwargs)
    463     except TypeError:
    464         return np_func(a, **kwargs)

~/miniforge3/envs/dask/lib/python3.9/site-packages/numpy/core/_asarray.py in asarray(a, dtype, order, like)
     98     """
     99     if like is not None:
--> 100         return _asarray_with_like(a, dtype=dtype, order=order, like=like)
    101
    102     return array(a, dtype, copy=False, order=order)

<__array_function__ internals> in asarray(*args, **kwargs)

~/miniforge3/envs/dask/lib/python3.9/site-packages/numpy/core/_asarray.py in asarray(a, dtype, order, like)
    100         return _asarray_with_like(a, dtype=dtype, order=order, like=like)
    101
--> 102     return array(a, dtype, copy=False, order=order)
    103
    104

~/projects/dask/dask/dask/array/core.py in __array__(self, dtype, **kwargs)
   1504             x = x.astype(dtype)
   1505         if not isinstance(x, np.ndarray):
-> 1506             x = np.array(x)
   1507         return x
   1508

... last 1 frames repeated, from the frame below ...

~/projects/dask/dask/dask/array/core.py in __array__(self, dtype, **kwargs)
   1504             x = x.astype(dtype)
   1505         if not isinstance(x, np.ndarray):
-> 1506             x = np.array(x)
   1507         return x
   1508

RecursionError: maximum recursion depth exceeded in comparison
```
Thank you very much @jrbourbeau. If you need me, just let me know... ;-) 
Thanks for the report @jcfaracco , and @jrbourbeau for the ping.

This seems to be related to `da.linspace` and predates NEP-35 support (added in NumPy 1.20, and Dask 2021.03.0 via https://github.com/dask/dask/pull/6738):

```python
In [1]: import numpy as np, dask, dask.array as da

In [2]: np.__version__
Out[2]: '1.19.5'

In [3]: dask.__version__
Out[3]: '2021.01.1'

In [4]: r = 8
   ...:
   ...: x = np.array([0.2, 6.4, 3.0, 1.6])
   ...: bins = np.linspace(np.amin(x), np.amax(x)+1, r)
   ...: inds = np.digitize(x, bins)
   ...:
   ...: x = da.array([0.2, 6.4, 3.0, 1.6])
   ...: bins = da.linspace(da.argmin(x), da.argmax(x)+1, r)
   ...: inds = da.digitize(x, bins)
---------------------------------------------------------------------------
RecursionError                            Traceback (most recent call last)
<ipython-input-4-a5c9c50bfd0f> in <module>
      7 x = da.array([0.2, 6.4, 3.0, 1.6])
      8 bins = da.linspace(da.argmin(x), da.argmax(x)+1, r)
----> 9 inds = da.digitize(x, bins)

/datasets/pentschev/miniconda3/envs/dask/lib/python3.9/site-packages/dask/array/routines.py in digitize(a, bins, right)
    671 @derived_from(np)
    672 def digitize(a, bins, right=False):
--> 673     bins = np.asarray(bins)
    674     dtype = np.digitize([0], bins, right=False).dtype
    675     return a.map_blocks(np.digitize, dtype=dtype, bins=bins, right=right)

/datasets/pentschev/miniconda3/envs/dask/lib/python3.9/site-packages/numpy/core/_asarray.py in asarray(a, dtype, order)
     81
     82     """
---> 83     return array(a, dtype, copy=False, order=order)
     84
     85

/datasets/pentschev/miniconda3/envs/dask/lib/python3.9/site-packages/dask/array/core.py in __array__(self, dtype, **kwargs)
   1434             x = x.astype(dtype)
   1435         if not isinstance(x, np.ndarray):
-> 1436             x = np.array(x)
   1437         return x
   1438

... last 1 frames repeated, from the frame below ...

/datasets/pentschev/miniconda3/envs/dask/lib/python3.9/site-packages/dask/array/core.py in __array__(self, dtype, **kwargs)
   1434             x = x.astype(dtype)
   1435         if not isinstance(x, np.ndarray):
-> 1436             x = np.array(x)
   1437         return x
   1438

RecursionError: maximum recursion depth exceeded in comparison

In [5]: x = np.array([0.2, 6.4, 3.0, 1.6])
   ...: y = da.array([0.2, 6.4, 3.0, 1.6])
   ...: bins = np.linspace(np.amin(x), np.amax(x)+1, r)
   ...: inds = np.digitize(y, bins)

In [6]: inds
Out[6]: dask.array<digitize, shape=(4,), dtype=int64, chunksize=(4,), chunktype=numpy.ndarray>
```

I could take a look at this, but probably not until after the Dask Summit.

Resolving patch:
diff --git a/dask/array/chunk.py b/dask/array/chunk.py
--- a/dask/array/chunk.py
+++ b/dask/array/chunk.py
@@ -265,6 +265,18 @@ def arange(start, stop, step, length, dtype, like=None):
     return res[:-1] if len(res) > length else res
 
 
+def linspace(start, stop, num, endpoint=True, dtype=None):
+    from .core import Array
+
+    if isinstance(start, Array):
+        start = start.compute()
+
+    if isinstance(stop, Array):
+        stop = stop.compute()
+
+    return np.linspace(start, stop, num, endpoint=endpoint, dtype=dtype)
+
+
 def astype(x, astype_dtype=None, **kwargs):
     return x.astype(astype_dtype, **kwargs)
 
diff --git a/dask/array/creation.py b/dask/array/creation.py
--- a/dask/array/creation.py
+++ b/dask/array/creation.py
@@ -309,7 +309,7 @@ def linspace(
         bs_space = bs - 1 if endpoint else bs
         blockstop = blockstart + (bs_space * step)
         task = (
-            partial(np.linspace, endpoint=endpoint, dtype=dtype),
+            partial(chunk.linspace, endpoint=endpoint, dtype=dtype),
             blockstart,
             blockstop,
             bs,


Test patch:
diff --git a/dask/array/tests/test_creation.py b/dask/array/tests/test_creation.py
--- a/dask/array/tests/test_creation.py
+++ b/dask/array/tests/test_creation.py
@@ -142,6 +142,11 @@ def test_linspace(endpoint):
         da.linspace(6, 49, endpoint=endpoint, chunks=5, dtype=float).dask
     ) == sorted(da.linspace(6, 49, endpoint=endpoint, chunks=5, dtype=float).dask)
 
+    x = da.array([0.2, 6.4, 3.0, 1.6])
+    nparr = np.linspace(0, 2, 8, endpoint=endpoint)
+    darr = da.linspace(da.argmin(x), da.argmax(x) + 1, 8, endpoint=endpoint)
+    assert_eq(darr, nparr)
+
 
 def test_arange():
     darr = da.arange(77, chunks=13)