add `PyBytes::new_bound`
This commit is contained in:
parent
aa1a9864f7
commit
4c94be51a7
|
@ -388,7 +388,7 @@ enum RustyEnum<'a> {
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
# {
|
# {
|
||||||
# let thing = PyBytes::new(py, b"text");
|
# let thing = PyBytes::new_bound(py, b"text");
|
||||||
# let rust_thing: RustyEnum<'_> = thing.extract()?;
|
# let rust_thing: RustyEnum<'_> = thing.extract()?;
|
||||||
#
|
#
|
||||||
# assert_eq!(
|
# assert_eq!(
|
||||||
|
|
|
@ -42,10 +42,9 @@ impl BytesExtractor {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn return_memoryview(py: Python<'_>) -> PyResult<&PyMemoryView> {
|
fn return_memoryview(py: Python<'_>) -> PyResult<Bound<'_, PyMemoryView>> {
|
||||||
let bytes: &PyAny = PyBytes::new(py, b"hello world").into();
|
let bytes = PyBytes::new_bound(py, b"hello world");
|
||||||
let memoryview = TryInto::try_into(bytes)?;
|
PyMemoryView::from_bound(&bytes)
|
||||||
Ok(memoryview)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
|
|
|
@ -77,7 +77,7 @@
|
||||||
//! let res = Python::with_gil(|py| {
|
//! let res = Python::with_gil(|py| {
|
||||||
//! let zlib = PyModule::import(py, "zlib")?;
|
//! let zlib = PyModule::import(py, "zlib")?;
|
||||||
//! let decompress = zlib.getattr("decompress")?;
|
//! let decompress = zlib.getattr("decompress")?;
|
||||||
//! let bytes = PyBytes::new(py, bytes);
|
//! let bytes = PyBytes::new_bound(py, bytes);
|
||||||
//! let value = decompress.call1((bytes,))?;
|
//! let value = decompress.call1((bytes,))?;
|
||||||
//! value.extract::<Vec<u8>>()
|
//! value.extract::<Vec<u8>>()
|
||||||
//! })?;
|
//! })?;
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
//! let res = Python::with_gil(|py| {
|
//! let res = Python::with_gil(|py| {
|
||||||
//! let zlib = PyModule::import(py, "zlib")?;
|
//! let zlib = PyModule::import(py, "zlib")?;
|
||||||
//! let decompress = zlib.getattr("decompress")?;
|
//! let decompress = zlib.getattr("decompress")?;
|
||||||
//! let bytes = PyBytes::new(py, bytes);
|
//! let bytes = PyBytes::new_bound(py, bytes);
|
||||||
//! let value = decompress.call1((bytes,))?;
|
//! let value = decompress.call1((bytes,))?;
|
||||||
//! value.extract::<Vec<u8>>()
|
//! value.extract::<Vec<u8>>()
|
||||||
//! })?;
|
//! })?;
|
||||||
|
|
|
@ -78,7 +78,7 @@ macro_rules! bigint_conversion {
|
||||||
#[cfg(Py_LIMITED_API)]
|
#[cfg(Py_LIMITED_API)]
|
||||||
fn to_object(&self, py: Python<'_>) -> PyObject {
|
fn to_object(&self, py: Python<'_>) -> PyObject {
|
||||||
let bytes = $to_bytes(self);
|
let bytes = $to_bytes(self);
|
||||||
let bytes_obj = PyBytes::new(py, &bytes);
|
let bytes_obj = PyBytes::new_bound(py, &bytes);
|
||||||
let kwargs = if $is_signed > 0 {
|
let kwargs = if $is_signed > 0 {
|
||||||
let kwargs = PyDict::new(py);
|
let kwargs = PyDict::new(py);
|
||||||
kwargs.set_item(crate::intern!(py, "signed"), true).unwrap();
|
kwargs.set_item(crate::intern!(py, "signed"), true).unwrap();
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
#[cfg(feature = "experimental-inspect")]
|
#[cfg(feature = "experimental-inspect")]
|
||||||
use crate::inspect::types::TypeInfo;
|
use crate::inspect::types::TypeInfo;
|
||||||
use crate::{types::PyBytes, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python, ToPyObject};
|
use crate::{types::PyBytes, FromPyObject, IntoPy, PyAny, PyObject, PyResult, Python};
|
||||||
|
|
||||||
impl<'a> IntoPy<PyObject> for &'a [u8] {
|
impl<'a> IntoPy<PyObject> for &'a [u8] {
|
||||||
fn into_py(self, py: Python<'_>) -> PyObject {
|
fn into_py(self, py: Python<'_>) -> PyObject {
|
||||||
PyBytes::new(py, self).to_object(py)
|
PyBytes::new_bound(py, self).unbind().into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "experimental-inspect")]
|
#[cfg(feature = "experimental-inspect")]
|
||||||
|
|
|
@ -24,8 +24,8 @@ impl Dummy {
|
||||||
"Dummy"
|
"Dummy"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn __bytes__<'py>(&self, py: crate::Python<'py>) -> &'py crate::types::PyBytes {
|
fn __bytes__<'py>(&self, py: crate::Python<'py>) -> crate::Bound<'py, crate::types::PyBytes> {
|
||||||
crate::types::PyBytes::new(py, &[0])
|
crate::types::PyBytes::new_bound(py, &[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn __format__(&self, format_spec: ::std::string::String) -> ::std::string::String {
|
fn __format__(&self, format_spec: ::std::string::String) -> ::std::string::String {
|
||||||
|
@ -420,8 +420,8 @@ impl Dummy {
|
||||||
"Dummy"
|
"Dummy"
|
||||||
}
|
}
|
||||||
|
|
||||||
fn __bytes__<'py>(&self, py: crate::Python<'py>) -> &'py crate::types::PyBytes {
|
fn __bytes__<'py>(&self, py: crate::Python<'py>) -> crate::Bound<'py, crate::types::PyBytes> {
|
||||||
crate::types::PyBytes::new(py, &[0])
|
crate::types::PyBytes::new_bound(py, &[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
fn __format__(&self, format_spec: ::std::string::String) -> ::std::string::String {
|
fn __format__(&self, format_spec: ::std::string::String) -> ::std::string::String {
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
|
use crate::ffi_ptr_ext::FfiPtrExt;
|
||||||
use crate::instance::{Borrowed, Bound};
|
use crate::instance::{Borrowed, Bound};
|
||||||
|
use crate::types::any::PyAnyMethods;
|
||||||
use crate::{ffi, FromPyObject, IntoPy, Py, PyAny, PyNativeType, PyResult, Python, ToPyObject};
|
use crate::{ffi, FromPyObject, IntoPy, Py, PyAny, PyNativeType, PyResult, Python, ToPyObject};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::ops::Index;
|
use std::ops::Index;
|
||||||
|
@ -17,14 +19,45 @@ pub struct PyBytes(PyAny);
|
||||||
pyobject_native_type_core!(PyBytes, pyobject_native_static_type_object!(ffi::PyBytes_Type), #checkfunction=ffi::PyBytes_Check);
|
pyobject_native_type_core!(PyBytes, pyobject_native_static_type_object!(ffi::PyBytes_Type), #checkfunction=ffi::PyBytes_Check);
|
||||||
|
|
||||||
impl PyBytes {
|
impl PyBytes {
|
||||||
|
/// Deprecated form of [`PyBytes::new_bound`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "gil-refs"),
|
||||||
|
deprecated(
|
||||||
|
since = "0.21.0",
|
||||||
|
note = "`PyBytes::new` will be replaced by `PyBytes::new_bound` in a future PyO3 version"
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub fn new<'p>(py: Python<'p>, s: &[u8]) -> &'p PyBytes {
|
||||||
|
Self::new_bound(py, s).into_gil_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new Python bytestring object.
|
/// Creates a new Python bytestring object.
|
||||||
/// The bytestring is initialized by copying the data from the `&[u8]`.
|
/// The bytestring is initialized by copying the data from the `&[u8]`.
|
||||||
///
|
///
|
||||||
/// Panics if out of memory.
|
/// Panics if out of memory.
|
||||||
pub fn new<'p>(py: Python<'p>, s: &[u8]) -> &'p PyBytes {
|
pub fn new_bound<'p>(py: Python<'p>, s: &[u8]) -> Bound<'p, PyBytes> {
|
||||||
let ptr = s.as_ptr() as *const c_char;
|
let ptr = s.as_ptr() as *const c_char;
|
||||||
let len = s.len() as ffi::Py_ssize_t;
|
let len = s.len() as ffi::Py_ssize_t;
|
||||||
unsafe { py.from_owned_ptr(ffi::PyBytes_FromStringAndSize(ptr, len)) }
|
unsafe {
|
||||||
|
ffi::PyBytes_FromStringAndSize(ptr, len)
|
||||||
|
.assume_owned(py)
|
||||||
|
.downcast_into_unchecked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deprecated form of [`PyBytes::new_bound_with`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "gil-refs"),
|
||||||
|
deprecated(
|
||||||
|
since = "0.21.0",
|
||||||
|
note = "`PyBytes::new_with` will be replaced by `PyBytes::new_bound_with` in a future PyO3 version"
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub fn new_with<F>(py: Python<'_>, len: usize, init: F) -> PyResult<&PyBytes>
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut [u8]) -> PyResult<()>,
|
||||||
|
{
|
||||||
|
Self::new_bound_with(py, len, init).map(Bound::into_gil_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new Python `bytes` object with an `init` closure to write its contents.
|
/// Creates a new Python `bytes` object with an `init` closure to write its contents.
|
||||||
|
@ -41,34 +74,49 @@ impl PyBytes {
|
||||||
///
|
///
|
||||||
/// # fn main() -> PyResult<()> {
|
/// # fn main() -> PyResult<()> {
|
||||||
/// Python::with_gil(|py| -> PyResult<()> {
|
/// Python::with_gil(|py| -> PyResult<()> {
|
||||||
/// let py_bytes = PyBytes::new_with(py, 10, |bytes: &mut [u8]| {
|
/// let py_bytes = PyBytes::new_bound_with(py, 10, |bytes: &mut [u8]| {
|
||||||
/// bytes.copy_from_slice(b"Hello Rust");
|
/// bytes.copy_from_slice(b"Hello Rust");
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
/// })?;
|
/// })?;
|
||||||
/// let bytes: &[u8] = FromPyObject::extract(py_bytes)?;
|
/// let bytes: &[u8] = py_bytes.extract()?;
|
||||||
/// assert_eq!(bytes, b"Hello Rust");
|
/// assert_eq!(bytes, b"Hello Rust");
|
||||||
/// Ok(())
|
/// Ok(())
|
||||||
/// })
|
/// })
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
pub fn new_with<F>(py: Python<'_>, len: usize, init: F) -> PyResult<&PyBytes>
|
pub fn new_bound_with<F>(py: Python<'_>, len: usize, init: F) -> PyResult<Bound<'_, PyBytes>>
|
||||||
where
|
where
|
||||||
F: FnOnce(&mut [u8]) -> PyResult<()>,
|
F: FnOnce(&mut [u8]) -> PyResult<()>,
|
||||||
{
|
{
|
||||||
unsafe {
|
unsafe {
|
||||||
let pyptr = ffi::PyBytes_FromStringAndSize(std::ptr::null(), len as ffi::Py_ssize_t);
|
let pyptr = ffi::PyBytes_FromStringAndSize(std::ptr::null(), len as ffi::Py_ssize_t);
|
||||||
// Check for an allocation error and return it
|
// Check for an allocation error and return it
|
||||||
let pypybytes: Py<PyBytes> = Py::from_owned_ptr_or_err(py, pyptr)?;
|
let pybytes = pyptr.assume_owned_or_err(py)?.downcast_into_unchecked();
|
||||||
let buffer: *mut u8 = ffi::PyBytes_AsString(pyptr).cast();
|
let buffer: *mut u8 = ffi::PyBytes_AsString(pyptr).cast();
|
||||||
debug_assert!(!buffer.is_null());
|
debug_assert!(!buffer.is_null());
|
||||||
// Zero-initialise the uninitialised bytestring
|
// Zero-initialise the uninitialised bytestring
|
||||||
std::ptr::write_bytes(buffer, 0u8, len);
|
std::ptr::write_bytes(buffer, 0u8, len);
|
||||||
// (Further) Initialise the bytestring in init
|
// (Further) Initialise the bytestring in init
|
||||||
// If init returns an Err, pypybytearray will automatically deallocate the buffer
|
// If init returns an Err, pypybytearray will automatically deallocate the buffer
|
||||||
init(std::slice::from_raw_parts_mut(buffer, len)).map(|_| pypybytes.into_ref(py))
|
init(std::slice::from_raw_parts_mut(buffer, len)).map(|_| pybytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Deprecated form of [`PyBytes::bound_from_ptr`].
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// See [`PyBytes::bound_from_ptr`].
|
||||||
|
#[cfg_attr(
|
||||||
|
not(feature = "gil-refs"),
|
||||||
|
deprecated(
|
||||||
|
since = "0.21.0",
|
||||||
|
note = "`PyBytes::from_ptr` will be replaced by `PyBytes::bound_from_ptr` in a future PyO3 version"
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub unsafe fn from_ptr(py: Python<'_>, ptr: *const u8, len: usize) -> &PyBytes {
|
||||||
|
Self::bound_from_ptr(py, ptr, len).into_gil_ref()
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates a new Python byte string object from a raw pointer and length.
|
/// Creates a new Python byte string object from a raw pointer and length.
|
||||||
///
|
///
|
||||||
/// Panics if out of memory.
|
/// Panics if out of memory.
|
||||||
|
@ -79,11 +127,10 @@ impl PyBytes {
|
||||||
/// leading pointer of a slice of length `len`. [As with
|
/// leading pointer of a slice of length `len`. [As with
|
||||||
/// `std::slice::from_raw_parts`, this is
|
/// `std::slice::from_raw_parts`, this is
|
||||||
/// unsafe](https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety).
|
/// unsafe](https://doc.rust-lang.org/std/slice/fn.from_raw_parts.html#safety).
|
||||||
pub unsafe fn from_ptr(py: Python<'_>, ptr: *const u8, len: usize) -> &PyBytes {
|
pub unsafe fn bound_from_ptr(py: Python<'_>, ptr: *const u8, len: usize) -> Bound<'_, PyBytes> {
|
||||||
py.from_owned_ptr(ffi::PyBytes_FromStringAndSize(
|
ffi::PyBytes_FromStringAndSize(ptr as *const _, len as isize)
|
||||||
ptr as *const _,
|
.assume_owned(py)
|
||||||
len as isize,
|
.downcast_into_unchecked()
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the Python string as a byte slice.
|
/// Gets the Python string as a byte slice.
|
||||||
|
@ -142,6 +189,15 @@ impl<I: SliceIndex<[u8]>> Index<I> for PyBytes {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is the same way [Vec] is indexed.
|
||||||
|
impl<I: SliceIndex<[u8]>> Index<I> for Bound<'_, PyBytes> {
|
||||||
|
type Output = I::Output;
|
||||||
|
|
||||||
|
fn index(&self, index: I) -> &Self::Output {
|
||||||
|
&self.as_bytes()[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Special-purpose trait impl to efficiently handle both `bytes` and `bytearray`
|
/// Special-purpose trait impl to efficiently handle both `bytes` and `bytearray`
|
||||||
///
|
///
|
||||||
/// If the source object is a `bytes` object, the `Cow` will be borrowed and
|
/// If the source object is a `bytes` object, the `Cow` will be borrowed and
|
||||||
|
@ -160,7 +216,7 @@ impl<'source> FromPyObject<'source> for Cow<'source, [u8]> {
|
||||||
|
|
||||||
impl ToPyObject for Cow<'_, [u8]> {
|
impl ToPyObject for Cow<'_, [u8]> {
|
||||||
fn to_object(&self, py: Python<'_>) -> Py<PyAny> {
|
fn to_object(&self, py: Python<'_>) -> Py<PyAny> {
|
||||||
PyBytes::new(py, self.as_ref()).into()
|
PyBytes::new_bound(py, self.as_ref()).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,6 +227,7 @@ impl IntoPy<Py<PyAny>> for Cow<'_, [u8]> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -182,6 +239,17 @@ mod tests {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bound_bytes_index() {
|
||||||
|
Python::with_gil(|py| {
|
||||||
|
let bytes = PyBytes::new_bound(py, b"Hello World");
|
||||||
|
assert_eq!(bytes[1], b'e');
|
||||||
|
|
||||||
|
let bytes = &bytes;
|
||||||
|
assert_eq!(bytes[1], b'e');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_bytes_new_with() -> super::PyResult<()> {
|
fn test_bytes_new_with() -> super::PyResult<()> {
|
||||||
Python::with_gil(|py| -> super::PyResult<()> {
|
Python::with_gil(|py| -> super::PyResult<()> {
|
||||||
|
|
|
@ -20,8 +20,8 @@ fn test_pybytes_bytes_conversion() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn bytes_vec_conversion(py: Python<'_>, bytes: Vec<u8>) -> &PyBytes {
|
fn bytes_vec_conversion(py: Python<'_>, bytes: Vec<u8>) -> Bound<'_, PyBytes> {
|
||||||
PyBytes::new(py, bytes.as_slice())
|
PyBytes::new_bound(py, bytes.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -43,7 +43,7 @@ fn test_bytearray_vec_conversion() {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_py_as_bytes() {
|
fn test_py_as_bytes() {
|
||||||
let pyobj: pyo3::Py<pyo3::types::PyBytes> =
|
let pyobj: pyo3::Py<pyo3::types::PyBytes> =
|
||||||
Python::with_gil(|py| pyo3::types::PyBytes::new(py, b"abc").into_py(py));
|
Python::with_gil(|py| pyo3::types::PyBytes::new_bound(py, b"abc").unbind());
|
||||||
|
|
||||||
let data = Python::with_gil(|py| pyobj.as_bytes(py));
|
let data = Python::with_gil(|py| pyobj.as_bytes(py));
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue