fix(arrays): stop fill from growing the array on reversed spans

fill rebuilt the array as array[:start] + fill + array[end:]. When start
came after end those two slices overlapped, duplicating the shared
elements and growing the array while writing no value. Normalize start and
end into [0, length] and fill in place with an index loop so an empty span
is a no-op and the length stays fixed.
This commit is contained in:
dualfroz committed 2026-09-06 01:12:09 +02:00
1 parent b129d1f04f
commit 442a17b572
2 files changed
+19 -6

No files matched your search

+11 -6
View File
@@ -531,13 +531,18 @@ def fill(
.. versionadded:: 3.1.0
"""
if end is None:
end = len(array)
else:
end = min(end, len(array))
length = len(array)
# Use this style of assignment so that `array` is mutated.
array[:] = array[:start] + [value] * len(array[start:end]) + array[end:] # type: ignore
if end is None:
end = length
# Normalize to ``[0, length]``, negatives as slicing does, and fill in
# place so the length stays fixed.
start = max(length + start, 0) if start < 0 else min(start, length)
end = max(length + end, 0) if end < 0 else min(end, length)
for index in range(start, end):
array[index] = value # type: ignore
return array # type: ignore
+8
View File
@@ -158,6 +158,14 @@ def test_duplicates(case, expected):
(([1, 2, 3, 4, 5], 0, 0, 5), [0, 0, 0, 0, 0]),
(([1, 2, 3, 4, 5], 0, 0, 8), [0, 0, 0, 0, 0]),
(([1, 2, 3, 4, 5], 0, 0, -1), [0, 0, 0, 0, 5]),
# start after end must be a no-op and must not grow the array.
(([1, 2, 3, 4], "x", 4, 1), [1, 2, 3, 4]),
(([1, 2, 3, 4, 5], 0, 3, 2), [1, 2, 3, 4, 5]),
# negative start supported the same way slicing does.
(([1, 2, 3, 4, 5], 0, -2), [1, 2, 3, 0, 0]),
(([1, 2, 3, 4, 5], 0, -2, -1), [1, 2, 3, 0, 5]),
# reversed negative span is a no-op, not an array-growing overlap.
(([1, 2, 3], "x", -1, -2), [1, 2, 3]),
],
)
def test_fill(case, expected):