Skip to content

fix: indexing, __setitem__ flow transfer, and fill edge cases#1149

Merged
henryiii merged 3 commits into
developfrom
fix/indexing-setitem
Jun 11, 2026
Merged

fix: indexing, __setitem__ flow transfer, and fill edge cases#1149
henryiii merged 3 commits into
developfrom
fix/indexing-setitem

Conversation

@henryiii

Copy link
Copy Markdown
Member

🤖 AI text below 🤖

Part of #1143. Fixes the following verified findings, all in the Python layer, each with regression tests:

  • B1: __setitem__ silently dropped flow bins when the value was a Histogramnp.asarray(value) invoked __array__ (view(flow=False)), so the expanded-setting branch could never match. Histogram values now use view(flow=True).
  • B5: threaded fill crashed on scalar positional args (np.array_split on a 0-d array) and on 0-d array weights (np.isscalar(np.array(2.0)) is False). Scalars now broadcast against each thread's chunk, np.ndim(...) == 0 is used for weight/sample, and an all-scalar fill is performed exactly once instead of once per thread.
  • B8: integer index bounds checks — h[-size] (valid bin 0) was rejected by an abs()-based check, and ax[-size-1] wrapped into the underflow bin; both now raise/accept correctly, with accurate error messages.
  • B9: _isstr returned True for empty iterables (all() over nothing), so Regular(...).index([]) raised TypeError. Empty sequences/arrays are no longer treated as strings, StrCategory.index([]) still works, and one-shot iterators are no longer consumed by the check.
  • B10: the "Did you forget to compile boost-histogram?" guard was dead code (submodules imported _core first). The guarded import now lives in boost_histogram/__init__.py before any submodule import.
  • B14: clearer errors — a plain string index (h["a"]) raises a clean IndexError instead of per-character list indexing; storage=42 / storage=bh.storage.Double raise TypeError with accurate messages (previously issubclass() crash / inverted KeyError); transform="log" / transform=Pow raise clear TypeErrors (previously AttributeError / direct-base-only check).
  • B15: ArrayTuple.__dir__ called dir() on a string literal; it now lists ndarray attributes.

Test suite: 1017 passed, 1 xfailed (pre-existing); mypy strict clean; prek -a clean.

🤖 Generated with Claude Code

henryiii added 3 commits June 11, 2026 01:04
…and __dir__

Part of #1143:

- B8b: Axis.__getitem__ no longer wraps ax[-size-1] into the underflow
  bin; out-of-range negative indices now raise IndexError, and the error
  message reports the requested index and the axis size.
- B9: _isstr no longer treats empty sequences/arrays as strings, so
  numerical axes accept .index([]). StrCategory still accepts empty
  sized iterables, and one-shot iterators are no longer consumed by the
  check.
- B14c: passing a transform class (e.g. transform.Pow) or a
  non-AxisTransform object (e.g. 'log') now raises a clear TypeError
  instead of relying on direct-base introspection or failing with
  AttributeError.
- B15: ArrayTuple.__dir__ now lists ndarray attributes instead of
  calling dir() on a string literal.

Assisted-by: ClaudeCode:claude-fable-5
Part of #1143 (B10): the guarded _core import in histogram.py was dead
code, since boost_histogram/__init__.py imported submodules (which
import _core directly) before histogram.py ever ran, so users only saw
a bare ModuleNotFoundError. Move the guard into the package __init__,
before any submodule import, and drop the redundant guard in
histogram.py (which keeps its plain import).

Also pass the combined message (original error plus hint) on Python
3.10, as originally intended.

Assisted-by: ClaudeCode:claude-fable-5
…nds, and clearer errors

Part of #1143:

- B1: setting from a Histogram (h[...] = h2) now keeps the value's flow
  bins; np.asarray(value) called __array__, which silently dropped them
  so the expanded-setting branch could never match for histogram values.
- B5: threaded fill no longer crashes on scalar positional arguments or
  0-d array weights/samples; scalars are broadcast against each thread's
  chunk, and an all-scalar fill is performed exactly once instead of
  once per thread.
- B8a: integer indexing accepts -size (bin 0); the bounds check used
  abs(), wrongly rejecting it.
- B14a: a plain string index (h['a']) now raises a clear IndexError
  pointing at the locator protocol instead of being treated as a
  per-character list of indices.
- B14b: invalid storage arguments raise TypeError with an accurate
  message both for non-storage objects (previously an issubclass()
  crash) and for uninitialized storage classes (previously a KeyError
  with an inverted message).

Assisted-by: ClaudeCode:claude-fable-5
@github-actions github-actions Bot added the needs changelog Might need a changelog entry label Jun 11, 2026
@henryiii henryiii marked this pull request as ready for review June 11, 2026 17:33
@henryiii henryiii merged commit 6992ac2 into develop Jun 11, 2026
37 of 39 checks passed
@henryiii henryiii deleted the fix/indexing-setitem branch June 11, 2026 17:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs changelog Might need a changelog entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant