feat(stdlib/sqlite): Implement slice assignment for Blob (#6039)

This commit is contained in:
Jiseok CHOI authored and GitHub committed 2025-07-27 10:43:11 +09:00
1 parent 4079776c36
commit 38ca076cb5
2 files changed
+57 -16

No files matched your search

-10
View File
@@ -1322,8 +1322,6 @@ class BlobTests(unittest.TestCase):
expected = b"This blob data string is exactly fifty bytes long."
self.assertEqual(self.blob.read(), expected)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_blob_set_slice_buffer_object(self):
from array import array
self.blob[0:5] = memoryview(b"12345")
@@ -1351,22 +1349,16 @@ class BlobTests(unittest.TestCase):
def test_blob_get_slice_with_skip(self):
self.assertEqual(self.blob[0:10:2], b"ti lb")
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_blob_set_slice(self):
self.blob[0:5] = b"12345"
expected = b"12345" + self.data[5:]
actual = self.cx.execute("select b from test").fetchone()[0]
self.assertEqual(actual, expected)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_blob_set_empty_slice(self):
self.blob[0:0] = b""
self.assertEqual(self.blob[:], self.data)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_blob_set_slice_with_skip(self):
self.blob[0:10:2] = b"12345"
actual = self.cx.execute("select b from test").fetchone()[0]
@@ -1419,8 +1411,6 @@ class BlobTests(unittest.TestCase):
with self.assertRaisesRegex(ValueError, "must be in range"):
self.blob[0] = 2**65
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_blob_set_slice_error(self):
with self.assertRaisesRegex(IndexError, "wrong size"):
self.blob[5:10] = b"a"
+57 -6
View File
@@ -2238,11 +2238,12 @@ mod _sqlite {
vm: &VirtualMachine,
) -> PyResult<()> {
let Some(value) = value else {
return Err(vm.new_type_error("Blob doesn't support deletion"));
return Err(vm.new_type_error("Blob doesn't support slice deletion"));
};
let inner = self.inner(vm)?;
if let Some(index) = needle.try_index_opt(vm) {
// Handle single item assignment: blob[i] = b
let Some(value) = value.downcast_ref::<PyInt>() else {
return Err(vm.new_type_error(format!(
"'{}' object cannot be interpreted as an integer",
@@ -2255,11 +2256,61 @@ mod _sqlite {
Self::expect_write(blob_len, 1, index, vm)?;
let ret = inner.blob.write_single(value, index);
self.check(ret, vm)
} else if let Some(_slice) = needle.downcast_ref::<PySlice>() {
Err(vm.new_not_implemented_error("Blob slice assignment is not implemented"))
// let blob_len = inner.blob.bytes();
// let slice = slice.to_saturated(vm)?;
// let (range, step, length) = slice.adjust_indices(blob_len as usize);
} else if let Some(slice) = needle.downcast_ref::<PySlice>() {
// Handle slice assignment: blob[a:b:c] = b"..."
let value_buf = PyBuffer::try_from_borrowed_object(vm, &value)?;
let buf = value_buf
.as_contiguous()
.ok_or_else(|| vm.new_buffer_error("underlying buffer is not C-contiguous"))?;
let blob_len = inner.blob.bytes();
let slice = slice.to_saturated(vm)?;
let (range, step, slice_len) = slice.adjust_indices(blob_len as usize);
if step == 0 {
return Err(vm.new_value_error("slice step cannot be zero"));
}
if buf.len() != slice_len {
return Err(vm.new_index_error("Blob slice assignment is wrong size"));
}
if slice_len == 0 {
return Ok(());
}
if step == 1 {
let ret = inner.blob.write(
buf.as_ptr().cast(),
buf.len() as c_int,
range.start as c_int,
);
self.check(ret, vm)
} else {
let span_len = range.end - range.start;
let mut temp_buf = vec![0u8; span_len];
let ret = inner.blob.read(
temp_buf.as_mut_ptr().cast(),
span_len as c_int,
range.start as c_int,
);
self.check(ret, vm)?;
let mut i_in_temp: usize = 0;
for i_in_src in 0..slice_len {
temp_buf[i_in_temp] = buf[i_in_src];
i_in_temp += step as usize;
}
let ret = inner.blob.write(
temp_buf.as_ptr().cast(),
span_len as c_int,
range.start as c_int,
);
self.check(ret, vm)
}
} else {
Err(vm.new_type_error("Blob indices must be integers"))
}