We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
ScatterInspector does not deselect all on index == None
I've been playing around with the scattertoggle.py example. Maybe I'm missing something, but the ScatterInspector seems to fail to deselect all points when clicking on whitespace on the plot (that is, index == None). Something along the lines of this seems to fix it.

``` python
class PatchedScatterInspector(ScatterInspector):
        def _deselect(self, index=None):
            plot = self.component
            if index:
                super(PatchedScatterInspector, self)._deselect(index)
            else:
                for name in ('index', 'value'):
                    if not hasattr(plot, name):
                        continue
                    md = getattr(plot, name).metadata
                    md[self.selection_metadata_name] = []
                    getattr(plot, name).metadata_changed = True
            return
```

Cheers,

-A


--- 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/tools/scatter_inspector.py`
Content:
```
1 """ Defines the ScatterInspector tool class.
2 """
3 
4 # Enthought library imports
5 from traits.api import Any, Bool, Enum, Event, HasStrictTraits, Str
6 
7 # Local, relative imports
8 from .select_tool import SelectTool
9 
10 HOVER_EVENT = "hover"
11 
12 SELECT_EVENT = "select"
13 
14 DESELECT_EVENT = "deselect"
15 
16 
17 class ScatterInspectorEvent(HasStrictTraits):
18     #: Is it a hover event or a selection event?
19     event_type = Enum([HOVER_EVENT, SELECT_EVENT, DESELECT_EVENT])
20 
21     #: What index was involved?
22     event_index = Any
23 
24 
25 class ScatterInspector(SelectTool):
26     """ A tool for inspecting scatter plots.
27 
28     It writes the index of the point under the cursor to the metadata of the
29     index and value data sources, and allows clicking to select the point.
30     Other components can listen for metadata updates on the data sources.
31 
32     By default, it writes the index of the point under the cursor to the
33     "hover" key in metadata, and the index of a clicked point to "selection".
34     """
35 
36     #: If persistent_hover is False, then a point will be de-hovered as soon as
37     #: the mouse leaves its hit-testing area. If persistent_hover is True, then
38     #: a point does no de-hover until another point get hover focus.
39     persistent_hover = Bool(False)
40 
41     #: The names of the data source metadata for hover and selection events.
42     hover_metadata_name = Str('hover')
43     selection_metadata_name = Str('selections')
44 
45     #: This tool emits events when hover or selection changes
46     inspector_event = Event(ScatterInspectorEvent)
47 
48     # -------------------------------------------------------------------------
49     # Override/configure inherited traits
50     # -------------------------------------------------------------------------
51 
52     #: This tool is not visible
53     visible = False
54 
55     #: This tool does not have a visual representation
56     draw_mode = "none"
57 
58     def normal_mouse_move(self, event):
59         """ Handles the mouse moving when the tool is in the 'normal' state.
60 
61         If the cursor is within **threshold** of a data point, the method
62         writes the index to the plot's data sources' "hover" metadata.
63 
64         This method emits a ScatterInspectorEvent when a new scatter point is
65         hovered over and when the mouse leaves that point.
66         """
67         plot = self.component
68         index = plot.map_index((event.x, event.y), threshold=self.threshold)
69         insp_event = ScatterInspectorEvent(event_type=HOVER_EVENT,
70                                            event_index=index)
71         if index is not None:
72             old = plot.index.metadata.get(self.hover_metadata_name, None)
73             plot.index.metadata[self.hover_metadata_name] = [index]
74             if old != [index]:
75                 self.inspector_event = insp_event
76             if hasattr(plot, "value"):
77                 plot.value.metadata[self.hover_metadata_name] = [index]
78         elif not self.persistent_hover:
79             old = plot.index.metadata.pop(self.hover_metadata_name, None)
80             if old:
81                 self.inspector_event = insp_event
82             if hasattr(plot, "value"):
83                 plot.value.metadata.pop(self.hover_metadata_name, None)
84 
85         return
86 
87     def _get_selection_state(self, event):
88         plot = self.component
89         index = plot.map_index((event.x, event.y), threshold=self.threshold)
90 
91         already_selected = False
92         for name in ('index', 'value'):
93             if not hasattr(plot, name):
94                 continue
95             md = getattr(plot, name).metadata
96             if md is None or self.selection_metadata_name not in md:
97                 continue
98             if index in md[self.selection_metadata_name]:
99                 already_selected = True
100                 break
101         return already_selected, (index is not None)
102 
103     def _get_selection_token(self, event):
104         plot = self.component
105         index = plot.map_index((event.x, event.y), threshold=self.threshold)
106         return index
107 
108     def _deselect(self, index=None):
109         """ Deselects a particular index.  If no index is given, then
110         deselects all points.
111         """
112         plot = self.component
113         insp_event = ScatterInspectorEvent(event_type=DESELECT_EVENT,
114                                            event_index=index)
115         for name in ('index', 'value'):
116             if not hasattr(plot, name):
117                 continue
118             md = getattr(plot, name).metadata
119             if self.selection_metadata_name not in md:
120                 pass
121             elif index in md[self.selection_metadata_name]:
122                 new_list = md[self.selection_metadata_name][:]
123                 new_list.remove(index)
124                 md[self.selection_metadata_name] = new_list
125                 # Only issue 1 event:
126                 if name == 'index':
127                     self.inspector_event = insp_event
128         return
129 
130     def _select(self, index, append=True):
131         plot = self.component
132         insp_event = ScatterInspectorEvent(event_type=SELECT_EVENT,
133                                            event_index=index)
134         for name in ('index', 'value'):
135             if not hasattr(plot, name):
136                 continue
137             md = getattr(plot, name).metadata
138             selection = md.get(self.selection_metadata_name, None)
139 
140             # If no existing selection
141             if selection is None:
142                 md[self.selection_metadata_name] = [index]
143             # check for list-like object supporting append
144             else:
145                 if append:
146                     if index not in md[self.selection_metadata_name]:
147                         new_list = md[self.selection_metadata_name] + [index]
148                         md[self.selection_metadata_name] = new_list
149                         # Manually trigger the metadata_changed event on
150                         # the datasource. Datasources only automatically
151                         # fire notifications when the values inside the
152                         # metadata dict change, but they do not listen
153                         # for further changes on those values.
154                         # DEPRECATED: use self.inspector_event instead:
155                         getattr(plot, name).metadata_changed = True
156                 else:
157                     md[self.selection_metadata_name] = [index]
158 
159             # Test to only issue 1 event per selection, not 1 per axis:
160             if name == 'index':
161                 self.inspector_event = insp_event
162 
163         return
164 
165 
166 # EOF
167
```
--- 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/tools/scatter_inspector.py b/chaco/tools/scatter_inspector.py
--- a/chaco/tools/scatter_inspector.py
+++ b/chaco/tools/scatter_inspector.py
@@ -125,6 +125,11 @@
                 # Only issue 1 event:
                 if name == 'index':
                     self.inspector_event = insp_event
+            elif index is None:
+                md[self.selection_metadata_name] = []
+                # Only issue 1 event:
+                if name == 'index':
+                    self.inspector_event = insp_event
         return
 
     def _select(self, index, append=True):

{"golden_diff": "diff --git a/chaco/tools/scatter_inspector.py b/chaco/tools/scatter_inspector.py\n--- a/chaco/tools/scatter_inspector.py\n+++ b/chaco/tools/scatter_inspector.py\n@@ -125,6 +125,11 @@\n                 # Only issue 1 event:\n                 if name == 'index':\n                     self.inspector_event = insp_event\n+            elif index is None:\n+                md[self.selection_metadata_name] = []\n+                # Only issue 1 event:\n+                if name == 'index':\n+                    self.inspector_event = insp_event\n         return\n \n     def _select(self, index, append=True):\n", "issue": "ScatterInspector does not deselect all on index == None\nI've been playing around with the scattertoggle.py example. Maybe I'm missing something, but the ScatterInspector seems to fail to deselect all points when clicking on whitespace on the plot (that is, index == None). Something along the lines of this seems to fix it.\n\n``` python\nclass PatchedScatterInspector(ScatterInspector):\n        def _deselect(self, index=None):\n            plot = self.component\n            if index:\n                super(PatchedScatterInspector, self)._deselect(index)\n            else:\n                for name in ('index', 'value'):\n                    if not hasattr(plot, name):\n                        continue\n                    md = getattr(plot, name).metadata\n                    md[self.selection_metadata_name] = []\n                    getattr(plot, name).metadata_changed = True\n            return\n```\n\nCheers,\n\n-A\n\n", "before_files": [{"content": "\"\"\" Defines the ScatterInspector tool class.\n\"\"\"\n\n# Enthought library imports\nfrom traits.api import Any, Bool, Enum, Event, HasStrictTraits, Str\n\n# Local, relative imports\nfrom .select_tool import SelectTool\n\nHOVER_EVENT = \"hover\"\n\nSELECT_EVENT = \"select\"\n\nDESELECT_EVENT = \"deselect\"\n\n\nclass ScatterInspectorEvent(HasStrictTraits):\n    #: Is it a hover event or a selection event?\n    event_type = Enum([HOVER_EVENT, SELECT_EVENT, DESELECT_EVENT])\n\n    #: What index was involved?\n    event_index = Any\n\n\nclass ScatterInspector(SelectTool):\n    \"\"\" A tool for inspecting scatter plots.\n\n    It writes the index of the point under the cursor to the metadata of the\n    index and value data sources, and allows clicking to select the point.\n    Other components can listen for metadata updates on the data sources.\n\n    By default, it writes the index of the point under the cursor to the\n    \"hover\" key in metadata, and the index of a clicked point to \"selection\".\n    \"\"\"\n\n    #: If persistent_hover is False, then a point will be de-hovered as soon as\n    #: the mouse leaves its hit-testing area. If persistent_hover is True, then\n    #: a point does no de-hover until another point get hover focus.\n    persistent_hover = Bool(False)\n\n    #: The names of the data source metadata for hover and selection events.\n    hover_metadata_name = Str('hover')\n    selection_metadata_name = Str('selections')\n\n    #: This tool emits events when hover or selection changes\n    inspector_event = Event(ScatterInspectorEvent)\n\n    # -------------------------------------------------------------------------\n    # Override/configure inherited traits\n    # -------------------------------------------------------------------------\n\n    #: This tool is not visible\n    visible = False\n\n    #: This tool does not have a visual representation\n    draw_mode = \"none\"\n\n    def normal_mouse_move(self, event):\n        \"\"\" Handles the mouse moving when the tool is in the 'normal' state.\n\n        If the cursor is within **threshold** of a data point, the method\n        writes the index to the plot's data sources' \"hover\" metadata.\n\n        This method emits a ScatterInspectorEvent when a new scatter point is\n        hovered over and when the mouse leaves that point.\n        \"\"\"\n        plot = self.component\n        index = plot.map_index((event.x, event.y), threshold=self.threshold)\n        insp_event = ScatterInspectorEvent(event_type=HOVER_EVENT,\n                                           event_index=index)\n        if index is not None:\n            old = plot.index.metadata.get(self.hover_metadata_name, None)\n            plot.index.metadata[self.hover_metadata_name] = [index]\n            if old != [index]:\n                self.inspector_event = insp_event\n            if hasattr(plot, \"value\"):\n                plot.value.metadata[self.hover_metadata_name] = [index]\n        elif not self.persistent_hover:\n            old = plot.index.metadata.pop(self.hover_metadata_name, None)\n            if old:\n                self.inspector_event = insp_event\n            if hasattr(plot, \"value\"):\n                plot.value.metadata.pop(self.hover_metadata_name, None)\n\n        return\n\n    def _get_selection_state(self, event):\n        plot = self.component\n        index = plot.map_index((event.x, event.y), threshold=self.threshold)\n\n        already_selected = False\n        for name in ('index', 'value'):\n            if not hasattr(plot, name):\n                continue\n            md = getattr(plot, name).metadata\n            if md is None or self.selection_metadata_name not in md:\n                continue\n            if index in md[self.selection_metadata_name]:\n                already_selected = True\n                break\n        return already_selected, (index is not None)\n\n    def _get_selection_token(self, event):\n        plot = self.component\n        index = plot.map_index((event.x, event.y), threshold=self.threshold)\n        return index\n\n    def _deselect(self, index=None):\n        \"\"\" Deselects a particular index.  If no index is given, then\n        deselects all points.\n        \"\"\"\n        plot = self.component\n        insp_event = ScatterInspectorEvent(event_type=DESELECT_EVENT,\n                                           event_index=index)\n        for name in ('index', 'value'):\n            if not hasattr(plot, name):\n                continue\n            md = getattr(plot, name).metadata\n            if self.selection_metadata_name not in md:\n                pass\n            elif index in md[self.selection_metadata_name]:\n                new_list = md[self.selection_metadata_name][:]\n                new_list.remove(index)\n                md[self.selection_metadata_name] = new_list\n                # Only issue 1 event:\n                if name == 'index':\n                    self.inspector_event = insp_event\n        return\n\n    def _select(self, index, append=True):\n        plot = self.component\n        insp_event = ScatterInspectorEvent(event_type=SELECT_EVENT,\n                                           event_index=index)\n        for name in ('index', 'value'):\n            if not hasattr(plot, name):\n                continue\n            md = getattr(plot, name).metadata\n            selection = md.get(self.selection_metadata_name, None)\n\n            # If no existing selection\n            if selection is None:\n                md[self.selection_metadata_name] = [index]\n            # check for list-like object supporting append\n            else:\n                if append:\n                    if index not in md[self.selection_metadata_name]:\n                        new_list = md[self.selection_metadata_name] + [index]\n                        md[self.selection_metadata_name] = new_list\n                        # Manually trigger the metadata_changed event on\n                        # the datasource. Datasources only automatically\n                        # fire notifications when the values inside the\n                        # metadata dict change, but they do not listen\n                        # for further changes on those values.\n                        # DEPRECATED: use self.inspector_event instead:\n                        getattr(plot, name).metadata_changed = True\n                else:\n                    md[self.selection_metadata_name] = [index]\n\n            # Test to only issue 1 event per selection, not 1 per axis:\n            if name == 'index':\n                self.inspector_event = insp_event\n\n        return\n\n\n# EOF\n", "path": "chaco/tools/scatter_inspector.py"}], "after_files": [{"content": "\"\"\" Defines the ScatterInspector tool class.\n\"\"\"\n\n# Enthought library imports\nfrom traits.api import Any, Bool, Enum, Event, HasStrictTraits, Str\n\n# Local, relative imports\nfrom .select_tool import SelectTool\n\nHOVER_EVENT = \"hover\"\n\nSELECT_EVENT = \"select\"\n\nDESELECT_EVENT = \"deselect\"\n\n\nclass ScatterInspectorEvent(HasStrictTraits):\n    #: Is it a hover event or a selection event?\n    event_type = Enum([HOVER_EVENT, SELECT_EVENT, DESELECT_EVENT])\n\n    #: What index was involved?\n    event_index = Any\n\n\nclass ScatterInspector(SelectTool):\n    \"\"\" A tool for inspecting scatter plots.\n\n    It writes the index of the point under the cursor to the metadata of the\n    index and value data sources, and allows clicking to select the point.\n    Other components can listen for metadata updates on the data sources.\n\n    By default, it writes the index of the point under the cursor to the\n    \"hover\" key in metadata, and the index of a clicked point to \"selection\".\n    \"\"\"\n\n    #: If persistent_hover is False, then a point will be de-hovered as soon as\n    #: the mouse leaves its hit-testing area. If persistent_hover is True, then\n    #: a point does no de-hover until another point get hover focus.\n    persistent_hover = Bool(False)\n\n    #: The names of the data source metadata for hover and selection events.\n    hover_metadata_name = Str('hover')\n    selection_metadata_name = Str('selections')\n\n    #: This tool emits events when hover or selection changes\n    inspector_event = Event(ScatterInspectorEvent)\n\n    # -------------------------------------------------------------------------\n    # Override/configure inherited traits\n    # -------------------------------------------------------------------------\n\n    #: This tool is not visible\n    visible = False\n\n    #: This tool does not have a visual representation\n    draw_mode = \"none\"\n\n    def normal_mouse_move(self, event):\n        \"\"\" Handles the mouse moving when the tool is in the 'normal' state.\n\n        If the cursor is within **threshold** of a data point, the method\n        writes the index to the plot's data sources' \"hover\" metadata.\n\n        This method emits a ScatterInspectorEvent when a new scatter point is\n        hovered over and when the mouse leaves that point.\n        \"\"\"\n        plot = self.component\n        index = plot.map_index((event.x, event.y), threshold=self.threshold)\n        insp_event = ScatterInspectorEvent(event_type=HOVER_EVENT,\n                                           event_index=index)\n        if index is not None:\n            old = plot.index.metadata.get(self.hover_metadata_name, None)\n            plot.index.metadata[self.hover_metadata_name] = [index]\n            if old != [index]:\n                self.inspector_event = insp_event\n            if hasattr(plot, \"value\"):\n                plot.value.metadata[self.hover_metadata_name] = [index]\n        elif not self.persistent_hover:\n            old = plot.index.metadata.pop(self.hover_metadata_name, None)\n            if old:\n                self.inspector_event = insp_event\n            if hasattr(plot, \"value\"):\n                plot.value.metadata.pop(self.hover_metadata_name, None)\n\n        return\n\n    def _get_selection_state(self, event):\n        plot = self.component\n        index = plot.map_index((event.x, event.y), threshold=self.threshold)\n\n        already_selected = False\n        for name in ('index', 'value'):\n            if not hasattr(plot, name):\n                continue\n            md = getattr(plot, name).metadata\n            if md is None or self.selection_metadata_name not in md:\n                continue\n            if index in md[self.selection_metadata_name]:\n                already_selected = True\n                break\n        return already_selected, (index is not None)\n\n    def _get_selection_token(self, event):\n        plot = self.component\n        index = plot.map_index((event.x, event.y), threshold=self.threshold)\n        return index\n\n    def _deselect(self, index=None):\n        \"\"\" Deselects a particular index.  If no index is given, then\n        deselects all points.\n        \"\"\"\n        plot = self.component\n        insp_event = ScatterInspectorEvent(event_type=DESELECT_EVENT,\n                                           event_index=index)\n        for name in ('index', 'value'):\n            if not hasattr(plot, name):\n                continue\n            md = getattr(plot, name).metadata\n            if self.selection_metadata_name not in md:\n                pass\n            elif index in md[self.selection_metadata_name]:\n                new_list = md[self.selection_metadata_name][:]\n                new_list.remove(index)\n                md[self.selection_metadata_name] = new_list\n                # Only issue 1 event:\n                if name == 'index':\n                    self.inspector_event = insp_event\n            elif index is None:\n                md[self.selection_metadata_name] = []\n                # Only issue 1 event:\n                if name == 'index':\n                    self.inspector_event = insp_event\n        return\n\n    def _select(self, index, append=True):\n        plot = self.component\n        insp_event = ScatterInspectorEvent(event_type=SELECT_EVENT,\n                                           event_index=index)\n        for name in ('index', 'value'):\n            if not hasattr(plot, name):\n                continue\n            md = getattr(plot, name).metadata\n            selection = md.get(self.selection_metadata_name, None)\n\n            # If no existing selection\n            if selection is None:\n                md[self.selection_metadata_name] = [index]\n            # check for list-like object supporting append\n            else:\n                if append:\n                    if index not in md[self.selection_metadata_name]:\n                        new_list = md[self.selection_metadata_name] + [index]\n                        md[self.selection_metadata_name] = new_list\n                        # Manually trigger the metadata_changed event on\n                        # the datasource. Datasources only automatically\n                        # fire notifications when the values inside the\n                        # metadata dict change, but they do not listen\n                        # for further changes on those values.\n                        # DEPRECATED: use self.inspector_event instead:\n                        getattr(plot, name).metadata_changed = True\n                else:\n                    md[self.selection_metadata_name] = [index]\n\n            # Test to only issue 1 event per selection, not 1 per axis:\n            if name == 'index':\n                self.inspector_event = insp_event\n\n        return\n\n\n# EOF\n", "path": "chaco/tools/scatter_inspector.py"}]}