You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Properties .start, .stop, . step of (generic) slice[...] should be optional (| None) #15526
#13008 made slice.__new__ more precise. Generic slice[T] accepts T | None as arguments when creating a slice.
However, properties .start, .stop, . step of (generic) slice[...] are typed as there respective types, i.e. T in above example.
Consequence
This leads to typecheckers accepting:
def test(s: slice[int]) -> None:
assert_type(s.start, int)
if s.start is None:
assert_never(s.start)
whereas below obviously breaks:
test(slice(42))
Solution
Therefore, I suggest properties .start, .stop, . step should have optional (| None) return types:
defslice_slice(old_slice: slice, applied_slice: slice, size: int) ->slice:
"""Given a slice and the size of the dimension to which it will be applied, index it with another slice to return a new slice equivalent to applying the slices sequentially """old_slice=slice(*old_slice.indices(size))
size_after_old_slice=len(range(old_slice.start, old_slice.stop, old_slice.step))
ifsize_after_old_slice==0:
# nothing left after applying first slicereturnslice(0)
applied_slice=slice(*applied_slice.indices(size_after_old_slice))
start=old_slice.start+applied_slice.start*old_slice.stepifstart<0:
# nothing left after applying second slice# (can only happen for old_slice.step < 0, e.g. [10::-1], [20:])returnslice(0)
stop=old_slice.start+applied_slice.stop*old_slice.stepifstop<0:
stop=Nonestep=old_slice.step*applied_slice.stepreturnslice(start, stop, step)
Activity
srittau commentedon Mar 19, 2026
I'm unsure why we're using
Anyinstead ofNonefor the return type annotations ofslice.__new__. The comment says:But I'm not sure what that means.
srittau commentedon Mar 19, 2026
Cc @randolf-scholz
randolf-scholz commentedon Mar 19, 2026
Using
T | Nonefor start/stop/step was my initial instinct as well, but there were cases in mypy primer that got hit by that: #12899 (comment)See for example this commit: 3010eda
and the mypy primer output: #12899 (comment)
If we make None implicit, this means we force every user of slices to accept open-ended slice, but there are users who seem to rely on only using bounded slices in some cases, for example, code like this in xarray: (https://github.com/pydata/xarray/blob/a1c0617051260cc84789bfc6c31a7fef3d64f01b/xarray/core/indexing.py#L318
)
randolf-scholz commentedon Mar 19, 2026
This was a result of this discussion: #12899 (comment)
randolf-scholz commentedon Mar 22, 2026
This is what the primer looks like if we drastically simplify towards (#15536)
randolf-scholz commentedon Mar 22, 2026
some of these actually seem to be problems with
mypyrather than anything else, for example:makes mypy complain, but this should just be
slice[None, None, None] | slice[None | None | int]EDIT: This seems to be fixed on mypy master branch, maybe we wait for 1.20 which should release soon and then check primer again.