Skip to content

Fix Path slicing to match standard sequence semantics - #301

Open
gaoflow wants to merge 1 commit into
mahmoud:masterfrom
gaoflow:fix-path-slice-semantics
Open

Fix Path slicing to match standard sequence semantics#301
gaoflow wants to merge 1 commit into
mahmoud:masterfrom
gaoflow:fix-path-slice-semantics

Conversation

@gaoflow

@gaoflow gaoflow commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Problem

Path.__getitem__ handles slicing by scaling the start/stop indices into
the path's internal (op, value) ops layout, but it never clamps out-of-range
bounds and mishandles negative steps. The result is a Path whose .values()
disagrees with the equivalent tuple slice, and empty slices produce a Path
with a misaligned ops tuple:

>>> from glom import Path
>>> p = Path('a', 'b', 'c')
>>> p.values()
('a', 'b', 'c')
>>> p[-5:].values()          # expected ('a', 'b', 'c')
('c',)
>>> p[::-1].values()         # expected ('c', 'b', 'a')
('a', 'b', 'c')
>>> Path('x', 'y')[-4:-3]    # expected Path() ; corrupt result

Integer indexing already clamps/raises correctly; only the slice branch is
affected. This is the sibling of the recent integer-index fix (GH-299).

Fix

Split the slice branch out and drive it with range(*i.indices(len(self))),
which yields exactly the component indices standard sequence slicing would
(clamped bounds, negative steps honored). The (op, value) pairs for those
components are then reassembled. The integer-index branch is unchanged.

Tests

test_path_slices_match_sequence_semantics checks clamped negative bounds,
negative steps, empty-slice validity, and an exhaustive cross-check of
Path(...)[key].values() == values_tuple[key] over paths of length 0–4 and a
grid of start/stop/step values.

pytest glom/test/test_path_and_t.py passes (the 3 unrelated test_cli.py
failures in my environment are a missing optional YAML dependency and reproduce
on a clean checkout).

Path.__getitem__ built the sliced ops by hand: it scaled the start/stop
indices into the internal (op, value) layout but never clamped
out-of-range bounds and mishandled negative steps. As a result a slice
such as Path('a','b','c')[-5:] or [::-1] returned a corrupt Path whose
.values() disagreed with the equivalent tuple slice, and empty slices
produced a Path with a misaligned ops tuple.

Handle the slice branch separately using range(*i.indices(len(self))),
which yields exactly the component indices Python sequence slicing would,
then reassemble the (op, value) pairs for those components. The integer
index branch is unchanged.
@codecov

codecov Bot commented Jul 6, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.23%. Comparing base (6fd4134) to head (b2d3775).
⚠️ Report is 3 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master     #301   +/-   ##
=======================================
  Coverage   98.22%   98.23%           
=======================================
  Files          27       27           
  Lines        4391     4407   +16     
  Branches      612      617    +5     
=======================================
+ Hits         4313     4329   +16     
  Misses         53       53           
  Partials       25       25           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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.

1 participant