Skip to content

Commit 068e311

Browse files
committed
array: improve test coverage
1 parent 7ead166 commit 068e311

File tree

2 files changed

+90
-79
lines changed

2 files changed

+90
-79
lines changed

src/conversions/array.rs

+89-77
Original file line numberDiff line numberDiff line change
@@ -3,70 +3,6 @@ use crate::{
33
ToPyObject,
44
};
55

6-
#[cfg(not(min_const_generics))]
7-
macro_rules! array_impls {
8-
($($N:expr),+) => {
9-
$(
10-
impl<T> IntoPy<PyObject> for [T; $N]
11-
where
12-
T: ToPyObject
13-
{
14-
fn into_py(self, py: Python) -> PyObject {
15-
self.as_ref().to_object(py)
16-
}
17-
}
18-
19-
impl<'a, T> FromPyObject<'a> for [T; $N]
20-
where
21-
T: Copy + Default + FromPyObject<'a>,
22-
{
23-
#[cfg(not(feature = "nightly"))]
24-
fn extract(obj: &'a PyAny) -> PyResult<Self> {
25-
let mut array = [T::default(); $N];
26-
_extract_sequence_into_slice(obj, &mut array)?;
27-
Ok(array)
28-
}
29-
30-
#[cfg(feature = "nightly")]
31-
default fn extract(obj: &'a PyAny) -> PyResult<Self> {
32-
let mut array = [T::default(); $N];
33-
_extract_sequence_into_slice(obj, &mut array)?;
34-
Ok(array)
35-
}
36-
}
37-
38-
#[cfg(feature = "nightly")]
39-
impl<'source, T> FromPyObject<'source> for [T; $N]
40-
where
41-
for<'a> T: Default + FromPyObject<'a> + crate::buffer::Element,
42-
{
43-
fn extract(obj: &'source PyAny) -> PyResult<Self> {
44-
let mut array = [T::default(); $N];
45-
// first try buffer protocol
46-
if unsafe { crate::ffi::PyObject_CheckBuffer(obj.as_ptr()) } == 1 {
47-
if let Ok(buf) = crate::buffer::PyBuffer::get(obj) {
48-
if buf.dimensions() == 1 && buf.copy_to_slice(obj.py(), &mut array).is_ok() {
49-
buf.release(obj.py());
50-
return Ok(array);
51-
}
52-
buf.release(obj.py());
53-
}
54-
}
55-
// fall back to sequence protocol
56-
_extract_sequence_into_slice(obj, &mut array)?;
57-
Ok(array)
58-
}
59-
}
60-
)+
61-
}
62-
}
63-
64-
#[cfg(not(min_const_generics))]
65-
array_impls!(
66-
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
67-
26, 27, 28, 29, 30, 31, 32
68-
);
69-
706
#[cfg(min_const_generics)]
717
impl<T, const N: usize> IntoPy<PyObject> for [T; N]
728
where
@@ -100,20 +36,18 @@ where
10036
{
10137
fn extract(obj: &'source PyAny) -> PyResult<Self> {
10238
use crate::{AsPyPointer, PyNativeType};
103-
let mut array = [T::default(); N];
10439
// first try buffer protocol
10540
if unsafe { crate::ffi::PyObject_CheckBuffer(obj.as_ptr()) } == 1 {
10641
if let Ok(buf) = crate::buffer::PyBuffer::get(obj) {
42+
let mut array = [T::default(); N];
10743
if buf.dimensions() == 1 && buf.copy_to_slice(obj.py(), &mut array).is_ok() {
10844
buf.release(obj.py());
10945
return Ok(array);
11046
}
11147
buf.release(obj.py());
11248
}
11349
}
114-
// fall back to sequence protocol
115-
_extract_sequence_into_slice(obj, &mut array)?;
116-
Ok(array)
50+
create_array_from_obj(obj)
11751
}
11852
}
11953

@@ -123,12 +57,11 @@ where
12357
T: FromPyObject<'s>,
12458
{
12559
let seq = <crate::types::PySequence as PyTryFrom>::try_from(obj)?;
126-
let expected_len = seq.len()? as usize;
127-
array_try_from_fn(|idx| {
128-
seq.get_item(idx as isize)
129-
.map_err(|_| invalid_sequence_length(expected_len, idx + 1))?
130-
.extract::<T>()
131-
})
60+
let seq_len = seq.len()? as usize;
61+
if seq_len != N {
62+
return Err(invalid_sequence_length(N, seq_len));
63+
}
64+
array_try_from_fn(|idx| seq.get_item(idx as isize).and_then(PyAny::extract))
13265
}
13366

13467
// TODO use std::array::try_from_fn, if that stabilises:
@@ -174,7 +107,72 @@ where
174107
}
175108
}
176109

177-
fn _extract_sequence_into_slice<'s, T>(obj: &'s PyAny, slice: &mut [T]) -> PyResult<()>
110+
#[cfg(not(min_const_generics))]
111+
macro_rules! array_impls {
112+
($($N:expr),+) => {
113+
$(
114+
impl<T> IntoPy<PyObject> for [T; $N]
115+
where
116+
T: ToPyObject
117+
{
118+
fn into_py(self, py: Python) -> PyObject {
119+
self.as_ref().to_object(py)
120+
}
121+
}
122+
123+
impl<'a, T> FromPyObject<'a> for [T; $N]
124+
where
125+
T: Copy + Default + FromPyObject<'a>,
126+
{
127+
#[cfg(not(feature = "nightly"))]
128+
fn extract(obj: &'a PyAny) -> PyResult<Self> {
129+
let mut array = [T::default(); $N];
130+
extract_sequence_into_slice(obj, &mut array)?;
131+
Ok(array)
132+
}
133+
134+
#[cfg(feature = "nightly")]
135+
default fn extract(obj: &'a PyAny) -> PyResult<Self> {
136+
let mut array = [T::default(); $N];
137+
extract_sequence_into_slice(obj, &mut array)?;
138+
Ok(array)
139+
}
140+
}
141+
142+
#[cfg(feature = "nightly")]
143+
impl<'source, T> FromPyObject<'source> for [T; $N]
144+
where
145+
for<'a> T: Default + FromPyObject<'a> + crate::buffer::Element,
146+
{
147+
fn extract(obj: &'source PyAny) -> PyResult<Self> {
148+
let mut array = [T::default(); $N];
149+
// first try buffer protocol
150+
if unsafe { crate::ffi::PyObject_CheckBuffer(obj.as_ptr()) } == 1 {
151+
if let Ok(buf) = crate::buffer::PyBuffer::get(obj) {
152+
if buf.dimensions() == 1 && buf.copy_to_slice(obj.py(), &mut array).is_ok() {
153+
buf.release(obj.py());
154+
return Ok(array);
155+
}
156+
buf.release(obj.py());
157+
}
158+
}
159+
// fall back to sequence protocol
160+
extract_sequence_into_slice(obj, &mut array)?;
161+
Ok(array)
162+
}
163+
}
164+
)+
165+
}
166+
}
167+
168+
#[cfg(not(min_const_generics))]
169+
array_impls!(
170+
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
171+
26, 27, 28, 29, 30, 31, 32
172+
);
173+
174+
#[cfg(not(min_const_generics))]
175+
fn extract_sequence_into_slice<'s, T>(obj: &'s PyAny, slice: &mut [T]) -> PyResult<()>
178176
where
179177
T: FromPyObject<'s>,
180178
{
@@ -189,7 +187,7 @@ where
189187
Ok(())
190188
}
191189

192-
pub fn invalid_sequence_length(expected: usize, actual: usize) -> PyErr {
190+
fn invalid_sequence_length(expected: usize, actual: usize) -> PyErr {
193191
exceptions::PyValueError::new_err(format!(
194192
"expected a sequence of length {} (got {})",
195193
expected, actual
@@ -198,7 +196,7 @@ pub fn invalid_sequence_length(expected: usize, actual: usize) -> PyErr {
198196

199197
#[cfg(test)]
200198
mod test {
201-
use crate::Python;
199+
use crate::{PyResult, Python};
202200
#[cfg(min_const_generics)]
203201
use std::{
204202
panic,
@@ -238,6 +236,20 @@ mod test {
238236
assert!(&v == b"abc");
239237
}
240238

239+
#[test]
240+
fn test_extract_invalid_sequence_length() {
241+
let gil = Python::acquire_gil();
242+
let py = gil.python();
243+
let v: PyResult<[u8; 3]> = py
244+
.eval("bytearray(b'abcdefg')", None, None)
245+
.unwrap()
246+
.extract();
247+
assert_eq!(
248+
v.unwrap_err().to_string(),
249+
"ValueError: expected a sequence of length 3 (got 7)"
250+
);
251+
}
252+
241253
#[cfg(min_const_generics)]
242254
#[test]
243255
fn test_extract_bytearray_to_array() {

src/conversions/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
//! This module contains conversions between non-String Rust object and their string representation
2-
//! in Python
1+
//! This module contains conversions between various Rust object and their representation in Python.
32
43
mod array;
54
mod osstr;

0 commit comments

Comments
 (0)