pyo3/conversions/
bytes.rs

1#![cfg(feature = "bytes")]
2
3//! Conversions to and from [bytes](https://docs.rs/bytes/latest/bytes/)'s [`Bytes`].
4//!
5//! This is useful for efficiently converting Python's `bytes` types efficiently.
6//! While `bytes` will be directly borrowed, converting from `bytearray` will result in a copy.
7//!
8//! When converting `Bytes` back into Python, this will do a copy, just like `&[u8]` and `Vec<u8>`.
9//!
10//! # When to use `Bytes`
11//!
12//! Unless you specifically need [`Bytes`] for ref-counted ownership and sharing,
13//! you may find that using `&[u8]`, `Vec<u8>`, [`Bound<PyBytes>`], or [`PyBackedBytes`]
14//! is simpler for most use cases.
15//!
16//! # Setup
17//!
18//! To use this feature, add in your **`Cargo.toml`**:
19//!
20//! ```toml
21//! [dependencies]
22//! bytes = "1.10"
23#![doc = concat!("pyo3 = { version = \"", env!("CARGO_PKG_VERSION"),  "\", features = [\"bytes\"] }")]
24//!
25//! Note that you must use compatible versions of bytes and PyO3.
26//!
27//! # Example
28//!
29//! Rust code to create functions which return `Bytes` or take `Bytes` as arguments:
30//!
31//! ```rust,no_run
32//! use pyo3::prelude::*;
33//!
34//! #[pyfunction]
35//! fn get_message_bytes() -> Bytes {
36//!     Bytes::from_static(b"Hello Python!")
37//! }
38//!
39//! #[pyfunction]
40//! fn num_bytes(bytes: Bytes) -> usize {
41//!     bytes.len()
42//! }
43//!
44//! #[pymodule]
45//! fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
46//!     m.add_function(wrap_pyfunction!(get_message_bytes, m)?)?;
47//!     m.add_function(wrap_pyfunction!(num_bytes, m)?)?;
48//!     Ok(())
49//! }
50//! ```
51//!
52//! Python code that calls these functions:
53//!
54//! ```python
55//! from my_module import get_message_bytes, num_bytes
56//!
57//! message = get_message_bytes()
58//! assert message == b"Hello Python!"
59//!
60//! size = num_bytes(message)
61//! assert size == 13
62//! ```
63use bytes::Bytes;
64
65use crate::conversion::IntoPyObject;
66use crate::instance::Bound;
67use crate::pybacked::PyBackedBytes;
68use crate::types::any::PyAnyMethods;
69use crate::types::PyBytes;
70use crate::{FromPyObject, PyAny, PyErr, PyResult, Python};
71
72impl FromPyObject<'_> for Bytes {
73    fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Self> {
74        Ok(Bytes::from_owner(ob.extract::<PyBackedBytes>()?))
75    }
76}
77
78impl<'py> IntoPyObject<'py> for Bytes {
79    type Target = PyBytes;
80    type Output = Bound<'py, Self::Target>;
81    type Error = PyErr;
82
83    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
84        Ok(PyBytes::new(py, &self))
85    }
86}
87
88impl<'py> IntoPyObject<'py> for &Bytes {
89    type Target = PyBytes;
90    type Output = Bound<'py, Self::Target>;
91    type Error = PyErr;
92
93    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
94        Ok(PyBytes::new(py, self))
95    }
96}
97
98#[cfg(test)]
99mod tests {
100    use super::*;
101    use crate::types::{PyByteArray, PyByteArrayMethods, PyBytes};
102    use crate::Python;
103
104    #[test]
105    fn test_bytes() {
106        Python::attach(|py| {
107            let py_bytes = PyBytes::new(py, b"foobar");
108            let bytes: Bytes = py_bytes.extract().unwrap();
109            assert_eq!(&*bytes, b"foobar");
110
111            let bytes = Bytes::from_static(b"foobar").into_pyobject(py).unwrap();
112            assert!(bytes.is_instance_of::<PyBytes>());
113        });
114    }
115
116    #[test]
117    fn test_bytearray() {
118        Python::attach(|py| {
119            let py_bytearray = PyByteArray::new(py, b"foobar");
120            let bytes: Bytes = py_bytearray.extract().unwrap();
121            assert_eq!(&*bytes, b"foobar");
122
123            // Editing the bytearray should not change extracted Bytes
124            unsafe { py_bytearray.as_bytes_mut()[0] = b'x' };
125            assert_eq!(&bytes, "foobar");
126            assert_eq!(&py_bytearray.extract::<Vec<u8>>().unwrap(), b"xoobar");
127        });
128    }
129}