We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Replace cKDTree by KDTree
see note here: https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.cKDTree.html#scipy.spatial.cKDTree

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `vpype/line_index.py`
Content:
```
1 from __future__ import annotations
2 
3 import logging
4 from typing import Iterable
5 
6 import numpy as np
7 from scipy.spatial import cKDTree as KDTree
8 
9 # REMINDER: anything added here must be added to docs/api.rst
10 __all__ = ["LineIndex"]
11 
12 
13 class LineIndex:
14     """Wrapper to scipy.spatial.cKDTree to facilitate systematic processing of a line
15     collection.
16 
17     Implementation note: we use the `available` bool array because deleting stuff from the
18     index is costly.
19     """
20 
21     def __init__(self, lines: Iterable[np.ndarray], reverse: bool = False):
22         self.lines = [line for line in lines if len(line) > 0]
23         self.reverse = reverse
24         self._make_index()
25 
26     def _make_index(self) -> None:
27         logging.info(f"LineIndex: creating index for {len(self.lines)} lines")
28         self.available = np.ones(shape=len(self.lines), dtype=bool)
29 
30         # create rtree index
31         self.index = KDTree(
32             np.array([(line[0].real, line[0].imag) for line in self.lines]).reshape(-1, 2)
33         )
34 
35         # create reverse index
36         if self.reverse:
37             self.rindex = KDTree(
38                 np.array([(line[-1].real, line[-1].imag) for line in self.lines]).reshape(
39                     -1, 2
40                 )
41             )
42 
43     def _reindex(self) -> None:
44         self.lines = [line for idx, line in enumerate(self.lines) if self.available[idx]]
45         self._make_index()
46 
47     def __len__(self) -> int:
48         return np.count_nonzero(self.available)
49 
50     def __getitem__(self, item):
51         return self.lines[item]
52 
53     def pop_front(self) -> np.ndarray:
54         if len(self) == 0:
55             raise RuntimeError
56         idx = int(np.argmax(self.available))
57         self.available[idx] = False
58         return self.lines[idx]
59 
60     def pop(self, idx: int) -> np.ndarray | None:
61         if not self.available[idx]:
62             return None
63         self.available[idx] = False
64         return self.lines[idx]
65 
66     def find_nearest_within(self, p: complex, max_dist: float) -> tuple[int | None, bool]:
67         """Find the closest line, assuming a maximum admissible distance.
68         Returns a tuple of (idx, reverse), where `idx` may be None if nothing is found.
69         `reverse` indicates whether or not a line ending has been matched instead of a start.
70         False is always returned if index was created with `reverse=False`.s
71         """
72 
73         ridx = None
74         rdist: float | None = 0.0
75 
76         while True:
77             reindex, idx, dist = self._find_nearest_within_in_index(p, max_dist, self.index)
78             if reindex:
79                 self._reindex()
80                 continue
81 
82             if self.reverse:
83                 reindex, ridx, rdist = self._find_nearest_within_in_index(
84                     p, max_dist, self.rindex
85                 )
86                 if reindex:
87                     self._reindex()
88                     continue
89             break
90 
91         if self.reverse:
92             if idx is None and ridx is None:
93                 return None, False
94             elif idx is not None and ridx is None:
95                 return idx, False
96             elif idx is None and ridx is not None:
97                 return ridx, True
98             elif rdist < dist:  # type: ignore
99                 return ridx, True
100             else:
101                 return idx, False
102         else:
103             return idx, False
104 
105     def _find_nearest_within_in_index(
106         self, p: complex, max_dist: float, index: KDTree
107     ) -> tuple[bool, int | None, float | None]:
108         """Find nearest in specific index. Return (reindex, idx, dist) tuple, where
109         reindex indicates if a reindex is needed.
110         """
111 
112         # For performance reason, we query only a max of k candidates. In the special case
113         # where all distances are not inf and none are available, we might have more than k
114         # suitable candidate, so we reindex and loop. Otherwise, we check the query results
115         # for availability and not inf and return anything found
116         dists, idxs = index.query((p.real, p.imag), k=50, distance_upper_bound=max_dist)
117         dists = np.array(dists)
118 
119         not_inf = ~np.isinf(dists)
120         if np.all(not_inf) and np.all(~self.available[idxs[not_inf]]):
121             return True, None, 0
122 
123         candidates = self.available[idxs[not_inf]]
124 
125         if np.any(candidates):
126             idx = np.argmax(candidates)
127             return False, idxs[not_inf][idx], dists[not_inf][idx]
128         else:
129             return False, None, 0
130 
131     # noinspection PyUnboundLocalVariable
132     def find_nearest(self, p: complex) -> tuple[int, bool]:
133         while True:
134             idx, dist = self._find_nearest_in_index(p, self.index)
135             if self.reverse:
136                 ridx, rdist = self._find_nearest_in_index(p, self.rindex)
137 
138                 if ridx is not None and idx is not None:
139                     break
140             elif idx is not None:
141                 break
142             self._reindex()
143 
144         if self.reverse:
145             if rdist < dist:
146                 return ridx, True  # type: ignore
147             else:
148                 return idx, False
149         else:
150             return idx, False
151 
152     def _find_nearest_in_index(self, p: complex, index: KDTree) -> tuple[int | None, float]:
153         """Check the N nearest lines, hopefully find one that is active."""
154 
155         dists, idxs = index.query((p.real, p.imag), k=100)
156         for dist, idx in zip(dists, idxs):
157             if ~np.isinf(dist) and self.available[idx]:
158                 return idx, dist
159 
160         return None, 0.0
161
```
--- 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/vpype/line_index.py b/vpype/line_index.py
--- a/vpype/line_index.py
+++ b/vpype/line_index.py
@@ -4,14 +4,14 @@
 from typing import Iterable
 
 import numpy as np
-from scipy.spatial import cKDTree as KDTree
+from scipy.spatial import KDTree
 
 # REMINDER: anything added here must be added to docs/api.rst
 __all__ = ["LineIndex"]
 
 
 class LineIndex:
-    """Wrapper to scipy.spatial.cKDTree to facilitate systematic processing of a line
+    """Wrapper to scipy.spatial.KDTree to facilitate systematic processing of a line
     collection.
 
     Implementation note: we use the `available` bool array because deleting stuff from the

{"golden_diff": "diff --git a/vpype/line_index.py b/vpype/line_index.py\n--- a/vpype/line_index.py\n+++ b/vpype/line_index.py\n@@ -4,14 +4,14 @@\n from typing import Iterable\n \n import numpy as np\n-from scipy.spatial import cKDTree as KDTree\n+from scipy.spatial import KDTree\n \n # REMINDER: anything added here must be added to docs/api.rst\n __all__ = [\"LineIndex\"]\n \n \n class LineIndex:\n-    \"\"\"Wrapper to scipy.spatial.cKDTree to facilitate systematic processing of a line\n+    \"\"\"Wrapper to scipy.spatial.KDTree to facilitate systematic processing of a line\n     collection.\n \n     Implementation note: we use the `available` bool array because deleting stuff from the\n", "issue": "Replace cKDTree by KDTree\nsee note here: https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.cKDTree.html#scipy.spatial.cKDTree\n", "before_files": [{"content": "from __future__ import annotations\n\nimport logging\nfrom typing import Iterable\n\nimport numpy as np\nfrom scipy.spatial import cKDTree as KDTree\n\n# REMINDER: anything added here must be added to docs/api.rst\n__all__ = [\"LineIndex\"]\n\n\nclass LineIndex:\n    \"\"\"Wrapper to scipy.spatial.cKDTree to facilitate systematic processing of a line\n    collection.\n\n    Implementation note: we use the `available` bool array because deleting stuff from the\n    index is costly.\n    \"\"\"\n\n    def __init__(self, lines: Iterable[np.ndarray], reverse: bool = False):\n        self.lines = [line for line in lines if len(line) > 0]\n        self.reverse = reverse\n        self._make_index()\n\n    def _make_index(self) -> None:\n        logging.info(f\"LineIndex: creating index for {len(self.lines)} lines\")\n        self.available = np.ones(shape=len(self.lines), dtype=bool)\n\n        # create rtree index\n        self.index = KDTree(\n            np.array([(line[0].real, line[0].imag) for line in self.lines]).reshape(-1, 2)\n        )\n\n        # create reverse index\n        if self.reverse:\n            self.rindex = KDTree(\n                np.array([(line[-1].real, line[-1].imag) for line in self.lines]).reshape(\n                    -1, 2\n                )\n            )\n\n    def _reindex(self) -> None:\n        self.lines = [line for idx, line in enumerate(self.lines) if self.available[idx]]\n        self._make_index()\n\n    def __len__(self) -> int:\n        return np.count_nonzero(self.available)\n\n    def __getitem__(self, item):\n        return self.lines[item]\n\n    def pop_front(self) -> np.ndarray:\n        if len(self) == 0:\n            raise RuntimeError\n        idx = int(np.argmax(self.available))\n        self.available[idx] = False\n        return self.lines[idx]\n\n    def pop(self, idx: int) -> np.ndarray | None:\n        if not self.available[idx]:\n            return None\n        self.available[idx] = False\n        return self.lines[idx]\n\n    def find_nearest_within(self, p: complex, max_dist: float) -> tuple[int | None, bool]:\n        \"\"\"Find the closest line, assuming a maximum admissible distance.\n        Returns a tuple of (idx, reverse), where `idx` may be None if nothing is found.\n        `reverse` indicates whether or not a line ending has been matched instead of a start.\n        False is always returned if index was created with `reverse=False`.s\n        \"\"\"\n\n        ridx = None\n        rdist: float | None = 0.0\n\n        while True:\n            reindex, idx, dist = self._find_nearest_within_in_index(p, max_dist, self.index)\n            if reindex:\n                self._reindex()\n                continue\n\n            if self.reverse:\n                reindex, ridx, rdist = self._find_nearest_within_in_index(\n                    p, max_dist, self.rindex\n                )\n                if reindex:\n                    self._reindex()\n                    continue\n            break\n\n        if self.reverse:\n            if idx is None and ridx is None:\n                return None, False\n            elif idx is not None and ridx is None:\n                return idx, False\n            elif idx is None and ridx is not None:\n                return ridx, True\n            elif rdist < dist:  # type: ignore\n                return ridx, True\n            else:\n                return idx, False\n        else:\n            return idx, False\n\n    def _find_nearest_within_in_index(\n        self, p: complex, max_dist: float, index: KDTree\n    ) -> tuple[bool, int | None, float | None]:\n        \"\"\"Find nearest in specific index. Return (reindex, idx, dist) tuple, where\n        reindex indicates if a reindex is needed.\n        \"\"\"\n\n        # For performance reason, we query only a max of k candidates. In the special case\n        # where all distances are not inf and none are available, we might have more than k\n        # suitable candidate, so we reindex and loop. Otherwise, we check the query results\n        # for availability and not inf and return anything found\n        dists, idxs = index.query((p.real, p.imag), k=50, distance_upper_bound=max_dist)\n        dists = np.array(dists)\n\n        not_inf = ~np.isinf(dists)\n        if np.all(not_inf) and np.all(~self.available[idxs[not_inf]]):\n            return True, None, 0\n\n        candidates = self.available[idxs[not_inf]]\n\n        if np.any(candidates):\n            idx = np.argmax(candidates)\n            return False, idxs[not_inf][idx], dists[not_inf][idx]\n        else:\n            return False, None, 0\n\n    # noinspection PyUnboundLocalVariable\n    def find_nearest(self, p: complex) -> tuple[int, bool]:\n        while True:\n            idx, dist = self._find_nearest_in_index(p, self.index)\n            if self.reverse:\n                ridx, rdist = self._find_nearest_in_index(p, self.rindex)\n\n                if ridx is not None and idx is not None:\n                    break\n            elif idx is not None:\n                break\n            self._reindex()\n\n        if self.reverse:\n            if rdist < dist:\n                return ridx, True  # type: ignore\n            else:\n                return idx, False\n        else:\n            return idx, False\n\n    def _find_nearest_in_index(self, p: complex, index: KDTree) -> tuple[int | None, float]:\n        \"\"\"Check the N nearest lines, hopefully find one that is active.\"\"\"\n\n        dists, idxs = index.query((p.real, p.imag), k=100)\n        for dist, idx in zip(dists, idxs):\n            if ~np.isinf(dist) and self.available[idx]:\n                return idx, dist\n\n        return None, 0.0\n", "path": "vpype/line_index.py"}], "after_files": [{"content": "from __future__ import annotations\n\nimport logging\nfrom typing import Iterable\n\nimport numpy as np\nfrom scipy.spatial import KDTree\n\n# REMINDER: anything added here must be added to docs/api.rst\n__all__ = [\"LineIndex\"]\n\n\nclass LineIndex:\n    \"\"\"Wrapper to scipy.spatial.KDTree to facilitate systematic processing of a line\n    collection.\n\n    Implementation note: we use the `available` bool array because deleting stuff from the\n    index is costly.\n    \"\"\"\n\n    def __init__(self, lines: Iterable[np.ndarray], reverse: bool = False):\n        self.lines = [line for line in lines if len(line) > 0]\n        self.reverse = reverse\n        self._make_index()\n\n    def _make_index(self) -> None:\n        logging.info(f\"LineIndex: creating index for {len(self.lines)} lines\")\n        self.available = np.ones(shape=len(self.lines), dtype=bool)\n\n        # create rtree index\n        self.index = KDTree(\n            np.array([(line[0].real, line[0].imag) for line in self.lines]).reshape(-1, 2)\n        )\n\n        # create reverse index\n        if self.reverse:\n            self.rindex = KDTree(\n                np.array([(line[-1].real, line[-1].imag) for line in self.lines]).reshape(\n                    -1, 2\n                )\n            )\n\n    def _reindex(self) -> None:\n        self.lines = [line for idx, line in enumerate(self.lines) if self.available[idx]]\n        self._make_index()\n\n    def __len__(self) -> int:\n        return np.count_nonzero(self.available)\n\n    def __getitem__(self, item):\n        return self.lines[item]\n\n    def pop_front(self) -> np.ndarray:\n        if len(self) == 0:\n            raise RuntimeError\n        idx = int(np.argmax(self.available))\n        self.available[idx] = False\n        return self.lines[idx]\n\n    def pop(self, idx: int) -> np.ndarray | None:\n        if not self.available[idx]:\n            return None\n        self.available[idx] = False\n        return self.lines[idx]\n\n    def find_nearest_within(self, p: complex, max_dist: float) -> tuple[int | None, bool]:\n        \"\"\"Find the closest line, assuming a maximum admissible distance.\n        Returns a tuple of (idx, reverse), where `idx` may be None if nothing is found.\n        `reverse` indicates whether or not a line ending has been matched instead of a start.\n        False is always returned if index was created with `reverse=False`.s\n        \"\"\"\n\n        ridx = None\n        rdist: float | None = 0.0\n\n        while True:\n            reindex, idx, dist = self._find_nearest_within_in_index(p, max_dist, self.index)\n            if reindex:\n                self._reindex()\n                continue\n\n            if self.reverse:\n                reindex, ridx, rdist = self._find_nearest_within_in_index(\n                    p, max_dist, self.rindex\n                )\n                if reindex:\n                    self._reindex()\n                    continue\n            break\n\n        if self.reverse:\n            if idx is None and ridx is None:\n                return None, False\n            elif idx is not None and ridx is None:\n                return idx, False\n            elif idx is None and ridx is not None:\n                return ridx, True\n            elif rdist < dist:  # type: ignore\n                return ridx, True\n            else:\n                return idx, False\n        else:\n            return idx, False\n\n    def _find_nearest_within_in_index(\n        self, p: complex, max_dist: float, index: KDTree\n    ) -> tuple[bool, int | None, float | None]:\n        \"\"\"Find nearest in specific index. Return (reindex, idx, dist) tuple, where\n        reindex indicates if a reindex is needed.\n        \"\"\"\n\n        # For performance reason, we query only a max of k candidates. In the special case\n        # where all distances are not inf and none are available, we might have more than k\n        # suitable candidate, so we reindex and loop. Otherwise, we check the query results\n        # for availability and not inf and return anything found\n        dists, idxs = index.query((p.real, p.imag), k=50, distance_upper_bound=max_dist)\n        dists = np.array(dists)\n\n        not_inf = ~np.isinf(dists)\n        if np.all(not_inf) and np.all(~self.available[idxs[not_inf]]):\n            return True, None, 0\n\n        candidates = self.available[idxs[not_inf]]\n\n        if np.any(candidates):\n            idx = np.argmax(candidates)\n            return False, idxs[not_inf][idx], dists[not_inf][idx]\n        else:\n            return False, None, 0\n\n    # noinspection PyUnboundLocalVariable\n    def find_nearest(self, p: complex) -> tuple[int, bool]:\n        while True:\n            idx, dist = self._find_nearest_in_index(p, self.index)\n            if self.reverse:\n                ridx, rdist = self._find_nearest_in_index(p, self.rindex)\n\n                if ridx is not None and idx is not None:\n                    break\n            elif idx is not None:\n                break\n            self._reindex()\n\n        if self.reverse:\n            if rdist < dist:\n                return ridx, True  # type: ignore\n            else:\n                return idx, False\n        else:\n            return idx, False\n\n    def _find_nearest_in_index(self, p: complex, index: KDTree) -> tuple[int | None, float]:\n        \"\"\"Check the N nearest lines, hopefully find one that is active.\"\"\"\n\n        dists, idxs = index.query((p.real, p.imag), k=100)\n        for dist, idx in zip(dists, idxs):\n            if ~np.isinf(dist) and self.available[idx]:\n                return idx, dist\n\n        return None, 0.0\n", "path": "vpype/line_index.py"}]}