cost_distance: replace mutable default target_values=[] with None sentinel (#3340)#3348
Merged
brendancol merged 3 commits intoJun 15, 2026
Merged
Conversation
…istance (#3340) cost_distance() declared target_values: list = [], a mutable default shared across calls. The value is rebound via np.asarray on entry so there was no active mutation bug, but the declaration is the unsafe form and drifts from proximity()/allocation(), which use the None sentinel. Switch to target_values: list = None with an internal 'if target_values is None: target_values = []' substitution. Non-breaking (callers passing a list are unaffected; default behavior unchanged), no deprecation shim needed. Adds regression tests pinning the None default and confirming None / [] / omitted are equivalent.
brendancol
commented
Jun 15, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
PR Review: cost_distance — replace mutable default target_values=[] with None sentinel
Blockers
None.
Suggestions
None.
Nits
xrspatial/cost_distance.py:1230— the docstring still reads "If empty, all non-zero finite pixels are sources." With the default nowNone, "if not provided" would be marginally more precise (proximity's docstring phrases it that way). Optional; theoptionalmarker on the type line already covers the None default.
What looks good
- Minimal, surgical change: signature default plus a two-line
None->[]substitution placed before the existingnp.asarrayrebind. No behavior change. - Matches the sibling convention exactly — proximity.py uses the same
target_values: list = Noneplus internal substitution. - Non-breaking, correctly identified as needing no deprecation shim (nothing renamed).
- Tests pin both the regression (default is
None) and the behavioral equivalence ofNone/[]/ omitted, with source-pixel assertions that confirm semantics rather than just "runs without error." - Backend dispatch untouched; the change lives entirely in the shared public wrapper, so all four backends inherit it.
Checklist
- No algorithm change (signature/default only)
- All backends inherit the change (shared wrapper)
- NaN handling unchanged
- Edge cases covered (None / [] / omitted equivalence)
- No premature materialization or copies introduced
- Benchmark not needed
- README matrix not applicable
- Docstring present; minor wording nit noted
brendancol
commented
Jun 15, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
Follow-up: applied the docstring nit (cost_distance.py:1230, "If empty" -> "If not provided") to match the proximity wording now that the default is the None sentinel. Tests still green. No blockers or suggestions outstanding.
…tency-cost_distance-2026-06-15-01
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 #3340
What
cost_distance()declaredtarget_values: list = [](a mutable default). Switched it to theNonesentinel with internal substitution, matchingproximity()/allocation().target_values: list = Noneif target_values is None: target_values = []before the existingnp.asarrayrebindNonedefault and confirmingNone/[]/ omitted are equivalentWhy
A shared
[]default is the unsafe form and drifted from the sibling proximity functions. The value was already rebound vianp.asarrayon entry, so there was no active mutation bug, but the declaration is still wrong and inconsistent. Found by /sweep-api-consistency (Cat 4, MEDIUM).Non-breaking: callers passing a list are unaffected and the default behavior (all non-zero finite pixels are sources) is identical. No deprecation shim needed since nothing is renamed.
Backends
Pure signature/default change in the public wrapper, shared by all four backends. Verified numpy, cupy, dask+numpy, dask+cupy still dispatch and produce identical results.
Test plan
pytest xrspatial/tests/test_cost_distance.py(57 passed)None, andNone/[]/ omitted give equal outputtest_target_values(explicit values still filter) passes on all parametrized backends