Validate focal_stats() stats_funcs before dispatch#2798
Merged
Conversation
Unknown names fell through as a raw KeyError, and a bare string like 'mean' was iterated character by character and failed on 'm'. Add a _validate_stats_funcs helper that wraps a bare string into a one-element list and raises a clear ValueError listing valid options for unknown names.
brendancol
commented
Jun 1, 2026
Contributor
Author
brendancol
left a comment
There was a problem hiding this comment.
PR Review: Validate focal_stats() stats_funcs before dispatch
Blockers (must fix before merge)
None.
Suggestions (should fix, not blocking)
None.
Nits (optional improvements)
- xrspatial/focal.py (the new
_validate_stats_funcshelper): an empty list (stats_funcs=[]) passes validation and then fails later inside the backend atxr.concatof an empty sequence. This is pre-existing behavior the PR does not change, so it is not a blocker. Since you are already adding a validation gate here, rejecting an empty list with the same clear ValueError would be cheap. Optional.
What looks good
_VALID_STATS_FUNCSmatches the keys in_focal_stats_cpuand the cuda/cupy mappers exactly, so validation accepts precisely the names the backends implement.- The bare string is normalized before the membership check, so the character-iteration bug is fixed at the source.
- Validation runs before backend dispatch, so numpy, cupy, dask+numpy, and dask+cupy all get the same behavior through one code path.
- The 3D per-band recursion re-validates the already-normalized list, which is idempotent and harmless.
- Three focused regression tests cover the unknown-name, bare-string, and valid-list cases. The full focal_stats suite still passes (30 tests).
Checklist
- Algorithm matches reference: n/a (validation only)
- All implemented backends produce consistent results: yes, validation is backend-agnostic
- NaN handling is correct: unchanged
- Edge cases covered by tests: unknown name, bare string, valid list covered; empty list not covered
- Dask chunk boundaries handled correctly: unchanged
- No premature materialization: yes
- Benchmark exists or not needed: not needed
- README feature matrix updated: not needed, no new function
- Docstrings present and accurate: yes
Address review nit: an empty stats_funcs list passed validation and then failed deep inside the backend at xr.concat. Reject it up front with the same clear ValueError.
brendancol
commented
Jun 1, 2026
Contributor
Author
brendancol
left a comment
There was a problem hiding this comment.
Follow-up review (after empty-list fix)
The earlier nit is resolved: _validate_stats_funcs now rejects an empty stats_funcs up front with a clear ValueError instead of letting it fail later at xr.concat. A regression test covers it.
No further findings.
Status
- Blockers: none
- Suggestions: none
- Nits: none (empty-list nit fixed)
Full focal_stats suite passes locally (31 tests).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #2770
Summary
focal_stats()now checks itsstats_funcsargument before dispatching to a backend. Before this, an unknown name fell through as a rawKeyError, and passing a bare string like'mean'got iterated character by character and blew up on'm'.A new
_validate_stats_funcshelper wraps a bare string into a one-element list and raises aValueErrorthat lists the valid options when it sees an unknown name.Backend coverage
Validation happens before backend dispatch, so it applies to numpy, cupy, dask+numpy, and dask+cupy alike.
Test plan
ValueError