Forward per-tool repr to the accessed tool (#3478)#3479
Merged
Conversation
Evaluating a single accessor tool such as da.xrs.slope returned a bound method, whose repr embeds repr(self) and therefore the whole accessor catalog added in #3477. Wrap public methods in a callable _AccessorTool proxy on attribute access so the repr shows that tool's own signature and docstring instead. Calls forward unchanged and the delegated docstring is mirrored for help()/inspect.
brendancol
commented
Jun 24, 2026
brendancol
left a comment
Contributor
Author
There was a problem hiding this comment.
PR Review: Forward per-tool repr to the accessed tool (#3478)
I read accessor.py in full and exercised the proxy against the edge cases below. The fix is correct and targeted. No blockers.
Blockers
None.
Suggestions
None.
Nits (optional)
_AccessorToolreturns a fresh proxy on every attribute access, soda.xrs.slope == da.xrs.slopeis now False where a bound method compared equal. Harmless for normal use, but anything downstream comparing accessor methods by equality would see the change. Low risk; not worth caching unless it bites.__getattribute__now runs for every attribute access on the accessor, includingself._objinside each method body. The earlyname.startswith("_")return keeps the hot path cheap (one extra call plus a startswith), so this is fine, just noting the cost is non-zero.
What looks good
- Root cause is correctly identified: a bound method's repr embeds
repr(self), which routed through the catalog__repr__. Wrapping at attribute access is the right layer. - The proxy mirrors
__doc__/__wrapped__/__name__, sohelp(),inspect.signature, andinspect.getdockeep working. Verifiedinspect.signature(da.xrs.slope)returns(**kwargs)andgetdocmatches the standalone function. - Private attributes and the catalog
__repr__are left untouched, soda.xrsstill renders the full listing. - Calls forward unchanged, positional args included (ndvi, rasterize are covered by the existing suite, which now all runs through the proxy).
- Missing attributes still raise AttributeError.
- Tests cover the scoped text repr, HTML repr, callable forwarding, help metadata, the Dataset accessor, and the unchanged catalog repr. Both accessors are handled.
Checklist
- Root cause correctly identified
- help()/inspect metadata preserved
- Catalog repr unchanged
- Calls forward unchanged (incl. positional args)
- Both DataArray and Dataset accessors covered
- Tests present and passing (86 in test_accessor.py), flake8 clean
- No compute path touched; backend-agnostic
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.
Summary
Closes #3478.
After #3477 added a categorized
__repr__to the.xrsaccessor, evaluating a single tool without calling it (e.g.da.xrs.slope) printed the whole accessor catalog.da.xrs.slopeis a bound method, and Python builds a bound method's repr as<bound method Cls.name of {repr(self)}>, sorepr(self)routed through the accessor's catalog__repr__and embedded the entire listing._AccessorToolproxy. Its__repr__/_repr_html_show that tool's own signature and docstring instead. Calls forward unchanged, and__doc__/__wrapped__are mirrored sohelp()andinspectkeep seeing the delegated docstring.da.xrs) is untouched.Backend coverage
Pure accessor-layer change; no compute path is touched. Behaves the same across numpy / cupy / dask+numpy / dask+cupy.
Test plan
repr(da.xrs.slope)shows slope's signature + docstring, not the cataloginspect.getdoc/help()metadata preserved on the proxytest_accessor.pysuite passes (86 tests), flake8 clean