diff --git a/newsfragments/5667.changed.md b/newsfragments/5667.changed.md new file mode 100644 index 00000000000..bf592255ff7 --- /dev/null +++ b/newsfragments/5667.changed.md @@ -0,0 +1 @@ +`FromPyObject` on `Cow<[u8]>` allow any `Sequence[int]` and change the error type to `PyErr` \ No newline at end of file diff --git a/pytests/stubs/buf_and_str.pyi b/pytests/stubs/buf_and_str.pyi index 73e6ccd0117..3313600ef4f 100644 --- a/pytests/stubs/buf_and_str.pyi +++ b/pytests/stubs/buf_and_str.pyi @@ -13,7 +13,7 @@ class BytesExtractor: @staticmethod def from_str_lossy(string: str) -> int: ... -def map_byte_cow(bytes: bytes | bytearray) -> bytes: ... +def map_byte_cow(bytes: Sequence[int]) -> bytes: ... def map_byte_slice(bytes: bytes) -> bytes: ... -def map_byte_vec(bytes: bytes | bytearray | Sequence[int]) -> bytes: ... +def map_byte_vec(bytes: Sequence[int]) -> bytes: ... def return_memoryview() -> memoryview: ... diff --git a/src/conversion.rs b/src/conversion.rs index 5579a50c347..8eff4671ebe 100644 --- a/src/conversion.rs +++ b/src/conversion.rs @@ -7,9 +7,9 @@ use crate::inspect::types::TypeInfo; use crate::inspect::TypeHint; use crate::pyclass::boolean_struct::False; use crate::pyclass::{PyClassGuardError, PyClassGuardMutError}; -use crate::types::PyTuple; #[cfg(feature = "experimental-inspect")] -use crate::types::{PyList, PySequence}; +use crate::types::PyList; +use crate::types::PyTuple; use crate::{ Borrowed, Bound, BoundObject, Py, PyAny, PyClass, PyClassGuard, PyErr, PyRef, PyRefMut, PyTypeCheck, Python, @@ -463,12 +463,6 @@ pub trait FromPyObject<'a, 'py>: Sized { Option::>::None } - /// The union of Sequence[Self::INPUT_TYPE] and the input sequence extraction function [`FromPyObject::sequence_extractor`] if it's defined - #[cfg(feature = "experimental-inspect")] - #[doc(hidden)] - const SEQUENCE_INPUT_TYPE: TypeHint = - TypeHint::subscript(&PySequence::TYPE_HINT, &[Self::INPUT_TYPE]); - /// Helper used to make a specialized path in extracting `DateTime` where `Tz` is /// `chrono::Local`, which will accept "naive" datetime objects as being in the local timezone. #[cfg(feature = "chrono-local")] diff --git a/src/conversions/std/array.rs b/src/conversions/std/array.rs index 5d8034f369c..a27e8c107b5 100644 --- a/src/conversions/std/array.rs +++ b/src/conversions/std/array.rs @@ -51,7 +51,7 @@ where type Error = PyErr; #[cfg(feature = "experimental-inspect")] - const INPUT_TYPE: TypeHint = T::SEQUENCE_INPUT_TYPE; + const INPUT_TYPE: TypeHint = TypeHint::subscript(&PySequence::TYPE_HINT, &[T::INPUT_TYPE]); fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { if let Some(extractor) = T::sequence_extractor(obj, crate::conversion::private::Token) { diff --git a/src/conversions/std/num.rs b/src/conversions/std/num.rs index 3f10b47ddac..f20f6b18dcc 100644 --- a/src/conversions/std/num.rs +++ b/src/conversions/std/num.rs @@ -8,8 +8,6 @@ use crate::inspect::TypeHint; use crate::py_result_ext::PyResultExt; #[cfg(feature = "experimental-inspect")] use crate::type_object::PyTypeInfo; -#[cfg(feature = "experimental-inspect")] -use crate::types::PySequence; use crate::types::{PyByteArray, PyByteArrayMethods, PyBytes, PyInt}; use crate::{exceptions, ffi, Borrowed, Bound, FromPyObject, PyAny, PyErr, PyResult, Python}; use std::convert::Infallible; @@ -322,13 +320,6 @@ impl<'py> FromPyObject<'_, 'py> for u8 { None } } - - #[cfg(feature = "experimental-inspect")] - const SEQUENCE_INPUT_TYPE: TypeHint = TypeHint::union(&[ - PyBytes::TYPE_HINT, - PyByteArray::TYPE_HINT, - TypeHint::subscript(&PySequence::TYPE_HINT, &[Self::INPUT_TYPE]), - ]); } pub(crate) enum BytesSequenceExtractor<'a, 'py> { diff --git a/src/conversions/std/slice.rs b/src/conversions/std/slice.rs index 8a42c138986..936cfec6742 100644 --- a/src/conversions/std/slice.rs +++ b/src/conversions/std/slice.rs @@ -7,9 +7,7 @@ use crate::inspect::TypeHint; #[cfg(feature = "experimental-inspect")] use crate::type_object::PyTypeInfo; use crate::{ - conversion::IntoPyObject, - types::{PyByteArray, PyByteArrayMethods, PyBytes}, - Bound, CastError, PyAny, PyErr, Python, + conversion::IntoPyObject, types::PyBytes, Bound, CastError, PyAny, PyErr, PyResult, Python, }; impl<'a, 'py, T> IntoPyObject<'py> for &'a [T] @@ -63,18 +61,17 @@ impl<'a, 'py> crate::conversion::FromPyObject<'a, 'py> for &'a [u8] { /// pointing into the source object, and no copying or heap allocations will happen. /// If it is a `bytearray`, its contents will be copied to an owned `Cow`. impl<'a, 'py> crate::conversion::FromPyObject<'a, 'py> for Cow<'a, [u8]> { - type Error = CastError<'a, 'py>; + type Error = PyErr; #[cfg(feature = "experimental-inspect")] - const INPUT_TYPE: TypeHint = TypeHint::union(&[PyBytes::TYPE_HINT, PyByteArray::TYPE_HINT]); - - fn extract(ob: crate::Borrowed<'a, 'py, PyAny>) -> Result { - if let Ok(bytes) = ob.cast::() { - return Ok(Cow::Borrowed(bytes.as_bytes())); - } - - let byte_array = ob.cast::()?; - Ok(Cow::Owned(byte_array.to_vec())) + const INPUT_TYPE: TypeHint = Vec::::INPUT_TYPE; + + fn extract(ob: crate::Borrowed<'a, 'py, PyAny>) -> PyResult { + Ok(if let Ok(bytes) = ob.cast::() { + Cow::Borrowed(bytes.as_bytes()) // It's immutable, we can take a slice + } else { + Cow::Owned(Vec::extract(ob)?) // Not possible to take a slice, we have to build a Vec + }) } #[cfg(feature = "experimental-inspect")] diff --git a/src/conversions/std/vec.rs b/src/conversions/std/vec.rs index 89806631d35..28c9fc624ec 100644 --- a/src/conversions/std/vec.rs +++ b/src/conversions/std/vec.rs @@ -69,7 +69,7 @@ where type Error = PyErr; #[cfg(feature = "experimental-inspect")] - const INPUT_TYPE: TypeHint = T::SEQUENCE_INPUT_TYPE; + const INPUT_TYPE: TypeHint = TypeHint::subscript(&PySequence::TYPE_HINT, &[T::INPUT_TYPE]); fn extract(obj: Borrowed<'_, 'py, PyAny>) -> PyResult { if let Some(extractor) = T::sequence_extractor(obj, crate::conversion::private::Token) {