Skip to content

Commit 38ca076

Browse files
authored
feat(stdlib/sqlite): Implement slice assignment for Blob (#6039)
1 parent 4079776 commit 38ca076

File tree

2 files changed

+57
-16
lines changed

2 files changed

+57
-16
lines changed

Lib/test/test_sqlite3/test_dbapi.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,8 +1322,6 @@ def test_blob_set_item_with_offset(self):
13221322
expected = b"This blob data string is exactly fifty bytes long."
13231323
self.assertEqual(self.blob.read(), expected)
13241324

1325-
# TODO: RUSTPYTHON
1326-
@unittest.expectedFailure
13271325
def test_blob_set_slice_buffer_object(self):
13281326
from array import array
13291327
self.blob[0:5] = memoryview(b"12345")
@@ -1351,22 +1349,16 @@ def test_blob_get_slice_negative_index(self):
13511349
def test_blob_get_slice_with_skip(self):
13521350
self.assertEqual(self.blob[0:10:2], b"ti lb")
13531351

1354-
# TODO: RUSTPYTHON
1355-
@unittest.expectedFailure
13561352
def test_blob_set_slice(self):
13571353
self.blob[0:5] = b"12345"
13581354
expected = b"12345" + self.data[5:]
13591355
actual = self.cx.execute("select b from test").fetchone()[0]
13601356
self.assertEqual(actual, expected)
13611357

1362-
# TODO: RUSTPYTHON
1363-
@unittest.expectedFailure
13641358
def test_blob_set_empty_slice(self):
13651359
self.blob[0:0] = b""
13661360
self.assertEqual(self.blob[:], self.data)
13671361

1368-
# TODO: RUSTPYTHON
1369-
@unittest.expectedFailure
13701362
def test_blob_set_slice_with_skip(self):
13711363
self.blob[0:10:2] = b"12345"
13721364
actual = self.cx.execute("select b from test").fetchone()[0]
@@ -1419,8 +1411,6 @@ def test_blob_set_item_error(self):
14191411
with self.assertRaisesRegex(ValueError, "must be in range"):
14201412
self.blob[0] = 2**65
14211413

1422-
# TODO: RUSTPYTHON
1423-
@unittest.expectedFailure
14241414
def test_blob_set_slice_error(self):
14251415
with self.assertRaisesRegex(IndexError, "wrong size"):
14261416
self.blob[5:10] = b"a"

stdlib/src/sqlite.rs

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2238,11 +2238,12 @@ mod _sqlite {
22382238
vm: &VirtualMachine,
22392239
) -> PyResult<()> {
22402240
let Some(value) = value else {
2241-
return Err(vm.new_type_error("Blob doesn't support deletion"));
2241+
return Err(vm.new_type_error("Blob doesn't support slice deletion"));
22422242
};
22432243
let inner = self.inner(vm)?;
22442244

22452245
if let Some(index) = needle.try_index_opt(vm) {
2246+
// Handle single item assignment: blob[i] = b
22462247
let Some(value) = value.downcast_ref::<PyInt>() else {
22472248
return Err(vm.new_type_error(format!(
22482249
"'{}' object cannot be interpreted as an integer",
@@ -2255,11 +2256,61 @@ mod _sqlite {
22552256
Self::expect_write(blob_len, 1, index, vm)?;
22562257
let ret = inner.blob.write_single(value, index);
22572258
self.check(ret, vm)
2258-
} else if let Some(_slice) = needle.downcast_ref::<PySlice>() {
2259-
Err(vm.new_not_implemented_error("Blob slice assignment is not implemented"))
2260-
// let blob_len = inner.blob.bytes();
2261-
// let slice = slice.to_saturated(vm)?;
2262-
// let (range, step, length) = slice.adjust_indices(blob_len as usize);
2259+
} else if let Some(slice) = needle.downcast_ref::<PySlice>() {
2260+
// Handle slice assignment: blob[a:b:c] = b"..."
2261+
let value_buf = PyBuffer::try_from_borrowed_object(vm, &value)?;
2262+
2263+
let buf = value_buf
2264+
.as_contiguous()
2265+
.ok_or_else(|| vm.new_buffer_error("underlying buffer is not C-contiguous"))?;
2266+
2267+
let blob_len = inner.blob.bytes();
2268+
let slice = slice.to_saturated(vm)?;
2269+
let (range, step, slice_len) = slice.adjust_indices(blob_len as usize);
2270+
2271+
if step == 0 {
2272+
return Err(vm.new_value_error("slice step cannot be zero"));
2273+
}
2274+
2275+
if buf.len() != slice_len {
2276+
return Err(vm.new_index_error("Blob slice assignment is wrong size"));
2277+
}
2278+
2279+
if slice_len == 0 {
2280+
return Ok(());
2281+
}
2282+
2283+
if step == 1 {
2284+
let ret = inner.blob.write(
2285+
buf.as_ptr().cast(),
2286+
buf.len() as c_int,
2287+
range.start as c_int,
2288+
);
2289+
self.check(ret, vm)
2290+
} else {
2291+
let span_len = range.end - range.start;
2292+
let mut temp_buf = vec![0u8; span_len];
2293+
2294+
let ret = inner.blob.read(
2295+
temp_buf.as_mut_ptr().cast(),
2296+
span_len as c_int,
2297+
range.start as c_int,
2298+
);
2299+
self.check(ret, vm)?;
2300+
2301+
let mut i_in_temp: usize = 0;
2302+
for i_in_src in 0..slice_len {
2303+
temp_buf[i_in_temp] = buf[i_in_src];
2304+
i_in_temp += step as usize;
2305+
}
2306+
2307+
let ret = inner.blob.write(
2308+
temp_buf.as_ptr().cast(),
2309+
span_len as c_int,
2310+
range.start as c_int,
2311+
);
2312+
self.check(ret, vm)
2313+
}
22632314
} else {
22642315
Err(vm.new_type_error("Blob indices must be integers"))
22652316
}

0 commit comments

Comments
 (0)