add `PyString::new_bound`

This commit is contained in:
David Hewitt 2023-11-21 11:41:43 +00:00
parent 7549a21154
commit c47565666d
15 changed files with 96 additions and 65 deletions

View File

@ -236,7 +236,7 @@ struct RustyTransparentStruct {
# use pyo3::types::PyString; # use pyo3::types::PyString;
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
# let s = PyString::new(py, "test"); # let s = PyString::new_bound(py, "test");
# #
# let tup: RustyTransparentTupleStruct = s.extract()?; # let tup: RustyTransparentTupleStruct = s.extract()?;
# assert_eq!(tup.0, "test"); # assert_eq!(tup.0, "test");
@ -303,7 +303,7 @@ enum RustyEnum<'a> {
# ); # );
# } # }
# { # {
# let thing = PyString::new(py, "text"); # let thing = PyString::new_bound(py, "text");
# let rust_thing: RustyEnum<'_> = thing.extract()?; # let rust_thing: RustyEnum<'_> = thing.extract()?;
# #
# assert_eq!( # assert_eq!(

View File

@ -1,18 +1,16 @@
use codspeed_criterion_compat::{black_box, criterion_group, criterion_main, Bencher, Criterion}; use codspeed_criterion_compat::{black_box, criterion_group, criterion_main, Bencher, Criterion};
use pyo3::{ use pyo3::{
prelude::*,
types::{PyDict, PyFloat, PyInt, PyString}, types::{PyDict, PyFloat, PyInt, PyString},
IntoPy, PyAny, PyObject, Python, IntoPy, PyAny, PyObject, Python,
}; };
fn extract_str_extract_success(bench: &mut Bencher<'_>) { fn extract_str_extract_success(bench: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
let s = PyString::new(py, "Hello, World!") as &PyAny; let s = &PyString::new_bound(py, "Hello, World!");
bench.iter(|| { bench.iter(|| black_box(s).extract::<&str>().unwrap());
let v = black_box(s).extract::<&str>().unwrap();
black_box(v);
});
}); });
} }
@ -27,14 +25,14 @@ fn extract_str_extract_fail(bench: &mut Bencher<'_>) {
}); });
} }
#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
fn extract_str_downcast_success(bench: &mut Bencher<'_>) { fn extract_str_downcast_success(bench: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
let s = PyString::new(py, "Hello, World!") as &PyAny; let s = &PyString::new_bound(py, "Hello, World!");
bench.iter(|| { bench.iter(|| {
let py_str = black_box(s).downcast::<PyString>().unwrap(); let py_str = black_box(s).downcast::<PyString>().unwrap();
let v = py_str.to_str().unwrap(); py_str.to_str().unwrap()
black_box(v);
}); });
}); });
} }
@ -147,6 +145,7 @@ fn extract_float_downcast_fail(bench: &mut Bencher<'_>) {
fn criterion_benchmark(c: &mut Criterion) { fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("extract_str_extract_success", extract_str_extract_success); c.bench_function("extract_str_extract_success", extract_str_extract_success);
c.bench_function("extract_str_extract_fail", extract_str_extract_fail); c.bench_function("extract_str_extract_fail", extract_str_extract_fail);
#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
c.bench_function("extract_str_downcast_success", extract_str_downcast_success); c.bench_function("extract_str_downcast_success", extract_str_downcast_success);
c.bench_function("extract_str_downcast_fail", extract_str_downcast_fail); c.bench_function("extract_str_downcast_fail", extract_str_downcast_fail);
c.bench_function("extract_int_extract_success", extract_int_extract_success); c.bench_function("extract_int_extract_success", extract_int_extract_success);

View File

@ -14,10 +14,9 @@ enum ManyTypes {
fn enum_from_pyobject(b: &mut Bencher<'_>) { fn enum_from_pyobject(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = PyString::new(py, "hello world"); let any: &Bound<'_, PyAny> = &PyString::new_bound(py, "hello world");
b.iter(|| {
let _: ManyTypes = obj.extract().unwrap(); b.iter(|| any.extract::<ManyTypes>().unwrap());
});
}) })
} }
@ -39,7 +38,7 @@ fn list_via_extract(b: &mut Bencher<'_>) {
fn not_a_list_via_downcast(b: &mut Bencher<'_>) { fn not_a_list_via_downcast(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
let any: &PyAny = PyString::new(py, "foobar").into(); let any: &Bound<'_, PyAny> = &PyString::new_bound(py, "foobar");
b.iter(|| black_box(any).downcast::<PyList>().unwrap_err()); b.iter(|| black_box(any).downcast::<PyList>().unwrap_err());
}) })
@ -47,25 +46,25 @@ fn not_a_list_via_downcast(b: &mut Bencher<'_>) {
fn not_a_list_via_extract(b: &mut Bencher<'_>) { fn not_a_list_via_extract(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
let any: &PyAny = PyString::new(py, "foobar").into(); let any: &Bound<'_, PyAny> = &PyString::new_bound(py, "foobar");
b.iter(|| black_box(any).extract::<&PyList>().unwrap_err()); b.iter(|| black_box(any).extract::<Bound<'_, PyList>>().unwrap_err());
}) })
} }
#[derive(FromPyObject)] #[derive(FromPyObject)]
enum ListOrNotList<'a> { enum ListOrNotList<'a> {
List(&'a PyList), List(Bound<'a, PyList>),
NotList(&'a PyAny), NotList(Bound<'a, PyAny>),
} }
fn not_a_list_via_extract_enum(b: &mut Bencher<'_>) { fn not_a_list_via_extract_enum(b: &mut Bencher<'_>) {
Python::with_gil(|py| { Python::with_gil(|py| {
let any: &PyAny = PyString::new(py, "foobar").into(); let any: &Bound<'_, PyAny> = &PyString::new_bound(py, "foobar");
b.iter(|| match black_box(any).extract::<ListOrNotList<'_>>() { b.iter(|| match black_box(any).extract::<ListOrNotList<'_>>() {
Ok(ListOrNotList::List(_list)) => panic!(), Ok(ListOrNotList::List(_list)) => panic!(),
Ok(ListOrNotList::NotList(_any)) => (), Ok(ListOrNotList::NotList(any)) => any,
Err(_) => panic!(), Err(_) => panic!(),
}); });
}) })

View File

@ -190,7 +190,7 @@ pub trait IntoPy<T>: Sized {
/// ///
/// # fn main() -> PyResult<()> { /// # fn main() -> PyResult<()> {
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let obj: Py<PyString> = PyString::new(py, "blah").into(); /// let obj: Py<PyString> = PyString::new_bound(py, "blah").into();
/// ///
/// // Straight from an owned reference /// // Straight from an owned reference
/// let s: &str = obj.extract(py)?; /// let s: &str = obj.extract(py)?;

View File

@ -99,11 +99,11 @@ mod test_ipaddr {
#[test] #[test]
fn test_from_pystring() { fn test_from_pystring() {
Python::with_gil(|py| { Python::with_gil(|py| {
let py_str = PyString::new(py, "0:0:0:0:0:0:0:1"); let py_str = PyString::new_bound(py, "0:0:0:0:0:0:0:1");
let ip: IpAddr = py_str.to_object(py).extract(py).unwrap(); let ip: IpAddr = py_str.to_object(py).extract(py).unwrap();
assert_eq!(ip, IpAddr::from_str("::1").unwrap()); assert_eq!(ip, IpAddr::from_str("::1").unwrap());
let py_str = PyString::new(py, "invalid"); let py_str = PyString::new_bound(py, "invalid");
assert!(py_str.to_object(py).extract::<IpAddr>(py).is_err()); assert!(py_str.to_object(py).extract::<IpAddr>(py).is_err());
}); });
} }

View File

@ -11,14 +11,14 @@ use crate::{
impl ToPyObject for str { impl ToPyObject for str {
#[inline] #[inline]
fn to_object(&self, py: Python<'_>) -> PyObject { fn to_object(&self, py: Python<'_>) -> PyObject {
PyString::new(py, self).into() PyString::new_bound(py, self).into()
} }
} }
impl<'a> IntoPy<PyObject> for &'a str { impl<'a> IntoPy<PyObject> for &'a str {
#[inline] #[inline]
fn into_py(self, py: Python<'_>) -> PyObject { fn into_py(self, py: Python<'_>) -> PyObject {
PyString::new(py, self).into() PyString::new_bound(py, self).into()
} }
#[cfg(feature = "experimental-inspect")] #[cfg(feature = "experimental-inspect")]
@ -30,7 +30,7 @@ impl<'a> IntoPy<PyObject> for &'a str {
impl<'a> IntoPy<Py<PyString>> for &'a str { impl<'a> IntoPy<Py<PyString>> for &'a str {
#[inline] #[inline]
fn into_py(self, py: Python<'_>) -> Py<PyString> { fn into_py(self, py: Python<'_>) -> Py<PyString> {
PyString::new(py, self).into() PyString::new_bound(py, self).into()
} }
#[cfg(feature = "experimental-inspect")] #[cfg(feature = "experimental-inspect")]
@ -44,7 +44,7 @@ impl<'a> IntoPy<Py<PyString>> for &'a str {
impl ToPyObject for Cow<'_, str> { impl ToPyObject for Cow<'_, str> {
#[inline] #[inline]
fn to_object(&self, py: Python<'_>) -> PyObject { fn to_object(&self, py: Python<'_>) -> PyObject {
PyString::new(py, self).into() PyString::new_bound(py, self).into()
} }
} }
@ -65,7 +65,7 @@ impl IntoPy<PyObject> for Cow<'_, str> {
impl ToPyObject for String { impl ToPyObject for String {
#[inline] #[inline]
fn to_object(&self, py: Python<'_>) -> PyObject { fn to_object(&self, py: Python<'_>) -> PyObject {
PyString::new(py, self).into() PyString::new_bound(py, self).into()
} }
} }
@ -78,7 +78,7 @@ impl ToPyObject for char {
impl IntoPy<PyObject> for char { impl IntoPy<PyObject> for char {
fn into_py(self, py: Python<'_>) -> PyObject { fn into_py(self, py: Python<'_>) -> PyObject {
let mut bytes = [0u8; 4]; let mut bytes = [0u8; 4];
PyString::new(py, self.encode_utf8(&mut bytes)).into() PyString::new_bound(py, self.encode_utf8(&mut bytes)).into()
} }
#[cfg(feature = "experimental-inspect")] #[cfg(feature = "experimental-inspect")]
@ -89,7 +89,7 @@ impl IntoPy<PyObject> for char {
impl IntoPy<PyObject> for String { impl IntoPy<PyObject> for String {
fn into_py(self, py: Python<'_>) -> PyObject { fn into_py(self, py: Python<'_>) -> PyObject {
PyString::new(py, &self).into() PyString::new_bound(py, &self).into()
} }
#[cfg(feature = "experimental-inspect")] #[cfg(feature = "experimental-inspect")]
@ -101,7 +101,7 @@ impl IntoPy<PyObject> for String {
impl<'a> IntoPy<PyObject> for &'a String { impl<'a> IntoPy<PyObject> for &'a String {
#[inline] #[inline]
fn into_py(self, py: Python<'_>) -> PyObject { fn into_py(self, py: Python<'_>) -> PyObject {
PyString::new(py, self).into() PyString::new_bound(py, self).into()
} }
#[cfg(feature = "experimental-inspect")] #[cfg(feature = "experimental-inspect")]

View File

@ -206,7 +206,7 @@ impl PyErr {
/// assert_eq!(err.to_string(), "TypeError: "); /// assert_eq!(err.to_string(), "TypeError: ");
/// ///
/// // Case #3: Invalid exception value /// // Case #3: Invalid exception value
/// let err = PyErr::from_value(PyString::new(py, "foo").into()); /// let err = PyErr::from_value(PyString::new_bound(py, "foo").as_gil_ref());
/// assert_eq!( /// assert_eq!(
/// err.to_string(), /// err.to_string(),
/// "TypeError: exceptions must derive from BaseException" /// "TypeError: exceptions must derive from BaseException"

View File

@ -3,7 +3,7 @@ use crate::Python;
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
use crate::{ use crate::{
types::{PyDict, PyString}, types::{any::PyAnyMethods, PyDict, PyString},
IntoPy, Py, PyAny, IntoPy, Py, PyAny,
}; };
#[cfg(not(any(Py_3_12, Py_LIMITED_API)))] #[cfg(not(any(Py_3_12, Py_LIMITED_API)))]
@ -98,7 +98,7 @@ fn test_timezone_from_offset_and_name() {
Python::with_gil(|py| { Python::with_gil(|py| {
let delta = PyDelta::new(py, 0, 100, 0, false).unwrap(); let delta = PyDelta::new(py, 0, 100, 0, false).unwrap();
let tzname = PyString::new(py, "testtz"); let tzname = PyString::new_bound(py, "testtz");
let tz: &PyAny = unsafe { let tz: &PyAny = unsafe {
py.from_borrowed_ptr(PyTimeZone_FromOffsetAndName( py.from_borrowed_ptr(PyTimeZone_FromOffsetAndName(
delta.as_ptr(), delta.as_ptr(),
@ -167,7 +167,7 @@ fn ascii_object_bitfield() {
fn ascii() { fn ascii() {
Python::with_gil(|py| { Python::with_gil(|py| {
// This test relies on implementation details of PyString. // This test relies on implementation details of PyString.
let s = PyString::new(py, "hello, world"); let s = PyString::new_bound(py, "hello, world");
let ptr = s.as_ptr(); let ptr = s.as_ptr();
unsafe { unsafe {
@ -209,7 +209,7 @@ fn ascii() {
fn ucs4() { fn ucs4() {
Python::with_gil(|py| { Python::with_gil(|py| {
let s = "哈哈🐈"; let s = "哈哈🐈";
let py_string = PyString::new(py, s); let py_string = PyString::new_bound(py, s);
let ptr = py_string.as_ptr(); let ptr = py_string.as_ptr();
unsafe { unsafe {

View File

@ -1624,7 +1624,7 @@ a = A()
assert!(instance assert!(instance
.getattr(py, "foo")? .getattr(py, "foo")?
.as_ref(py) .as_ref(py)
.eq(PyString::new(py, "bar"))?); .eq(PyString::new_bound(py, "bar"))?);
instance.getattr(py, "foo")?; instance.getattr(py, "foo")?;
Ok(()) Ok(())

View File

@ -72,7 +72,7 @@
//! use send_wrapper::SendWrapper; //! use send_wrapper::SendWrapper;
//! //!
//! Python::with_gil(|py| { //! Python::with_gil(|py| {
//! let string = PyString::new(py, "foo"); //! let string = PyString::new_bound(py, "foo");
//! //!
//! let wrapped = SendWrapper::new(string); //! let wrapped = SendWrapper::new(string);
//! //!
@ -80,7 +80,7 @@
//! # #[cfg(not(feature = "nightly"))] //! # #[cfg(not(feature = "nightly"))]
//! # { //! # {
//! // 💥 Unsound! 💥 //! // 💥 Unsound! 💥
//! let smuggled: &PyString = *wrapped; //! let smuggled: &Bound<'_, PyString> = &*wrapped;
//! println!("{:?}", smuggled); //! println!("{:?}", smuggled);
//! # } //! # }
//! }); //! });
@ -164,12 +164,12 @@ use std::os::raw::c_int;
/// use send_wrapper::SendWrapper; /// use send_wrapper::SendWrapper;
/// ///
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let string = PyString::new(py, "foo"); /// let string = PyString::new_bound(py, "foo");
/// ///
/// let wrapped = SendWrapper::new(string); /// let wrapped = SendWrapper::new(string);
/// ///
/// py.allow_threads(|| { /// py.allow_threads(|| {
/// let sneaky: &PyString = *wrapped; /// let sneaky: &Bound<'_, PyString> = &*wrapped;
/// ///
/// println!("{:?}", sneaky); /// println!("{:?}", sneaky);
/// }); /// });
@ -210,7 +210,7 @@ mod nightly {
/// # use pyo3::prelude::*; /// # use pyo3::prelude::*;
/// # use pyo3::types::PyString; /// # use pyo3::types::PyString;
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let string = PyString::new(py, "foo"); /// let string = PyString::new_bound(py, "foo");
/// ///
/// py.allow_threads(|| { /// py.allow_threads(|| {
/// println!("{:?}", string); /// println!("{:?}", string);
@ -238,7 +238,7 @@ mod nightly {
/// use send_wrapper::SendWrapper; /// use send_wrapper::SendWrapper;
/// ///
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let string = PyString::new(py, "foo"); /// let string = PyString::new_bound(py, "foo");
/// ///
/// let wrapped = SendWrapper::new(string); /// let wrapped = SendWrapper::new(string);
/// ///
@ -521,7 +521,7 @@ impl<'py> Python<'py> {
/// use pyo3::types::PyString; /// use pyo3::types::PyString;
/// ///
/// fn parallel_print(py: Python<'_>) { /// fn parallel_print(py: Python<'_>) {
/// let s = PyString::new(py, "This object cannot be accessed without holding the GIL >_<"); /// let s = PyString::new_bound(py, "This object cannot be accessed without holding the GIL >_<");
/// py.allow_threads(move || { /// py.allow_threads(move || {
/// println!("{:?}", s); // This causes a compile error. /// println!("{:?}", s); // This causes a compile error.
/// }); /// });
@ -1004,6 +1004,7 @@ impl Python<'_> {
/// The `Ungil` bound on the closure does prevent hanging on to existing GIL-bound references /// The `Ungil` bound on the closure does prevent hanging on to existing GIL-bound references
/// ///
/// ```compile_fail /// ```compile_fail
/// # #![allow(deprecated)]
/// # use pyo3::prelude::*; /// # use pyo3::prelude::*;
/// # use pyo3::types::PyString; /// # use pyo3::types::PyString;
/// ///

View File

@ -219,7 +219,7 @@ impl PyAny {
/// # fn main() -> PyResult<()> { /// # fn main() -> PyResult<()> {
/// Python::with_gil(|py| -> PyResult<()> { /// Python::with_gil(|py| -> PyResult<()> {
/// let a = PyFloat::new_bound(py, 0_f64); /// let a = PyFloat::new_bound(py, 0_f64);
/// let b = PyString::new(py, "zero"); /// let b = PyString::new_bound(py, "zero");
/// assert!(a.compare(b).is_err()); /// assert!(a.compare(b).is_err());
/// Ok(()) /// Ok(())
/// })?; /// })?;
@ -1075,7 +1075,7 @@ pub trait PyAnyMethods<'py> {
/// # fn main() -> PyResult<()> { /// # fn main() -> PyResult<()> {
/// Python::with_gil(|py| -> PyResult<()> { /// Python::with_gil(|py| -> PyResult<()> {
/// let a = PyFloat::new_bound(py, 0_f64); /// let a = PyFloat::new_bound(py, 0_f64);
/// let b = PyString::new(py, "zero"); /// let b = PyString::new_bound(py, "zero");
/// assert!(a.compare(b).is_err()); /// assert!(a.compare(b).is_err());
/// Ok(()) /// Ok(())
/// })?; /// })?;

View File

@ -134,13 +134,29 @@ pub struct PyString(PyAny);
pyobject_native_type_core!(PyString, pyobject_native_static_type_object!(ffi::PyUnicode_Type), #checkfunction=ffi::PyUnicode_Check); pyobject_native_type_core!(PyString, pyobject_native_static_type_object!(ffi::PyUnicode_Type), #checkfunction=ffi::PyUnicode_Check);
impl PyString { impl PyString {
/// Deprecated form of [`PyString::new_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyString::new` will be replaced by `PyString::new_bound` in a future PyO3 version"
)
)]
pub fn new<'py>(py: Python<'py>, s: &str) -> &'py Self {
Self::new_bound(py, s).into_gil_ref()
}
/// Creates a new Python string object. /// Creates a new Python string object.
/// ///
/// Panics if out of memory. /// Panics if out of memory.
pub fn new<'p>(py: Python<'p>, s: &str) -> &'p PyString { pub fn new_bound<'py>(py: Python<'py>, s: &str) -> Bound<'py, PyString> {
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::PyUnicode_FromStringAndSize(ptr, len)) } unsafe {
ffi::PyUnicode_FromStringAndSize(ptr, len)
.assume_owned(py)
.downcast_into_unchecked()
}
} }
/// Intern the given string /// Intern the given string
@ -452,6 +468,7 @@ impl IntoPy<Py<PyString>> for &'_ Py<PyString> {
} }
#[cfg(test)] #[cfg(test)]
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
mod tests { mod tests {
use super::*; use super::*;
use crate::Python; use crate::Python;

View File

@ -73,7 +73,7 @@ impl Iter {
let res = reader_ref let res = reader_ref
.inner .inner
.get(&b) .get(&b)
.map(|s| PyString::new(py, s).into()); .map(|s| PyString::new_bound(py, s).into());
Ok(res) Ok(res)
} }
None => Ok(None), None => Ok(None),

View File

@ -3,7 +3,7 @@ use pyo3::types::PyString;
fn main() { fn main() {
Python::with_gil(|py| { Python::with_gil(|py| {
let string = PyString::new(py, "foo"); let string = PyString::new_bound(py, "foo");
py.allow_threads(|| { py.allow_threads(|| {
println!("{:?}", string); println!("{:?}", string);

View File

@ -1,4 +1,4 @@
error[E0277]: `UnsafeCell<PyObject>` cannot be shared between threads safely error[E0277]: `*mut pyo3::Python<'static>` cannot be shared between threads safely
--> tests/ui/not_send2.rs:8:26 --> tests/ui/not_send2.rs:8:26
| |
8 | py.allow_threads(|| { 8 | py.allow_threads(|| {
@ -7,21 +7,36 @@ error[E0277]: `UnsafeCell<PyObject>` cannot be shared between threads safely
| | required by a bound introduced by this call | | required by a bound introduced by this call
9 | | println!("{:?}", string); 9 | | println!("{:?}", string);
10 | | }); 10 | | });
| |_________^ `UnsafeCell<PyObject>` cannot be shared between threads safely | |_________^ `*mut pyo3::Python<'static>` cannot be shared between threads safely
| |
= help: within `&PyString`, the trait `Sync` is not implemented for `UnsafeCell<PyObject>` = help: within `pyo3::Bound<'_, PyString>`, the trait `Sync` is not implemented for `*mut pyo3::Python<'static>`
note: required because it appears within the type `PyAny` note: required because it appears within the type `PhantomData<*mut Python<'static>>`
--> src/types/any.rs --> $RUST/core/src/marker.rs
| |
| pub struct PyAny(UnsafeCell<ffi::PyObject>); | pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `NotSend`
--> src/impl_/not_send.rs
|
| pub(crate) struct NotSend(PhantomData<*mut Python<'static>>);
| ^^^^^^^
= note: required because it appears within the type `(&GILGuard, NotSend)`
note: required because it appears within the type `PhantomData<(&GILGuard, NotSend)>`
--> $RUST/core/src/marker.rs
|
| pub struct PhantomData<T: ?Sized>;
| ^^^^^^^^^^^
note: required because it appears within the type `Python<'_>`
--> src/marker.rs
|
| pub struct Python<'py>(PhantomData<(&'py GILGuard, NotSend)>);
| ^^^^^^
note: required because it appears within the type `Bound<'_, PyString>`
--> src/instance.rs
|
| pub struct Bound<'py, T>(Python<'py>, ManuallyDrop<Py<T>>);
| ^^^^^ | ^^^^^
note: required because it appears within the type `PyString` = note: required for `&pyo3::Bound<'_, PyString>` to implement `Send`
--> src/types/string.rs
|
| pub struct PyString(PyAny);
| ^^^^^^^^
= note: required because it appears within the type `&PyString`
= note: required for `&&PyString` to implement `Send`
note: required because it's used within this closure note: required because it's used within this closure
--> tests/ui/not_send2.rs:8:26 --> tests/ui/not_send2.rs:8:26
| |