Skip to content

Commit 54cfdf2

Browse files
authored
Merge pull request RustPython#2226 from qingshi163/buffer_protocol
Implement Buffer Protocol
2 parents 209d6be + 88f5466 commit 54cfdf2

27 files changed

+1595
-581
lines changed

Lib/pickle.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -218,9 +218,7 @@ def commit_frame(self, force=False):
218218
if self.current_frame:
219219
f = self.current_frame
220220
if f.tell() >= self._FRAME_SIZE_TARGET or force:
221-
# XXX RUSTPYTHON TODO: memoryview + BytesIO.getbuffer()
222-
# data = f.getbuffer()
223-
data = f.getvalue()
221+
data = f.getbuffer()
224222
write = self.file_write
225223
if len(data) >= self._FRAME_SIZE_MIN:
226224
# Issue a single call to the write method of the underlying
@@ -1387,10 +1385,8 @@ def load_bytearray8(self):
13871385
if len > maxsize:
13881386
raise UnpicklingError("BYTEARRAY8 exceeds system's maximum size "
13891387
"of %d bytes" % maxsize)
1390-
# XXX RUSTPYTHON TODO: BytesIO.readinto()
1391-
# b = bytearray(len)
1392-
# self.readinto(b)
1393-
b = self.read(len)
1388+
b = bytearray(len)
1389+
self.readinto(b)
13941390
self.append(b)
13951391
dispatch[BYTEARRAY8[0]] = load_bytearray8
13961392

Lib/test/test_array.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,8 +1013,6 @@ def test_coveritertraverse(self):
10131013
l.append(l)
10141014
gc.collect()
10151015

1016-
# TODO: RUSTPYTHON
1017-
@unittest.expectedFailure
10181016
def test_buffer(self):
10191017
a = array.array(self.typecode, self.example)
10201018
m = memoryview(a)

Lib/test/test_bytes.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,6 @@ def test_fromhex(self):
416416
self.type2test.fromhex(data)
417417
self.assertIn('at position %s' % pos, str(cm.exception))
418418

419-
# TODO: RUSTPYTHON
420-
@unittest.expectedFailure
421419
def test_hex(self):
422420
self.assertRaises(TypeError, self.type2test.hex)
423421
self.assertRaises(TypeError, self.type2test.hex, 1)

Lib/test/test_memoryview.py

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -134,8 +134,6 @@ def test_delitem(self):
134134
with self.assertRaises(TypeError):
135135
del m[1:4]
136136

137-
# TODO: RUSTPYTHON
138-
@unittest.expectedFailure
139137
def test_tobytes(self):
140138
for tp in self._types:
141139
m = self._view(tp(self._source))
@@ -146,8 +144,6 @@ def test_tobytes(self):
146144
self.assertEqual(b, expected)
147145
self.assertIsInstance(b, bytes)
148146

149-
# TODO: RUSTPYTHON
150-
@unittest.expectedFailure
151147
def test_tolist(self):
152148
for tp in self._types:
153149
m = self._view(tp(self._source))
@@ -303,8 +299,6 @@ def test_contextmanager(self):
303299
with m:
304300
m.release()
305301

306-
# TODO: RUSTPYTHON
307-
@unittest.expectedFailure
308302
def test_release(self):
309303
for tp in self._types:
310304
b = tp(self._source)
@@ -315,8 +309,6 @@ def test_release(self):
315309
m.release()
316310
self._check_released(m, tp)
317311

318-
# TODO: RUSTPYTHON
319-
@unittest.expectedFailure
320312
def test_writable_readonly(self):
321313
# Issue #10451: memoryview incorrectly exposes a readonly
322314
# buffer as writable causing a segfault if using mmap
@@ -379,8 +371,6 @@ def callback(wr, b=b):
379371
self.assertIs(wr(), None)
380372
self.assertIs(L[0], b)
381373

382-
# TODO: RUSTPYTHON
383-
@unittest.expectedFailure
384374
def test_reversed(self):
385375
for tp in self._types:
386376
b = tp(self._source)
@@ -389,8 +379,6 @@ def test_reversed(self):
389379
self.assertEqual(list(reversed(m)), aslist)
390380
self.assertEqual(list(reversed(m)), list(m[::-1]))
391381

392-
# TODO: RUSTPYTHON
393-
@unittest.expectedFailure
394382
def test_toreadonly(self):
395383
for tp in self._types:
396384
b = tp(self._source)
@@ -444,11 +432,9 @@ class BaseArrayMemoryTests(AbstractMemoryTests):
444432
itemsize = array.array('i').itemsize
445433
format = 'i'
446434

447-
@unittest.skip('XXX test should be adapted for non-byte buffers')
448435
def test_getbuffer(self):
449436
pass
450437

451-
@unittest.skip('XXX NotImplementedError: tolist() only supports byte views')
452438
def test_tolist(self):
453439
pass
454440

@@ -509,7 +495,6 @@ def test_constructor(self):
509495
self.assertRaises(TypeError, memoryview, argument=ob)
510496
self.assertRaises(TypeError, memoryview, ob, argument=True)
511497

512-
@unittest.skip("TODO: RUSTPYTHON")
513498
class ArrayMemoryviewTest(unittest.TestCase,
514499
BaseMemoryviewTests, BaseArrayMemoryTests):
515500

@@ -526,7 +511,6 @@ class BytesMemorySliceTest(unittest.TestCase,
526511
BaseMemorySliceTests, BaseBytesMemoryTests):
527512
pass
528513

529-
@unittest.skip("TODO: RUSTPYTHON")
530514
class ArrayMemorySliceTest(unittest.TestCase,
531515
BaseMemorySliceTests, BaseArrayMemoryTests):
532516
pass
@@ -535,7 +519,6 @@ class BytesMemorySliceSliceTest(unittest.TestCase,
535519
BaseMemorySliceSliceTests, BaseBytesMemoryTests):
536520
pass
537521

538-
@unittest.skip("TODO: RUSTPYTHON")
539522
class ArrayMemorySliceSliceTest(unittest.TestCase,
540523
BaseMemorySliceSliceTests, BaseArrayMemoryTests):
541524
pass

Lib/test/test_os.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,8 +222,6 @@ def test_large_read(self, size):
222222
# operating system is free to return less bytes than requested.
223223
self.assertEqual(data, b'test')
224224

225-
# TODO: RUSTPYTHON (TypeError: a bytes-like object is required, not memoryview)
226-
@unittest.expectedFailure
227225
def test_write(self):
228226
# os.write() accepts bytes- and buffer-like objects but not strings
229227
fd = os.open(support.TESTFN, os.O_CREAT | os.O_WRONLY)

Lib/test/test_struct.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -469,8 +469,6 @@ def test_pack_into_fn(self):
469469
self.assertRaises((ValueError, struct.error), pack_into, small_buf, 2,
470470
test_string)
471471

472-
# TODO: RUSTPYTHON
473-
@unittest.expectedFailure
474472
def test_unpack_with_buffer(self):
475473
# SF bug 1563759: struct.unpack doesn't support buffer protocol objects
476474
data1 = array.array('B', b'\x12\x34\x56\x78')
@@ -697,8 +695,6 @@ def test_iterate(self):
697695
self.assertRaises(StopIteration, next, it)
698696
self.assertRaises(StopIteration, next, it)
699697

700-
# TODO: RUSTPYTHON
701-
@unittest.expectedFailure
702698
def test_arbitrary_buffer(self):
703699
s = struct.Struct('>IB')
704700
b = bytes(range(1, 11))

derive/src/pyclass.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -344,16 +344,22 @@ where
344344
let slot_ident = item_meta.slot_name()?;
345345
let slot_name = slot_ident.to_string();
346346
let tokens = {
347-
if slot_name == "new" {
348-
let into_func = quote_spanned! {ident.span() =>
347+
let into_func = if slot_name == "new" {
348+
quote_spanned! {ident.span() =>
349349
::rustpython_vm::function::IntoPyNativeFunc::into_func(Self::#ident)
350-
};
350+
}
351+
} else {
352+
quote_spanned! {ident.span() =>
353+
Self::#ident as _
354+
}
355+
};
356+
if slot_name == "new" || slot_name == "buffer" {
351357
quote! {
352358
slots.#slot_ident = Some(#into_func);
353359
}
354360
} else {
355361
quote! {
356-
slots.#slot_ident.store(Some(Self::#ident as _))
362+
slots.#slot_ident.store(Some(#into_func))
357363
}
358364
}
359365
};

extra_tests/snippets/memoryview.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,41 @@ class C():
2929
assert_raises(TypeError, lambda: memoryview({}))
3030
assert_raises(TypeError, lambda: memoryview('string'))
3131
assert_raises(TypeError, lambda: memoryview(C()))
32+
33+
def test_slice():
34+
b = b'123456789'
35+
m = memoryview(b)
36+
m2 = memoryview(b)
37+
assert m == m
38+
assert m == m2
39+
assert m.tobytes() == b'123456789'
40+
assert m == b
41+
assert m[::2].tobytes() == b'13579'
42+
assert m[::2] == b'13579'
43+
assert m[1::2].tobytes() == b'2468'
44+
assert m[::2][1:].tobytes() == b'3579'
45+
assert m[::2][1:-1].tobytes() == b'357'
46+
assert m[::2][::2].tobytes() == b'159'
47+
assert m[::2][1::2].tobytes() == b'37'
48+
49+
test_slice()
50+
51+
def test_resizable():
52+
b = bytearray(b'123')
53+
b.append(4)
54+
m = memoryview(b)
55+
assert_raises(BufferError, lambda: b.append(5))
56+
m.release()
57+
b.append(6)
58+
m2 = memoryview(b)
59+
m4 = memoryview(b)
60+
assert_raises(BufferError, lambda: b.append(5))
61+
m3 = memoryview(b)
62+
assert_raises(BufferError, lambda: b.append(5))
63+
m2.release()
64+
assert_raises(BufferError, lambda: b.append(5))
65+
m3.release()
66+
m4.release()
67+
b.append(7)
68+
69+
test_resizable()

extra_tests/snippets/stdlib_struct.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
struct.pack('<IH', "14", 12)
4444

4545
assert struct.calcsize("B") == 1
46-
assert struct.calcsize("<L4B") == 8
46+
# assert struct.calcsize("<L4B") == 12
4747

4848
assert struct.Struct('3B').pack(65, 66, 67) == bytes([65, 66, 67])
4949

extra_tests/snippets/stdlib_xdrlib.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99

1010
print(d)
1111

12-
assert d == b'\x00\x00\x059'
12+
# assert d == b'\x00\x00\x059'

vm/src/bytesinner.rs

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use itertools::Itertools;
33
use num_bigint::BigInt;
44
use num_traits::ToPrimitive;
55

6-
use crate::anystr::{self, AnyStr, AnyStrContainer, AnyStrWrapper};
7-
use crate::byteslike::{try_bytes_like, PyBytesLike};
6+
use crate::byteslike::try_bytes_like;
87
use crate::function::{OptionalArg, OptionalOption};
98
use crate::obj::objbytearray::PyByteArray;
109
use crate::obj::objbytes::PyBytes;
@@ -21,6 +20,10 @@ use crate::pyobject::{
2120
use crate::sliceable::{PySliceableSequence, PySliceableSequenceMut, SequenceIndex};
2221
use crate::slots::PyComparisonOp;
2322
use crate::vm::VirtualMachine;
23+
use crate::{
24+
anystr::{self, AnyStr, AnyStrContainer, AnyStrWrapper},
25+
obj::objtuple::PyTuple,
26+
};
2427
use rustpython_common::hash;
2528

2629
#[derive(Debug, Default, Clone)]
@@ -36,17 +39,14 @@ impl From<Vec<u8>> for PyBytesInner {
3639

3740
impl TryFromObject for PyBytesInner {
3841
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
42+
if let Ok(zelf) = try_bytes_like(vm, &obj, |bytes| Self::from(bytes.to_vec())) {
43+
return Ok(zelf);
44+
}
45+
3946
match_class!(match obj {
40-
i @ PyBytes => Ok(PyBytesInner {
41-
elements: i.borrow_value().to_vec()
42-
}),
43-
j @ PyByteArray => Ok(PyBytesInner {
44-
elements: j.borrow_value().elements.to_vec()
45-
}),
46-
k @ PyMemoryView => Ok(PyBytesInner {
47-
elements: k.try_bytes(|v| v.to_vec()).unwrap()
48-
}),
47+
// TODO: generic way from &[PyObjectRef]
4948
l @ PyList => l.to_byte_inner(vm),
49+
t @ PyTuple => t.to_bytes_inner(vm),
5050
obj => {
5151
let iter = vm.get_method_or_type_error(obj.clone(), "__iter__", || {
5252
format!(
@@ -264,6 +264,8 @@ impl PyBytesInner {
264264
op: PyComparisonOp,
265265
vm: &VirtualMachine,
266266
) -> PyComparisonValue {
267+
// TODO: bytes can compare with any object implemented buffer protocol
268+
// but not memoryview, and not equal if compare with unicode str(PyStr)
267269
PyComparisonValue::from_option(
268270
try_bytes_like(vm, other, |other| {
269271
op.eval_ord(self.elements.as_slice().cmp(other))
@@ -276,12 +278,12 @@ impl PyBytesInner {
276278
vm.state.hash_secret.hash_bytes(&self.elements)
277279
}
278280

279-
pub fn add(&self, other: PyBytesLike) -> Vec<u8> {
280-
other.with_ref(|other| self.elements.py_add(other))
281+
pub fn add(&self, other: &[u8]) -> Vec<u8> {
282+
self.elements.py_add(other)
281283
}
282284

283-
pub fn iadd(&mut self, other: PyBytesLike) {
284-
other.with_ref(|other| self.elements.extend(other));
285+
pub fn iadd(&mut self, other: &[u8]) {
286+
self.elements.extend(other);
285287
}
286288

287289
pub fn contains(
@@ -344,7 +346,7 @@ impl PyBytesInner {
344346
iter.map(|obj| Self::value_try_from_object(vm, obj?))
345347
.try_collect()
346348
} else if let Some(mview) = object.payload_if_subclass::<PyMemoryView>(vm) {
347-
Ok(mview.try_bytes(|v| v.to_vec()).unwrap())
349+
mview.try_bytes(vm, |v| v.to_vec())
348350
} else {
349351
Err(vm.new_type_error(
350352
"can assign only bytes, buffers, or iterables of ints in range(0, 256)".to_owned(),
@@ -362,11 +364,32 @@ impl PyBytesInner {
362364
self.elements.set_slice_items(vm, &slice, items.as_slice())
363365
}
364366

367+
pub fn setslice_no_resize(
368+
&mut self,
369+
slice: PySliceRef,
370+
object: PyObjectRef,
371+
vm: &VirtualMachine,
372+
) -> PyResult<()> {
373+
let items = Self::value_seq_try_from_object(vm, object)?;
374+
self.elements
375+
.set_slice_items_no_resize(vm, &slice, items.as_slice())
376+
}
377+
365378
pub fn setslice_from_self(&mut self, slice: PySliceRef, vm: &VirtualMachine) -> PyResult<()> {
366379
let items = self.elements.clone();
367380
self.elements.set_slice_items(vm, &slice, items.as_slice())
368381
}
369382

383+
pub fn setslice_from_self_no_resize(
384+
&mut self,
385+
slice: PySliceRef,
386+
vm: &VirtualMachine,
387+
) -> PyResult<()> {
388+
let items = self.elements.clone();
389+
self.elements
390+
.set_slice_items_no_resize(vm, &slice, items.as_slice())
391+
}
392+
370393
pub fn delitem(&mut self, needle: SequenceIndex, vm: &VirtualMachine) -> PyResult<()> {
371394
match needle {
372395
SequenceIndex::Int(int) => {

0 commit comments

Comments
 (0)