We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ArrayDataSource get_data() returns scalar 0.0 when _data is None
The `ArrayDataSource` is meant to represent a 1D data structure stored in an array.  For some reason, when no data value has been set, the `get_data()` method returns a scalar `0.0` value.

It would probably make most sense for the value to be `empty(shape=(0,))` (ie. a 1D array of length 0).

Changing this may break existing code which expects empty `ArrayDataSources` to return a scalar 0.  On the flip side, this will potentially simplify the logic for `get_bounds()` and similar.


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `chaco/array_data_source.py`
Content:
```
1 """ Defines the ArrayDataSource class."""
2 
3 # Major library imports
4 from numpy import array, isfinite, ones, ndarray
5 import numpy as np
6 
7 # Enthought library imports
8 from traits.api import Any, Constant, Int, Tuple
9 
10 # Chaco imports
11 from .base import NumericalSequenceTrait, reverse_map_1d, SortOrderTrait
12 from .abstract_data_source import AbstractDataSource
13 
14 
15 def bounded_nanargmin(arr):
16     """Find the index of the minimum value, ignoring NaNs.
17 
18     If all NaNs, return 0.
19     """
20     # Different versions of numpy behave differently in the all-NaN case, so we
21     # catch this condition in two different ways.
22     try:
23         if np.issubdtype(arr.dtype, np.floating):
24             min = np.nanargmin(arr)
25         elif np.issubdtype(arr.dtype, np.number):
26             min = np.argmin(arr)
27         else:
28             min = 0
29     except ValueError:
30         return 0
31     if isfinite(min):
32         return min
33     else:
34         return 0
35 
36 
37 def bounded_nanargmax(arr):
38     """Find the index of the maximum value, ignoring NaNs.
39 
40     If all NaNs, return -1.
41     """
42     try:
43         if np.issubdtype(arr.dtype, np.floating):
44             max = np.nanargmax(arr)
45         elif np.issubdtype(arr.dtype, np.number):
46             max = np.argmax(arr)
47         else:
48             max = -1
49     except ValueError:
50         return -1
51     if isfinite(max):
52         return max
53     else:
54         return -1
55 
56 
57 class ArrayDataSource(AbstractDataSource):
58     """A data source representing a single, continuous array of numerical data.
59 
60     This class does not listen to the array for value changes; if you need that
61     behavior, create a subclass that hooks up the appropriate listeners.
62     """
63 
64     # ------------------------------------------------------------------------
65     # AbstractDataSource traits
66     # ------------------------------------------------------------------------
67 
68     #: The dimensionality of the indices into this data source (overrides
69     #: AbstractDataSource).
70     index_dimension = Constant("scalar")
71 
72     #: The dimensionality of the value at each index point (overrides
73     #: AbstractDataSource).
74     value_dimension = Constant("scalar")
75 
76     #: The sort order of the data.
77     #: This is a specialized optimization for 1-D arrays, but it's an important
78     #: one that's used everywhere.
79     sort_order = SortOrderTrait
80 
81     # ------------------------------------------------------------------------
82     # Private traits
83     # ------------------------------------------------------------------------
84 
85     # The data array itself.
86     _data = NumericalSequenceTrait
87 
88     # Cached values of min and max as long as **_data** doesn't change.
89     _cached_bounds = Tuple
90 
91     # Not necessary, since this is not a filter, but provided for convenience.
92     _cached_mask = Any
93 
94     # The index of the (first) minimum value in self._data
95     # FIXME: This is an Any instead of an Int trait because of how Traits
96     # typechecks numpy.int64 on 64-bit Windows systems.
97     _min_index = Any
98 
99     # The index of the (first) maximum value in self._data
100     # FIXME: This is an Any instead of an Int trait because of how Traits
101     # typechecks numpy.int64 on 64-bit Windows systems.
102     _max_index = Any
103 
104     # ------------------------------------------------------------------------
105     # Public methods
106     # ------------------------------------------------------------------------
107 
108     def __init__(self, data=array([]), sort_order="none", **kw):
109         AbstractDataSource.__init__(self, **kw)
110         self.set_data(data, sort_order)
111 
112     def set_data(self, newdata, sort_order=None):
113         """Sets the data, and optionally the sort order, for this data source.
114 
115         Parameters
116         ----------
117         newdata : array
118             The data to use.
119         sort_order : SortOrderTrait
120             The sort order of the data
121         """
122         self._data = newdata
123         if sort_order is not None:
124             self.sort_order = sort_order
125         self._compute_bounds()
126         self.data_changed = True
127 
128     def set_mask(self, mask):
129         """Sets the mask for this data source."""
130         self._cached_mask = mask
131         self.data_changed = True
132 
133     def remove_mask(self):
134         """Removes the mask on this data source."""
135         self._cached_mask = None
136         self.data_changed = True
137 
138     # ------------------------------------------------------------------------
139     # AbstractDataSource interface
140     # ------------------------------------------------------------------------
141 
142     def get_data(self):
143         """Returns the data for this data source, or 0.0 if it has no data.
144 
145         Implements AbstractDataSource.
146         """
147         if self._data is not None:
148             return self._data
149         else:
150             return 0.0
151 
152     def get_data_mask(self):
153         """get_data_mask() -> (data_array, mask_array)
154 
155         Implements AbstractDataSource.
156         """
157         if self._cached_mask is None:
158             return self._data, ones(len(self._data), dtype=bool)
159         else:
160             return self._data, self._cached_mask
161 
162     def is_masked(self):
163         """is_masked() -> bool
164 
165         Implements AbstractDataSource.
166         """
167         if self._cached_mask is not None:
168             return True
169         else:
170             return False
171 
172     def get_size(self):
173         """get_size() -> int
174 
175         Implements AbstractDataSource.
176         """
177         if self._data is not None:
178             return len(self._data)
179         else:
180             return 0
181 
182     def get_bounds(self):
183         """Returns the minimum and maximum values of the data source's data.
184 
185         Implements AbstractDataSource.
186         """
187         if (
188             self._cached_bounds is None
189             or self._cached_bounds == ()
190             or self._cached_bounds == 0.0
191         ):
192             self._compute_bounds()
193         return self._cached_bounds
194 
195     def reverse_map(self, pt, index=0, outside_returns_none=True):
196         """Returns the index of *pt* in the data source.
197 
198         Parameters
199         ----------
200         pt : scalar value
201             value to find
202         index
203             ignored for data series with 1-D indices
204         outside_returns_none : Boolean
205             Whether the method returns None if *pt* is outside the range of
206             the data source; if False, the method returns the value of the
207             bound that *pt* is outside of.
208         """
209         if self.sort_order == "none":
210             raise NotImplementedError
211 
212         # index is ignored for dataseries with 1-dimensional indices
213         minval, maxval = self._cached_bounds
214         if pt < minval:
215             if outside_returns_none:
216                 return None
217             else:
218                 return self._min_index
219         elif pt > maxval:
220             if outside_returns_none:
221                 return None
222             else:
223                 return self._max_index
224         else:
225             return reverse_map_1d(self._data, pt, self.sort_order)
226 
227     # ------------------------------------------------------------------------
228     # Private methods
229     # ------------------------------------------------------------------------
230 
231     def _compute_bounds(self, data=None):
232         """Computes the minimum and maximum values of self._data.
233 
234         If a data array is passed in, then that is used instead of self._data.
235         This behavior is useful for subclasses.
236         """
237         # TODO: as an optimization, perhaps create and cache a sorted
238         #       version of the dataset?
239 
240         if data is None:
241             # Several sources weren't setting the _data attribute, so we
242             # go through the interface.  This seems like the correct thing
243             # to do anyway... right?
244             # data = self._data
245             data = self.get_data()
246 
247         data_len = 0
248         try:
249             data_len = len(data)
250         except Exception:
251             pass
252         if data_len == 0:
253             self._min_index = 0
254             self._max_index = 0
255             self._cached_bounds = (0.0, 0.0)
256         elif data_len == 1:
257             self._min_index = 0
258             self._max_index = 0
259             self._cached_bounds = (data[0], data[0])
260         else:
261             if self.sort_order == "ascending":
262                 self._min_index = 0
263                 self._max_index = -1
264             elif self.sort_order == "descending":
265                 self._min_index = -1
266                 self._max_index = 0
267             else:
268                 # ignore NaN values.  This is probably a little slower,
269                 # but also much safer.
270 
271                 # data might be an array of strings or objects that
272                 # can't have argmin calculated on them.
273                 try:
274                     # the data may be in a subclass of numpy.array, viewing
275                     # the data as a ndarray will remove side effects of
276                     # the subclasses, such as different operator behaviors
277                     self._min_index = bounded_nanargmin(data.view(ndarray))
278                     self._max_index = bounded_nanargmax(data.view(ndarray))
279                 except (TypeError, IndexError, NotImplementedError):
280                     # For strings and objects, we punt...  These show up in
281                     # label-ish data sources.
282                     self._cached_bounds = (0.0, 0.0)
283 
284             self._cached_bounds = (
285                 data[self._min_index],
286                 data[self._max_index],
287             )
288 
289     # ------------------------------------------------------------------------
290     # Event handlers
291     # ------------------------------------------------------------------------
292 
293     def _metadata_changed(self, event):
294         self.metadata_changed = True
295 
296     def _metadata_items_changed(self, event):
297         self.metadata_changed = True
298 
299     # ------------------------------------------------------------------------
300     # Persistence-related methods
301     # ------------------------------------------------------------------------
302 
303     def __getstate__(self):
304         state = self.__dict__.copy()
305         if not self.persist_data:
306             state.pop("_data", None)
307             state.pop("_cached_mask", None)
308             state.pop("_cached_bounds", None)
309             state.pop("_min_index", None)
310             state.pop("_max_index", None)
311         return state
312 
313     def _post_load(self):
314         super(ArrayDataSource, self)._post_load()
315         self._cached_bounds = ()
316         self._cached_mask = None
317
```
--- 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/chaco/array_data_source.py b/chaco/array_data_source.py
--- a/chaco/array_data_source.py
+++ b/chaco/array_data_source.py
@@ -1,7 +1,7 @@
 """ Defines the ArrayDataSource class."""
 
 # Major library imports
-from numpy import array, isfinite, ones, ndarray
+from numpy import array, empty, isfinite, ones, ndarray
 import numpy as np
 
 # Enthought library imports
@@ -147,7 +147,7 @@
         if self._data is not None:
             return self._data
         else:
-            return 0.0
+            return empty(shape=(0,))
 
     def get_data_mask(self):
         """get_data_mask() -> (data_array, mask_array)
@@ -238,17 +238,10 @@
         #       version of the dataset?
 
         if data is None:
-            # Several sources weren't setting the _data attribute, so we
-            # go through the interface.  This seems like the correct thing
-            # to do anyway... right?
-            # data = self._data
             data = self.get_data()
 
-        data_len = 0
-        try:
-            data_len = len(data)
-        except Exception:
-            pass
+        data_len = len(data)
+
         if data_len == 0:
             self._min_index = 0
             self._max_index = 0

{"golden_diff": "diff --git a/chaco/array_data_source.py b/chaco/array_data_source.py\n--- a/chaco/array_data_source.py\n+++ b/chaco/array_data_source.py\n@@ -1,7 +1,7 @@\n \"\"\" Defines the ArrayDataSource class.\"\"\"\n \n # Major library imports\n-from numpy import array, isfinite, ones, ndarray\n+from numpy import array, empty, isfinite, ones, ndarray\n import numpy as np\n \n # Enthought library imports\n@@ -147,7 +147,7 @@\n         if self._data is not None:\n             return self._data\n         else:\n-            return 0.0\n+            return empty(shape=(0,))\n \n     def get_data_mask(self):\n         \"\"\"get_data_mask() -> (data_array, mask_array)\n@@ -238,17 +238,10 @@\n         #       version of the dataset?\n \n         if data is None:\n-            # Several sources weren't setting the _data attribute, so we\n-            # go through the interface.  This seems like the correct thing\n-            # to do anyway... right?\n-            # data = self._data\n             data = self.get_data()\n \n-        data_len = 0\n-        try:\n-            data_len = len(data)\n-        except Exception:\n-            pass\n+        data_len = len(data)\n+\n         if data_len == 0:\n             self._min_index = 0\n             self._max_index = 0\n", "issue": "ArrayDataSource get_data() returns scalar 0.0 when _data is None\nThe `ArrayDataSource` is meant to represent a 1D data structure stored in an array.  For some reason, when no data value has been set, the `get_data()` method returns a scalar `0.0` value.\n\nIt would probably make most sense for the value to be `empty(shape=(0,))` (ie. a 1D array of length 0).\n\nChanging this may break existing code which expects empty `ArrayDataSources` to return a scalar 0.  On the flip side, this will potentially simplify the logic for `get_bounds()` and similar.\n\n", "before_files": [{"content": "\"\"\" Defines the ArrayDataSource class.\"\"\"\n\n# Major library imports\nfrom numpy import array, isfinite, ones, ndarray\nimport numpy as np\n\n# Enthought library imports\nfrom traits.api import Any, Constant, Int, Tuple\n\n# Chaco imports\nfrom .base import NumericalSequenceTrait, reverse_map_1d, SortOrderTrait\nfrom .abstract_data_source import AbstractDataSource\n\n\ndef bounded_nanargmin(arr):\n    \"\"\"Find the index of the minimum value, ignoring NaNs.\n\n    If all NaNs, return 0.\n    \"\"\"\n    # Different versions of numpy behave differently in the all-NaN case, so we\n    # catch this condition in two different ways.\n    try:\n        if np.issubdtype(arr.dtype, np.floating):\n            min = np.nanargmin(arr)\n        elif np.issubdtype(arr.dtype, np.number):\n            min = np.argmin(arr)\n        else:\n            min = 0\n    except ValueError:\n        return 0\n    if isfinite(min):\n        return min\n    else:\n        return 0\n\n\ndef bounded_nanargmax(arr):\n    \"\"\"Find the index of the maximum value, ignoring NaNs.\n\n    If all NaNs, return -1.\n    \"\"\"\n    try:\n        if np.issubdtype(arr.dtype, np.floating):\n            max = np.nanargmax(arr)\n        elif np.issubdtype(arr.dtype, np.number):\n            max = np.argmax(arr)\n        else:\n            max = -1\n    except ValueError:\n        return -1\n    if isfinite(max):\n        return max\n    else:\n        return -1\n\n\nclass ArrayDataSource(AbstractDataSource):\n    \"\"\"A data source representing a single, continuous array of numerical data.\n\n    This class does not listen to the array for value changes; if you need that\n    behavior, create a subclass that hooks up the appropriate listeners.\n    \"\"\"\n\n    # ------------------------------------------------------------------------\n    # AbstractDataSource traits\n    # ------------------------------------------------------------------------\n\n    #: The dimensionality of the indices into this data source (overrides\n    #: AbstractDataSource).\n    index_dimension = Constant(\"scalar\")\n\n    #: The dimensionality of the value at each index point (overrides\n    #: AbstractDataSource).\n    value_dimension = Constant(\"scalar\")\n\n    #: The sort order of the data.\n    #: This is a specialized optimization for 1-D arrays, but it's an important\n    #: one that's used everywhere.\n    sort_order = SortOrderTrait\n\n    # ------------------------------------------------------------------------\n    # Private traits\n    # ------------------------------------------------------------------------\n\n    # The data array itself.\n    _data = NumericalSequenceTrait\n\n    # Cached values of min and max as long as **_data** doesn't change.\n    _cached_bounds = Tuple\n\n    # Not necessary, since this is not a filter, but provided for convenience.\n    _cached_mask = Any\n\n    # The index of the (first) minimum value in self._data\n    # FIXME: This is an Any instead of an Int trait because of how Traits\n    # typechecks numpy.int64 on 64-bit Windows systems.\n    _min_index = Any\n\n    # The index of the (first) maximum value in self._data\n    # FIXME: This is an Any instead of an Int trait because of how Traits\n    # typechecks numpy.int64 on 64-bit Windows systems.\n    _max_index = Any\n\n    # ------------------------------------------------------------------------\n    # Public methods\n    # ------------------------------------------------------------------------\n\n    def __init__(self, data=array([]), sort_order=\"none\", **kw):\n        AbstractDataSource.__init__(self, **kw)\n        self.set_data(data, sort_order)\n\n    def set_data(self, newdata, sort_order=None):\n        \"\"\"Sets the data, and optionally the sort order, for this data source.\n\n        Parameters\n        ----------\n        newdata : array\n            The data to use.\n        sort_order : SortOrderTrait\n            The sort order of the data\n        \"\"\"\n        self._data = newdata\n        if sort_order is not None:\n            self.sort_order = sort_order\n        self._compute_bounds()\n        self.data_changed = True\n\n    def set_mask(self, mask):\n        \"\"\"Sets the mask for this data source.\"\"\"\n        self._cached_mask = mask\n        self.data_changed = True\n\n    def remove_mask(self):\n        \"\"\"Removes the mask on this data source.\"\"\"\n        self._cached_mask = None\n        self.data_changed = True\n\n    # ------------------------------------------------------------------------\n    # AbstractDataSource interface\n    # ------------------------------------------------------------------------\n\n    def get_data(self):\n        \"\"\"Returns the data for this data source, or 0.0 if it has no data.\n\n        Implements AbstractDataSource.\n        \"\"\"\n        if self._data is not None:\n            return self._data\n        else:\n            return 0.0\n\n    def get_data_mask(self):\n        \"\"\"get_data_mask() -> (data_array, mask_array)\n\n        Implements AbstractDataSource.\n        \"\"\"\n        if self._cached_mask is None:\n            return self._data, ones(len(self._data), dtype=bool)\n        else:\n            return self._data, self._cached_mask\n\n    def is_masked(self):\n        \"\"\"is_masked() -> bool\n\n        Implements AbstractDataSource.\n        \"\"\"\n        if self._cached_mask is not None:\n            return True\n        else:\n            return False\n\n    def get_size(self):\n        \"\"\"get_size() -> int\n\n        Implements AbstractDataSource.\n        \"\"\"\n        if self._data is not None:\n            return len(self._data)\n        else:\n            return 0\n\n    def get_bounds(self):\n        \"\"\"Returns the minimum and maximum values of the data source's data.\n\n        Implements AbstractDataSource.\n        \"\"\"\n        if (\n            self._cached_bounds is None\n            or self._cached_bounds == ()\n            or self._cached_bounds == 0.0\n        ):\n            self._compute_bounds()\n        return self._cached_bounds\n\n    def reverse_map(self, pt, index=0, outside_returns_none=True):\n        \"\"\"Returns the index of *pt* in the data source.\n\n        Parameters\n        ----------\n        pt : scalar value\n            value to find\n        index\n            ignored for data series with 1-D indices\n        outside_returns_none : Boolean\n            Whether the method returns None if *pt* is outside the range of\n            the data source; if False, the method returns the value of the\n            bound that *pt* is outside of.\n        \"\"\"\n        if self.sort_order == \"none\":\n            raise NotImplementedError\n\n        # index is ignored for dataseries with 1-dimensional indices\n        minval, maxval = self._cached_bounds\n        if pt < minval:\n            if outside_returns_none:\n                return None\n            else:\n                return self._min_index\n        elif pt > maxval:\n            if outside_returns_none:\n                return None\n            else:\n                return self._max_index\n        else:\n            return reverse_map_1d(self._data, pt, self.sort_order)\n\n    # ------------------------------------------------------------------------\n    # Private methods\n    # ------------------------------------------------------------------------\n\n    def _compute_bounds(self, data=None):\n        \"\"\"Computes the minimum and maximum values of self._data.\n\n        If a data array is passed in, then that is used instead of self._data.\n        This behavior is useful for subclasses.\n        \"\"\"\n        # TODO: as an optimization, perhaps create and cache a sorted\n        #       version of the dataset?\n\n        if data is None:\n            # Several sources weren't setting the _data attribute, so we\n            # go through the interface.  This seems like the correct thing\n            # to do anyway... right?\n            # data = self._data\n            data = self.get_data()\n\n        data_len = 0\n        try:\n            data_len = len(data)\n        except Exception:\n            pass\n        if data_len == 0:\n            self._min_index = 0\n            self._max_index = 0\n            self._cached_bounds = (0.0, 0.0)\n        elif data_len == 1:\n            self._min_index = 0\n            self._max_index = 0\n            self._cached_bounds = (data[0], data[0])\n        else:\n            if self.sort_order == \"ascending\":\n                self._min_index = 0\n                self._max_index = -1\n            elif self.sort_order == \"descending\":\n                self._min_index = -1\n                self._max_index = 0\n            else:\n                # ignore NaN values.  This is probably a little slower,\n                # but also much safer.\n\n                # data might be an array of strings or objects that\n                # can't have argmin calculated on them.\n                try:\n                    # the data may be in a subclass of numpy.array, viewing\n                    # the data as a ndarray will remove side effects of\n                    # the subclasses, such as different operator behaviors\n                    self._min_index = bounded_nanargmin(data.view(ndarray))\n                    self._max_index = bounded_nanargmax(data.view(ndarray))\n                except (TypeError, IndexError, NotImplementedError):\n                    # For strings and objects, we punt...  These show up in\n                    # label-ish data sources.\n                    self._cached_bounds = (0.0, 0.0)\n\n            self._cached_bounds = (\n                data[self._min_index],\n                data[self._max_index],\n            )\n\n    # ------------------------------------------------------------------------\n    # Event handlers\n    # ------------------------------------------------------------------------\n\n    def _metadata_changed(self, event):\n        self.metadata_changed = True\n\n    def _metadata_items_changed(self, event):\n        self.metadata_changed = True\n\n    # ------------------------------------------------------------------------\n    # Persistence-related methods\n    # ------------------------------------------------------------------------\n\n    def __getstate__(self):\n        state = self.__dict__.copy()\n        if not self.persist_data:\n            state.pop(\"_data\", None)\n            state.pop(\"_cached_mask\", None)\n            state.pop(\"_cached_bounds\", None)\n            state.pop(\"_min_index\", None)\n            state.pop(\"_max_index\", None)\n        return state\n\n    def _post_load(self):\n        super(ArrayDataSource, self)._post_load()\n        self._cached_bounds = ()\n        self._cached_mask = None\n", "path": "chaco/array_data_source.py"}], "after_files": [{"content": "\"\"\" Defines the ArrayDataSource class.\"\"\"\n\n# Major library imports\nfrom numpy import array, empty, isfinite, ones, ndarray\nimport numpy as np\n\n# Enthought library imports\nfrom traits.api import Any, Constant, Int, Tuple\n\n# Chaco imports\nfrom .base import NumericalSequenceTrait, reverse_map_1d, SortOrderTrait\nfrom .abstract_data_source import AbstractDataSource\n\n\ndef bounded_nanargmin(arr):\n    \"\"\"Find the index of the minimum value, ignoring NaNs.\n\n    If all NaNs, return 0.\n    \"\"\"\n    # Different versions of numpy behave differently in the all-NaN case, so we\n    # catch this condition in two different ways.\n    try:\n        if np.issubdtype(arr.dtype, np.floating):\n            min = np.nanargmin(arr)\n        elif np.issubdtype(arr.dtype, np.number):\n            min = np.argmin(arr)\n        else:\n            min = 0\n    except ValueError:\n        return 0\n    if isfinite(min):\n        return min\n    else:\n        return 0\n\n\ndef bounded_nanargmax(arr):\n    \"\"\"Find the index of the maximum value, ignoring NaNs.\n\n    If all NaNs, return -1.\n    \"\"\"\n    try:\n        if np.issubdtype(arr.dtype, np.floating):\n            max = np.nanargmax(arr)\n        elif np.issubdtype(arr.dtype, np.number):\n            max = np.argmax(arr)\n        else:\n            max = -1\n    except ValueError:\n        return -1\n    if isfinite(max):\n        return max\n    else:\n        return -1\n\n\nclass ArrayDataSource(AbstractDataSource):\n    \"\"\"A data source representing a single, continuous array of numerical data.\n\n    This class does not listen to the array for value changes; if you need that\n    behavior, create a subclass that hooks up the appropriate listeners.\n    \"\"\"\n\n    # ------------------------------------------------------------------------\n    # AbstractDataSource traits\n    # ------------------------------------------------------------------------\n\n    #: The dimensionality of the indices into this data source (overrides\n    #: AbstractDataSource).\n    index_dimension = Constant(\"scalar\")\n\n    #: The dimensionality of the value at each index point (overrides\n    #: AbstractDataSource).\n    value_dimension = Constant(\"scalar\")\n\n    #: The sort order of the data.\n    #: This is a specialized optimization for 1-D arrays, but it's an important\n    #: one that's used everywhere.\n    sort_order = SortOrderTrait\n\n    # ------------------------------------------------------------------------\n    # Private traits\n    # ------------------------------------------------------------------------\n\n    # The data array itself.\n    _data = NumericalSequenceTrait\n\n    # Cached values of min and max as long as **_data** doesn't change.\n    _cached_bounds = Tuple\n\n    # Not necessary, since this is not a filter, but provided for convenience.\n    _cached_mask = Any\n\n    # The index of the (first) minimum value in self._data\n    # FIXME: This is an Any instead of an Int trait because of how Traits\n    # typechecks numpy.int64 on 64-bit Windows systems.\n    _min_index = Any\n\n    # The index of the (first) maximum value in self._data\n    # FIXME: This is an Any instead of an Int trait because of how Traits\n    # typechecks numpy.int64 on 64-bit Windows systems.\n    _max_index = Any\n\n    # ------------------------------------------------------------------------\n    # Public methods\n    # ------------------------------------------------------------------------\n\n    def __init__(self, data=array([]), sort_order=\"none\", **kw):\n        AbstractDataSource.__init__(self, **kw)\n        self.set_data(data, sort_order)\n\n    def set_data(self, newdata, sort_order=None):\n        \"\"\"Sets the data, and optionally the sort order, for this data source.\n\n        Parameters\n        ----------\n        newdata : array\n            The data to use.\n        sort_order : SortOrderTrait\n            The sort order of the data\n        \"\"\"\n        self._data = newdata\n        if sort_order is not None:\n            self.sort_order = sort_order\n        self._compute_bounds()\n        self.data_changed = True\n\n    def set_mask(self, mask):\n        \"\"\"Sets the mask for this data source.\"\"\"\n        self._cached_mask = mask\n        self.data_changed = True\n\n    def remove_mask(self):\n        \"\"\"Removes the mask on this data source.\"\"\"\n        self._cached_mask = None\n        self.data_changed = True\n\n    # ------------------------------------------------------------------------\n    # AbstractDataSource interface\n    # ------------------------------------------------------------------------\n\n    def get_data(self):\n        \"\"\"Returns the data for this data source, or 0.0 if it has no data.\n\n        Implements AbstractDataSource.\n        \"\"\"\n        if self._data is not None:\n            return self._data\n        else:\n            return empty(shape=(0,))\n\n    def get_data_mask(self):\n        \"\"\"get_data_mask() -> (data_array, mask_array)\n\n        Implements AbstractDataSource.\n        \"\"\"\n        if self._cached_mask is None:\n            return self._data, ones(len(self._data), dtype=bool)\n        else:\n            return self._data, self._cached_mask\n\n    def is_masked(self):\n        \"\"\"is_masked() -> bool\n\n        Implements AbstractDataSource.\n        \"\"\"\n        if self._cached_mask is not None:\n            return True\n        else:\n            return False\n\n    def get_size(self):\n        \"\"\"get_size() -> int\n\n        Implements AbstractDataSource.\n        \"\"\"\n        if self._data is not None:\n            return len(self._data)\n        else:\n            return 0\n\n    def get_bounds(self):\n        \"\"\"Returns the minimum and maximum values of the data source's data.\n\n        Implements AbstractDataSource.\n        \"\"\"\n        if (\n            self._cached_bounds is None\n            or self._cached_bounds == ()\n            or self._cached_bounds == 0.0\n        ):\n            self._compute_bounds()\n        return self._cached_bounds\n\n    def reverse_map(self, pt, index=0, outside_returns_none=True):\n        \"\"\"Returns the index of *pt* in the data source.\n\n        Parameters\n        ----------\n        pt : scalar value\n            value to find\n        index\n            ignored for data series with 1-D indices\n        outside_returns_none : Boolean\n            Whether the method returns None if *pt* is outside the range of\n            the data source; if False, the method returns the value of the\n            bound that *pt* is outside of.\n        \"\"\"\n        if self.sort_order == \"none\":\n            raise NotImplementedError\n\n        # index is ignored for dataseries with 1-dimensional indices\n        minval, maxval = self._cached_bounds\n        if pt < minval:\n            if outside_returns_none:\n                return None\n            else:\n                return self._min_index\n        elif pt > maxval:\n            if outside_returns_none:\n                return None\n            else:\n                return self._max_index\n        else:\n            return reverse_map_1d(self._data, pt, self.sort_order)\n\n    # ------------------------------------------------------------------------\n    # Private methods\n    # ------------------------------------------------------------------------\n\n    def _compute_bounds(self, data=None):\n        \"\"\"Computes the minimum and maximum values of self._data.\n\n        If a data array is passed in, then that is used instead of self._data.\n        This behavior is useful for subclasses.\n        \"\"\"\n        # TODO: as an optimization, perhaps create and cache a sorted\n        #       version of the dataset?\n\n        if data is None:\n            data = self.get_data()\n\n        data_len = len(data)\n\n        if data_len == 0:\n            self._min_index = 0\n            self._max_index = 0\n            self._cached_bounds = (0.0, 0.0)\n        elif data_len == 1:\n            self._min_index = 0\n            self._max_index = 0\n            self._cached_bounds = (data[0], data[0])\n        else:\n            if self.sort_order == \"ascending\":\n                self._min_index = 0\n                self._max_index = -1\n            elif self.sort_order == \"descending\":\n                self._min_index = -1\n                self._max_index = 0\n            else:\n                # ignore NaN values.  This is probably a little slower,\n                # but also much safer.\n\n                # data might be an array of strings or objects that\n                # can't have argmin calculated on them.\n                try:\n                    # the data may be in a subclass of numpy.array, viewing\n                    # the data as a ndarray will remove side effects of\n                    # the subclasses, such as different operator behaviors\n                    self._min_index = bounded_nanargmin(data.view(ndarray))\n                    self._max_index = bounded_nanargmax(data.view(ndarray))\n                except (TypeError, IndexError, NotImplementedError):\n                    # For strings and objects, we punt...  These show up in\n                    # label-ish data sources.\n                    self._cached_bounds = (0.0, 0.0)\n\n            self._cached_bounds = (\n                data[self._min_index],\n                data[self._max_index],\n            )\n\n    # ------------------------------------------------------------------------\n    # Event handlers\n    # ------------------------------------------------------------------------\n\n    def _metadata_changed(self, event):\n        self.metadata_changed = True\n\n    def _metadata_items_changed(self, event):\n        self.metadata_changed = True\n\n    # ------------------------------------------------------------------------\n    # Persistence-related methods\n    # ------------------------------------------------------------------------\n\n    def __getstate__(self):\n        state = self.__dict__.copy()\n        if not self.persist_data:\n            state.pop(\"_data\", None)\n            state.pop(\"_cached_mask\", None)\n            state.pop(\"_cached_bounds\", None)\n            state.pop(\"_min_index\", None)\n            state.pop(\"_max_index\", None)\n        return state\n\n    def _post_load(self):\n        super(ArrayDataSource, self)._post_load()\n        self._cached_bounds = ()\n        self._cached_mask = None\n", "path": "chaco/array_data_source.py"}]}