Repository: scipy/scipy
Pull request: BUG: guard against non-finite kd-tree queries
<!-- 
Thanks for contributing a pull request! Please ensure that
your PR satisfies the checklist before submitting:
http://scipy.github.io/devdocs/dev/contributor/development_workflow.html#checklist-before-submitting-a-pr

Also, please name and describe your PR as you would write a
commit message:
http://scipy.github.io/devdocs/dev/contributor/development_workflow.html#writing-the-commit-message

Note that we are a team of volunteers; we appreciate your
patience during the review process.

Again, thanks for contributing!
-->

#### Reference issue
<!--Example: Closes gh-WXYZ.-->
closes #18497 
#### What does this implement/fix?
<!--Please explain your changes.-->
Adds a check for finiteness to `query` and `query_ball_point`
#### Additional information
<!--Any additional information you think is important.-->


Problem statement:
MAINT, BUG: guard against non-finite kd-tree queries
As a follow-up to gh-18230, we should follow the suggestion from @sturlamolden to guard against non-finite values in the various query methods available in our kd-tree analyses. It may be nice if we get this done before branching `maintenance/1.11.x` since we're banning non-finite points in the trees proper in that release series, though since the results are potentially nonsense of such queries we could justify putting in a bugfix point release after as well, if needed.

Hints from issue discussion:

Commit message: BUG: guard against non-finite kd-tree queries

Patch to review:
diff --git a/scipy/spatial/_ckdtree.pyx b/scipy/spatial/_ckdtree.pyx
index 6709de54733a..8d9b3fcf2a2d 100644
--- a/scipy/spatial/_ckdtree.pyx
+++ b/scipy/spatial/_ckdtree.pyx
@@ -791,6 +791,9 @@ cdef class cKDTree:
         if p < 1:
             raise ValueError("Only p-norms with 1<=p<=infinity permitted")
 
+        if not np.isfinite(x).all():
+            raise ValueError("'x' must be finite, check for nan or inf values")
+
         cdef:
             bool single = (x_arr.ndim == 1)
             bool nearest = False
@@ -940,6 +943,9 @@ cdef class cKDTree:
 
             const np.float64_t *vxx = <np.float64_t*>x_arr.data
             const np.float64_t *vrr = <np.float64_t*>r_arr.data
+        
+        if not np.isfinite(x).all():
+            raise ValueError("'x' must be finite, check for nan or inf values")
 
         if rlen:
             result = np.empty(retshape, dtype=np.intp)
diff --git a/scipy/spatial/tests/test_kdtree.py b/scipy/spatial/tests/test_kdtree.py
index a8a5b4fd0edb..c15935f9823c 100644
--- a/scipy/spatial/tests/test_kdtree.py
+++ b/scipy/spatial/tests/test_kdtree.py
@@ -1468,10 +1468,17 @@ def test_kdtree_nan():
         KDTree(data)
 
 
-def test_gh_18223():
+def test_nonfintite_inputs_gh_18223():
     rng = np.random.default_rng(12345)
     coords = rng.uniform(size=(100, 3), low=0.0, high=0.1)
-    KDTree(coords, balanced_tree=False, compact_nodes=False)
+    t = KDTree(coords, balanced_tree=False, compact_nodes=False)
+    bad_coord = [np.nan for _ in range(3)]
+
+    with pytest.raises(ValueError, match="must be finite"):
+        t.query(bad_coord)
+    with pytest.raises(ValueError, match="must be finite"):
+        t.query_ball_point(bad_coord, 1)
+
     coords[0, :] = np.nan
     with pytest.raises(ValueError, match="must be finite"):
         KDTree(coords, balanced_tree=True, compact_nodes=False)


Code review comments:
diff_hunk: @@ -1468,10 +1468,17 @@ def test_kdtree_nan():
         KDTree(data)
 
 
-def test_gh_18223():
+def test_nonfintite_inputs_gh_18223():
path: scipy/spatial/tests/test_kdtree.py
text: @author:
```suggestion
def test_nonfinite_inputs_gh_18223():
```