Skip to content

Pathfinding API consistency: preserve attrs in multi_stop_search, Dataset support for a_star_search, type hints, defaults#3653

Merged
brendancol merged 2 commits into
mainfrom
deep-sweep-api-consistency-pathfinding-2026-07-08
Jul 9, 2026
Merged

Pathfinding API consistency: preserve attrs in multi_stop_search, Dataset support for a_star_search, type hints, defaults#3653
brendancol merged 2 commits into
mainfrom
deep-sweep-api-consistency-pathfinding-2026-07-08

Conversation

@brendancol

Copy link
Copy Markdown
Contributor

Closes #3644

Four small API-consistency fixes in xrspatial/pathfinding.py, found by /sweep-api-consistency. None of them changes call syntax or results for existing correct code.

  • multi_stop_search now keeps the input surface's attrs (crs, res, units, ...) and adds waypoint_order / segment_costs / total_cost on top, instead of replacing them. a_star_search already preserved attrs; the two siblings now agree.
  • a_star_search gets @supports_dataset, matching multi_stop_search and the analogous functions elsewhere (cost_distance, proximity, surface_distance, hydro). Added a_star_search to the Dataset accessor next to the existing multi_stop_search entry.
  • Type hints: start/goal were annotated Union[tuple, list, np.array] (np.array is a function, not a type) and are now np.ndarray; x/y drop the Optional[str] claim since None was never accepted; friction becomes Optional[xr.DataArray]. Docstring fixes: surface was described as "2D array of values to bin" (a copy-paste leftover), connectivity had no description, and a few parameter entries were missing the numpy-style space before the colon.
  • barriers: list = [] mutable defaults in both public functions replaced with a None sentinel plus in-body substitution. The old default was never mutated, so this is prevention, not a bug fix; a test pins None == [] == omitted.

Backend coverage: no kernel changes. The full pathfinding suite (60 tests, including the cupy and dask+cupy ones) passes on a GPU host, and the new Dataset/attrs behavior was smoke-tested on cupy and dask+cupy directly.

Test plan:

  • pytest xrspatial/tests/test_pathfinding.py (60 passed, GPU host)
  • pytest xrspatial/tests/test_accessor.py -k "a_star or multi_stop or expected_methods" (7 passed)
  • New tests: attrs preservation, Dataset routing through a_star_search, barriers=None parity, ds.xrs.a_star_search
  • flake8 on touched files: only pre-existing findings remain

Also documented (not fixed): the first parameter is surface here vs raster / agg elsewhere in the library. Naming is split three ways across the codebase, so a local rename would churn a stable signature without settling the convention. Recorded in the sweep state CSV, which is included in this PR.

https://claude.ai/code/session_0155N4QGamQVxgpAAPbpQNq4

…ints, defaults (#3644)

- multi_stop_search now preserves input surface attrs alongside
  waypoint_order/segment_costs/total_cost
- a_star_search gets @supports_dataset like its sibling and the rest of
  the library; ds.xrs.a_star_search added to the Dataset accessor
- start/goal annotated np.ndarray (was np.array, a function), x/y
  annotated plain str (None never worked), friction Optional
- barriers mutable [] default replaced with None sentinel in both
  public functions, behavior unchanged
- docstring fixes: surface description ("values to bin" leftover),
  connectivity description, numpy-style spacing

Claude-Session: https://claude.ai/code/session_0155N4QGamQVxgpAAPbpQNq4

@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: Pathfinding API consistency (attrs, Dataset support, type hints, defaults)

Blockers (must fix before merge)

  • Adding @supports_dataset to a_star_search breaks keyword calls of the first parameter. a_star_search(surface=agg, start=..., goal=...) worked before this PR and now raises TypeError: a_star_search() missing 1 required positional argument: 'agg', because the wrapper in xrspatial/dataset_support.py:22 is def wrapper(agg, *args, **kwargs) and only binds the raster positionally. Verified on this branch. The flaw is pre-existing for every other decorated function whose first parameter is not named agg (cost_distance(raster=...) and proximity(raster=...) fail the same way on main), so the right fix is in the decorator: resolve the first parameter by its real name (from the signature already captured at line 18) when it arrives via kwargs, instead of requiring it positionally. That repairs cost_distance/proximity too and removes the regression here.

Suggestions (should fix, not blocking)

  • xrspatial/pathfinding.py Returns section of a_star_search still says only "path_agg: xr.DataArray of the same type as surface". The Parameters entry now documents Dataset input; the Returns entry should mention that a Dataset comes back for Dataset input, the way multi_stop_search's does.

Nits (optional improvements)

  • test_barriers_default_none_matches_empty_list runs numpy only. The barriers normalization happens before backend dispatch so this is fine, but a one-line comment saying so would save the next reader from wondering.

What looks good

  • Every finding in the linked issue #3644 is reproduced in the PR body and covered by a test: attrs preservation, Dataset routing, None/[]/omitted parity for barriers.
  • The attrs merge puts routing keys last, so waypoint_order/segment_costs/total_cost win over any input attr with the same name. That precedence matches the docstring.
  • Existing 60-test pathfinding suite passes on a GPU host, including the cupy and dask+cupy tests; the new behavior was also smoke-tested on cupy and dask+cupy directly.
  • No kernel or dispatch changes, so no new backend parity or dask chunking surface.
  • Sweep state CSV rides along in the same PR, as the sweep contract requires.

Checklist

  • Algorithm matches reference/paper (no algorithmic change)
  • All implemented backends produce consistent results
  • NaN handling is correct (unchanged)
  • Edge cases are covered by tests
  • Dask chunk boundaries handled correctly (unchanged)
  • No premature materialization or unnecessary copies
  • Benchmark exists (benchmarks/benchmarks/pathfinding.py, unchanged and still valid: positional call)
  • README feature matrix: no new functions, no backend change
  • Docstrings present and accurate (see Suggestion on the Returns section)

The wrapper only accepted the raster positionally, so decorating
a_star_search regressed a_star_search(surface=...) calls (and
cost_distance(raster=...) etc. were already broken the same way on
main). The wrapper now resolves the first parameter by its real name
when passed as a keyword. Also mention Dataset in the a_star_search
Returns section and note why the barriers-default test is numpy-only.

Claude-Session: https://claude.ai/code/session_0155N4QGamQVxgpAAPbpQNq4

@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 follow-up (after 65189ef)

All three findings from the first pass are addressed:

  • Blocker (keyword-calling regression): fixed in xrspatial/dataset_support.py. The wrapper now binds the raster positionally or by its real parameter name, so a_star_search(surface=...) works again, and previously broken keyword calls like cost_distance(raster=...) and proximity(raster=...) work too. Covered by test_first_param_by_keyword (exercises slope(agg=), proximity(raster=), a_star_search(surface=)) and test_first_param_missing_raises_typeerror.
  • Suggestion (Returns docstring): a_star_search Returns section now mentions the Dataset case.
  • Nit (numpy-only test): test_barriers_default_none_matches_empty_list now explains why one backend is enough.

Verified locally: test_dataset_support + test_pathfinding + test_accessor (178 passed) and a broader batch over decorated-function suites (classify, cost_distance, proximity, slope, multispectral: 1113 passed) on a GPU host.

One note for the log, not a request: the second commit's diff to test_pathfinding.py looks larger than it is because the newly appended test block was normalized to the file's CRLF line endings; the content is unchanged apart from the docstring note.

No remaining findings.

@brendancol brendancol merged commit 3837bba into main Jul 9, 2026
10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pathfinding: multi_stop_search drops input attrs, a_star_search lacks Dataset support, type hint and default fixes

1 participant