Skip to content

cost_distance: replace mutable default target_values=[] with None sentinel (#3340)#3348

Merged
brendancol merged 3 commits into
mainfrom
deep-sweep-api-consistency-cost_distance-2026-06-15-01
Jun 15, 2026
Merged

cost_distance: replace mutable default target_values=[] with None sentinel (#3340)#3348
brendancol merged 3 commits into
mainfrom
deep-sweep-api-consistency-cost_distance-2026-06-15-01

Conversation

@brendancol

Copy link
Copy Markdown
Contributor

Closes #3340

What

cost_distance() declared target_values: list = [] (a mutable default). Switched it to the None sentinel with internal substitution, matching proximity() / allocation().

  • Signature: target_values: list = None
  • Body: if target_values is None: target_values = [] before the existing np.asarray rebind
  • Regression tests pinning the None default and confirming None / [] / omitted are equivalent

Why

A shared [] default is the unsafe form and drifted from the sibling proximity functions. The value was already rebound via np.asarray on 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)
  • New: default is None, and None / [] / omitted give equal output
  • Existing test_target_values (explicit values still filter) passes on all parametrized backends

…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.
@github-actions github-actions Bot added the performance PR touches performance-sensitive code label Jun 15, 2026

@brendancol brendancol left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 now None, "if not provided" would be marginally more precise (proximity's docstring phrases it that way). Optional; the optional marker 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 existing np.asarray rebind. No behavior change.
  • Matches the sibling convention exactly — proximity.py uses the same target_values: list = None plus 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 of None / [] / 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 brendancol left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@brendancol brendancol merged commit 56b4564 into main Jun 15, 2026
7 checks passed
@brendancol brendancol deleted the deep-sweep-api-consistency-cost_distance-2026-06-15-01 branch June 25, 2026 14:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

performance PR touches performance-sensitive code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cost_distance(): replace mutable default target_values=[] with None sentinel

1 participant