Repository: dask/dask

Issue:
NumPy gufunc test failure
Currently `test_Array_numpy_gufunc_call__array_ufunc__02` in `dask/array/tests/test_array_core.py` fails on `master` due to a `meta`-related issue (see traceback below). This was discovered in #5220 when going through all our tests marked as `xfail`. I'm opening up a separate issue to track the test failure. 

<details>
<summary>Traceback:</summary>

```
__________________________________ test_Array_numpy_gufunc_call__array_ufunc__02 __________________________________

    @pytest.mark.skipif(
        LooseVersion(np.__version__) < "1.14.0",
        reason="NumPy doesn't have `np.linalg._umath_linalg` yet",
    )
    # @pytest.mark.xfail(
    #     reason="Protect from `np.linalg._umath_linalg.eig` breaking", strict=False
    # )
    def test_Array_numpy_gufunc_call__array_ufunc__02():
        x = da.random.normal(size=(3, 10, 10), chunks=(2, 10, 10))
        nx = x.compute()
        nw, nv = np.linalg._umath_linalg.eig(nx)
>       w, v = np.linalg._umath_linalg.eig(x, output_dtypes=(float, float))

dask/array/tests/test_array_core.py:266:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
dask/array/core.py:1122: in __array_ufunc__
    numpy_ufunc, numpy_ufunc.signature, *inputs, **kwargs
dask/array/gufunc.py:481: in apply_gufunc
    meta = meta_from_array(meta, len(output_shape), dtype=odt)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

x = array([], shape=(0, 0), dtype=complex128), ndim = 2, dtype = <class 'float'>

    def meta_from_array(x, ndim=None, dtype=None):
        """ Normalize an array to appropriate meta object

        Parameters
        ----------
        x: array-like, callable
            Either an object that looks sufficiently like a Numpy array,
            or a callable that accepts shape and dtype keywords
        ndim: int
            Number of dimensions of the array
        dtype: Numpy dtype
            A valid input for ``np.dtype``

        Returns
        -------
        array-like with zero elements of the correct dtype
        """
        # If using x._meta, x must be a Dask Array, some libraries (e.g. zarr)
        # implement a _meta attribute that are incompatible with Dask Array._meta
        if hasattr(x, "_meta") and isinstance(x, Array):
            x = x._meta

        if dtype is None and x is None:
            raise ValueError("You must specify the meta or dtype of the array")

        if np.isscalar(x):
            x = np.array(x)

        if x is None:
            x = np.ndarray

        if isinstance(x, type):
            x = x(shape=(0,) * (ndim or 0), dtype=dtype)

        if (
            not hasattr(x, "shape")
            or not hasattr(x, "dtype")
            or not isinstance(x.shape, tuple)
        ):
            return x

        if isinstance(x, list) or isinstance(x, tuple):
            ndims = [
                0
                if isinstance(a, numbers.Number)
                else a.ndim
                if hasattr(a, "ndim")
                else len(a)
                for a in x
            ]
            a = [a if nd == 0 else meta_from_array(a, nd) for a, nd in zip(x, ndims)]
            return a if isinstance(x, list) else tuple(x)

        if ndim is None:
            ndim = x.ndim

        try:
            meta = x[tuple(slice(0, 0, None) for _ in range(x.ndim))]
            if meta.ndim != ndim:
                if ndim > x.ndim:
                    meta = meta[(Ellipsis,) + tuple(None for _ in range(ndim - meta.ndim))]
                    meta = meta[tuple(slice(0, 0, None) for _ in range(meta.ndim))]
                elif ndim == 0:
                    meta = meta.sum()
                else:
                    meta = meta.reshape((0,) * ndim)
        except Exception:
            meta = np.empty((0,) * ndim, dtype=dtype or x.dtype)

        if np.isscalar(meta):
            meta = np.array(meta)

        if dtype and meta.dtype != dtype:
>           meta = meta.astype(dtype)
E           numpy.ComplexWarning: Casting complex values to real discards the imaginary part

dask/array/utils.py:105: ComplexWarning
```

</details>

cc-ing @pentschev, the `meta` master


Discussion:
Also cc'ing @magonser who wrote most of the GUFunc code

Resolving patch:
diff --git a/dask/array/gufunc.py b/dask/array/gufunc.py
--- a/dask/array/gufunc.py
+++ b/dask/array/gufunc.py
@@ -463,9 +463,7 @@ def apply_gufunc(func, signature, *args, **kwargs):
 
     ## Split output
     leaf_arrs = []
-    for i, (ocd, odt, oax, meta) in enumerate(
-        zip(output_coredimss, output_dtypes, output_axes, metas)
-    ):
+    for i, (ocd, oax, meta) in enumerate(zip(output_coredimss, output_axes, metas)):
         core_output_shape = tuple(core_shapes[d] for d in ocd)
         core_chunkinds = len(ocd) * (0,)
         output_shape = loop_output_shape + core_output_shape
@@ -478,7 +476,7 @@ def apply_gufunc(func, signature, *args, **kwargs):
             for key in keys
         }
         graph = HighLevelGraph.from_collections(leaf_name, leaf_dsk, dependencies=[tmp])
-        meta = meta_from_array(meta, len(output_shape), dtype=odt)
+        meta = meta_from_array(meta, len(output_shape))
         leaf_arr = Array(
             graph, leaf_name, chunks=output_chunks, shape=output_shape, meta=meta
         )


Test patch:
diff --git a/dask/array/tests/test_array_core.py b/dask/array/tests/test_array_core.py
--- a/dask/array/tests/test_array_core.py
+++ b/dask/array/tests/test_array_core.py
@@ -254,7 +254,6 @@ def test_Array_numpy_gufunc_call__array_ufunc__01():
     LooseVersion(np.__version__) < "1.14.0",
     reason="NumPy doesn't have `np.linalg._umath_linalg` yet",
 )
-@pytest.mark.xfail(reason="Protect from `np.linalg._umath_linalg.eig` breaking")
 def test_Array_numpy_gufunc_call__array_ufunc__02():
     x = da.random.normal(size=(3, 10, 10), chunks=(2, 10, 10))
     nx = x.compute()