Repository: dask/dask

Issue:
_enforce_dtype raises an AttributeError when computing dask.array scalars
In a related case to https://github.com/dask/dask/issues/3822, you can get an AttributeError when attempting to compute scalar objects arrays:
```
In [17]: x = da.from_array(np.array(['a', 'b', 'c'], dtype=object), chunks=-1)

In [18]: x[0]
Out[18]: dask.array<getitem, shape=(), dtype=object, chunksize=()>

In [19]: (x[0] == x[0]).compute()
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-19-10cd9c697b59> in <module>()
----> 1 (x[0] == x[0]).compute()

~/dev/dask/dask/base.py in compute(self, **kwargs)
    154         dask.base.compute
    155         """
--> 156         (result,) = compute(self, traverse=False, **kwargs)
    157         return result
    158

~/dev/dask/dask/base.py in compute(*args, **kwargs)
    393     keys = [x.__dask_keys__() for x in collections]
    394     postcomputes = [x.__dask_postcompute__() for x in collections]
--> 395     results = schedule(dsk, keys, **kwargs)
    396     return repack([f(r, *a) for r, (f, a) in zip(results, postcomputes)])
    397

~/dev/dask/dask/threaded.py in get(dsk, result, cache, num_workers, **kwargs)
     73     results = get_async(pool.apply_async, len(pool._pool), dsk, result,
     74                         cache=cache, get_id=_thread_get_id,
---> 75                         pack_exception=pack_exception, **kwargs)
     76
     77     # Cleanup pools associated to dead threads

~/dev/dask/dask/local.py in get_async(apply_async, num_workers, dsk, result, cache, get_id, rerun_exceptions_locally, pack_exception, raise_exception, callbacks, dumps, loads, **kwargs)
    499                         _execute_task(task, data)  # Re-execute locally
    500                     else:
--> 501                         raise_exception(exc, tb)
    502                 res, worker_id = loads(res_info)
    503                 state['cache'][key] = res

~/dev/dask/dask/compatibility.py in reraise(exc, tb)
    110         if exc.__traceback__ is not tb:
    111             raise exc.with_traceback(tb)
--> 112         raise exc
    113
    114 else:

~/dev/dask/dask/local.py in execute_task(key, task_info, dumps, loads, get_id, pack_exception)
    270     try:
    271         task, data = loads(task_info)
--> 272         result = _execute_task(task, data)
    273         id = get_id()
    274         result = dumps((result, id))

~/dev/dask/dask/local.py in _execute_task(arg, cache, dsk)
    251         func, args = arg[0], arg[1:]
    252         args2 = [_execute_task(a, cache) for a in args]
--> 253         return func(*args2)
    254     elif not ishashable(arg):
    255         return arg

~/dev/dask/dask/compatibility.py in apply(func, args, kwargs)
     91     def apply(func, args, kwargs=None):
     92         if kwargs:
---> 93             return func(*args, **kwargs)
     94         else:
     95             return func(*args)

~/dev/dask/dask/array/core.py in _enforce_dtype(*args, **kwargs)
   3553
   3554     result = function(*args, **kwargs)
-> 3555     if dtype != result.dtype and dtype != object:
   3556         if not np.can_cast(result, dtype, casting='same_kind'):
   3557             raise ValueError("Inferred dtype from function %r was %r "

AttributeError: 'bool' object has no attribute 'dtype'
```

This is causing xarray's test suite to fail: https://github.com/pydata/xarray/issues/2318


Discussion:

Resolving patch:
diff --git a/dask/array/core.py b/dask/array/core.py
--- a/dask/array/core.py
+++ b/dask/array/core.py
@@ -3552,7 +3552,7 @@ def _enforce_dtype(*args, **kwargs):
     function = kwargs.pop('enforce_dtype_function')
 
     result = function(*args, **kwargs)
-    if dtype != result.dtype and dtype != object:
+    if hasattr(result, 'dtype') and dtype != result.dtype and dtype != object:
         if not np.can_cast(result, dtype, casting='same_kind'):
             raise ValueError("Inferred dtype from function %r was %r "
                              "but got %r, which can't be cast using "


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
@@ -3644,3 +3644,8 @@ def test_3851():
         da.argmax(Y, axis=0).compute()
 
     assert not record
+
+
+def test_3925():
+    x = da.from_array(np.array(['a', 'b', 'c'], dtype=object), chunks=-1)
+    assert (x[0] == x[0]).compute(scheduler='sync')