We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Check for "ndarray-like" instead of ndarray in _warp; other places
I want to use `rasterio.warp.reproject` on an `xray.Dataset` with `xray.Dataset.apply` (http://xray.readthedocs.org/en/stable/). xray has a feature to turn the dataset into a `np.ndarray`, but that means losing all my metadata.

At https://github.com/mapbox/rasterio/blob/master/rasterio/_warp.pyx#L249, _warp checks that the source is an `np.ndarray` (whereas the source in my case is an `xray.DataArray` - satisfying the same interfaces as `np.ndarray`), so I get an invalid source error.

It could be a good idea to check for something like

```
def is_ndarray_like(source):
    return hasattr(source, '__array__')
```

instead of

```
isinstance(source, np.ndarray)
```

so other numpy-like arrays can be used.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `rasterio/dtypes.py`
Content:
```
1 # Mapping of GDAL to Numpy data types.
2 #
3 # Since 0.13 we are not importing numpy here and data types are strings.
4 # Happily strings can be used throughout Numpy and so existing code will
5 # break.
6 #
7 # Within Rasterio, to test data types, we use Numpy's dtype() factory to 
8 # do something like this:
9 #
10 #   if np.dtype(destination.dtype) == np.dtype(rasterio.uint8): ...
11 #
12 
13 bool_ = 'bool'
14 ubyte = uint8 = 'uint8'
15 uint16 = 'uint16'
16 int16 = 'int16'
17 uint32 = 'uint32'
18 int32 = 'int32'
19 float32 = 'float32'
20 float64 = 'float64'
21 complex_ = 'complex'
22 complex64 = 'complex64'
23 complex128 = 'complex128'
24 
25 # Not supported:
26 #  GDT_CInt16 = 8, GDT_CInt32 = 9, GDT_CFloat32 = 10, GDT_CFloat64 = 11
27 
28 dtype_fwd = {
29     0: None,            # GDT_Unknown
30     1: ubyte,           # GDT_Byte
31     2: uint16,          # GDT_UInt16
32     3: int16,           # GDT_Int16
33     4: uint32,          # GDT_UInt32
34     5: int32,           # GDT_Int32
35     6: float32,         # GDT_Float32
36     7: float64,         # GDT_Float64
37     8: complex_,        # GDT_CInt16
38     9: complex_,        # GDT_CInt32
39     10: complex64,      # GDT_CFloat32
40     11: complex128 }    # GDT_CFloat64
41 
42 dtype_rev = dict((v, k) for k, v in dtype_fwd.items())
43 dtype_rev['uint8'] = 1
44 
45 typename_fwd = {
46     0: 'Unknown',
47     1: 'Byte',
48     2: 'UInt16',
49     3: 'Int16',
50     4: 'UInt32',
51     5: 'Int32',
52     6: 'Float32',
53     7: 'Float64',
54     8: 'CInt16',
55     9: 'CInt32',
56     10: 'CFloat32',
57     11: 'CFloat64' }
58 
59 typename_rev = dict((v, k) for k, v in typename_fwd.items())
60 
61 def _gdal_typename(dt):
62     try:
63         return typename_fwd[dtype_rev[dt]]
64     except KeyError:
65         return typename_fwd[dtype_rev[dt().dtype.name]]
66 
67 def check_dtype(dt):
68     if dt not in dtype_rev:
69         try:
70             return dt().dtype.name in dtype_rev
71         except:
72             return False
73     return True
74 
75 
76 def get_minimum_int_dtype(values):
77     """
78     Uses range checking to determine the minimum integer data type required
79     to represent values.
80 
81     :param values: numpy array
82     :return: named data type that can be later used to create a numpy dtype
83     """
84 
85     min_value = values.min()
86     max_value = values.max()
87     
88     if min_value >= 0:
89         if max_value <= 255:
90             return uint8
91         elif max_value <= 65535:
92             return uint16
93         elif max_value <= 4294967295:
94             return uint32
95     elif min_value >= -32768 and max_value <= 32767:
96         return int16
97     elif min_value >= -2147483648 and max_value <= 2147483647:
98         return int32
99
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/rasterio/dtypes.py b/rasterio/dtypes.py
--- a/rasterio/dtypes.py
+++ b/rasterio/dtypes.py
@@ -96,3 +96,9 @@
         return int16
     elif min_value >= -2147483648 and max_value <= 2147483647:
         return int32
+
+
+def is_ndarray(array):
+    import numpy
+
+    return isinstance(array, numpy.ndarray) or hasattr(array, '__array__')

{"golden_diff": "diff --git a/rasterio/dtypes.py b/rasterio/dtypes.py\n--- a/rasterio/dtypes.py\n+++ b/rasterio/dtypes.py\n@@ -96,3 +96,9 @@\n         return int16\n     elif min_value >= -2147483648 and max_value <= 2147483647:\n         return int32\n+\n+\n+def is_ndarray(array):\n+    import numpy\n+\n+    return isinstance(array, numpy.ndarray) or hasattr(array, '__array__')\n", "issue": "Check for \"ndarray-like\" instead of ndarray in _warp; other places\nI want to use `rasterio.warp.reproject` on an `xray.Dataset` with `xray.Dataset.apply` (http://xray.readthedocs.org/en/stable/). xray has a feature to turn the dataset into a `np.ndarray`, but that means losing all my metadata.\n\nAt https://github.com/mapbox/rasterio/blob/master/rasterio/_warp.pyx#L249, _warp checks that the source is an `np.ndarray` (whereas the source in my case is an `xray.DataArray` - satisfying the same interfaces as `np.ndarray`), so I get an invalid source error.\n\nIt could be a good idea to check for something like\n\n```\ndef is_ndarray_like(source):\n    return hasattr(source, '__array__')\n```\n\ninstead of\n\n```\nisinstance(source, np.ndarray)\n```\n\nso other numpy-like arrays can be used.\n\n", "before_files": [{"content": "# Mapping of GDAL to Numpy data types.\n#\n# Since 0.13 we are not importing numpy here and data types are strings.\n# Happily strings can be used throughout Numpy and so existing code will\n# break.\n#\n# Within Rasterio, to test data types, we use Numpy's dtype() factory to \n# do something like this:\n#\n#   if np.dtype(destination.dtype) == np.dtype(rasterio.uint8): ...\n#\n\nbool_ = 'bool'\nubyte = uint8 = 'uint8'\nuint16 = 'uint16'\nint16 = 'int16'\nuint32 = 'uint32'\nint32 = 'int32'\nfloat32 = 'float32'\nfloat64 = 'float64'\ncomplex_ = 'complex'\ncomplex64 = 'complex64'\ncomplex128 = 'complex128'\n\n# Not supported:\n#  GDT_CInt16 = 8, GDT_CInt32 = 9, GDT_CFloat32 = 10, GDT_CFloat64 = 11\n\ndtype_fwd = {\n    0: None,            # GDT_Unknown\n    1: ubyte,           # GDT_Byte\n    2: uint16,          # GDT_UInt16\n    3: int16,           # GDT_Int16\n    4: uint32,          # GDT_UInt32\n    5: int32,           # GDT_Int32\n    6: float32,         # GDT_Float32\n    7: float64,         # GDT_Float64\n    8: complex_,        # GDT_CInt16\n    9: complex_,        # GDT_CInt32\n    10: complex64,      # GDT_CFloat32\n    11: complex128 }    # GDT_CFloat64\n\ndtype_rev = dict((v, k) for k, v in dtype_fwd.items())\ndtype_rev['uint8'] = 1\n\ntypename_fwd = {\n    0: 'Unknown',\n    1: 'Byte',\n    2: 'UInt16',\n    3: 'Int16',\n    4: 'UInt32',\n    5: 'Int32',\n    6: 'Float32',\n    7: 'Float64',\n    8: 'CInt16',\n    9: 'CInt32',\n    10: 'CFloat32',\n    11: 'CFloat64' }\n\ntypename_rev = dict((v, k) for k, v in typename_fwd.items())\n\ndef _gdal_typename(dt):\n    try:\n        return typename_fwd[dtype_rev[dt]]\n    except KeyError:\n        return typename_fwd[dtype_rev[dt().dtype.name]]\n\ndef check_dtype(dt):\n    if dt not in dtype_rev:\n        try:\n            return dt().dtype.name in dtype_rev\n        except:\n            return False\n    return True\n\n\ndef get_minimum_int_dtype(values):\n    \"\"\"\n    Uses range checking to determine the minimum integer data type required\n    to represent values.\n\n    :param values: numpy array\n    :return: named data type that can be later used to create a numpy dtype\n    \"\"\"\n\n    min_value = values.min()\n    max_value = values.max()\n    \n    if min_value >= 0:\n        if max_value <= 255:\n            return uint8\n        elif max_value <= 65535:\n            return uint16\n        elif max_value <= 4294967295:\n            return uint32\n    elif min_value >= -32768 and max_value <= 32767:\n        return int16\n    elif min_value >= -2147483648 and max_value <= 2147483647:\n        return int32\n", "path": "rasterio/dtypes.py"}], "after_files": [{"content": "# Mapping of GDAL to Numpy data types.\n#\n# Since 0.13 we are not importing numpy here and data types are strings.\n# Happily strings can be used throughout Numpy and so existing code will\n# break.\n#\n# Within Rasterio, to test data types, we use Numpy's dtype() factory to \n# do something like this:\n#\n#   if np.dtype(destination.dtype) == np.dtype(rasterio.uint8): ...\n#\n\nbool_ = 'bool'\nubyte = uint8 = 'uint8'\nuint16 = 'uint16'\nint16 = 'int16'\nuint32 = 'uint32'\nint32 = 'int32'\nfloat32 = 'float32'\nfloat64 = 'float64'\ncomplex_ = 'complex'\ncomplex64 = 'complex64'\ncomplex128 = 'complex128'\n\n# Not supported:\n#  GDT_CInt16 = 8, GDT_CInt32 = 9, GDT_CFloat32 = 10, GDT_CFloat64 = 11\n\ndtype_fwd = {\n    0: None,            # GDT_Unknown\n    1: ubyte,           # GDT_Byte\n    2: uint16,          # GDT_UInt16\n    3: int16,           # GDT_Int16\n    4: uint32,          # GDT_UInt32\n    5: int32,           # GDT_Int32\n    6: float32,         # GDT_Float32\n    7: float64,         # GDT_Float64\n    8: complex_,        # GDT_CInt16\n    9: complex_,        # GDT_CInt32\n    10: complex64,      # GDT_CFloat32\n    11: complex128 }    # GDT_CFloat64\n\ndtype_rev = dict((v, k) for k, v in dtype_fwd.items())\ndtype_rev['uint8'] = 1\n\ntypename_fwd = {\n    0: 'Unknown',\n    1: 'Byte',\n    2: 'UInt16',\n    3: 'Int16',\n    4: 'UInt32',\n    5: 'Int32',\n    6: 'Float32',\n    7: 'Float64',\n    8: 'CInt16',\n    9: 'CInt32',\n    10: 'CFloat32',\n    11: 'CFloat64' }\n\ntypename_rev = dict((v, k) for k, v in typename_fwd.items())\n\ndef _gdal_typename(dt):\n    try:\n        return typename_fwd[dtype_rev[dt]]\n    except KeyError:\n        return typename_fwd[dtype_rev[dt().dtype.name]]\n\ndef check_dtype(dt):\n    if dt not in dtype_rev:\n        try:\n            return dt().dtype.name in dtype_rev\n        except:\n            return False\n    return True\n\n\ndef get_minimum_int_dtype(values):\n    \"\"\"\n    Uses range checking to determine the minimum integer data type required\n    to represent values.\n\n    :param values: numpy array\n    :return: named data type that can be later used to create a numpy dtype\n    \"\"\"\n\n    min_value = values.min()\n    max_value = values.max()\n    \n    if min_value >= 0:\n        if max_value <= 255:\n            return uint8\n        elif max_value <= 65535:\n            return uint16\n        elif max_value <= 4294967295:\n            return uint32\n    elif min_value >= -32768 and max_value <= 32767:\n        return int16\n    elif min_value >= -2147483648 and max_value <= 2147483647:\n        return int32\n\n\ndef is_ndarray(array):\n    import numpy\n\n    return isinstance(array, numpy.ndarray) or hasattr(array, '__array__')\n", "path": "rasterio/dtypes.py"}]}