Skip to content

Commit

Permalink
feature gate deprecated APIs for PyString
Browse files Browse the repository at this point in the history
  • Loading branch information
Icxolu committed Apr 19, 2024
1 parent cd28e14 commit d3fb38c
Showing 1 changed file with 54 additions and 50 deletions.
104 changes: 54 additions & 50 deletions src/types/string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,12 +136,10 @@ pyobject_native_type_core!(PyString, pyobject_native_static_type_object!(ffi::Py

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"
)
#[cfg(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()
Expand All @@ -161,12 +159,10 @@ impl PyString {
}

/// Deprecated form of [`PyString::intern_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyString::intern` will be replaced by `PyString::intern_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyString::intern` will be replaced by `PyString::intern_bound` in a future PyO3 version"
)]
pub fn intern<'py>(py: Python<'py>, s: &str) -> &'py Self {
Self::intern_bound(py, s).into_gil_ref()
Expand All @@ -176,8 +172,8 @@ impl PyString {
///
/// This will return a reference to the same Python string object if called repeatedly with the same string.
///
/// Note that while this is more memory efficient than [`PyString::new`], it unconditionally allocates a
/// temporary Python string object and is thereby slower than [`PyString::new`].
/// Note that while this is more memory efficient than [`PyString::new_bound`], it unconditionally allocates a
/// temporary Python string object and is thereby slower than [`PyString::new_bound`].
///
/// Panics if out of memory.
pub fn intern_bound<'py>(py: Python<'py>, s: &str) -> Bound<'py, PyString> {
Expand All @@ -193,12 +189,10 @@ impl PyString {
}

/// Deprecated form of [`PyString::from_object_bound`].
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`PyString::from_object` will be replaced by `PyString::from_object_bound` in a future PyO3 version"
)
#[cfg(feature = "gil-refs")]
#[deprecated(
since = "0.21.0",
note = "`PyString::from_object` will be replaced by `PyString::from_object_bound` in a future PyO3 version"
)]
pub fn from_object<'py>(src: &'py PyAny, encoding: &str, errors: &str) -> PyResult<&'py Self> {
Self::from_object_bound(&src.as_borrowed(), encoding, errors).map(Bound::into_gil_ref)
Expand Down Expand Up @@ -502,7 +496,6 @@ impl IntoPy<Py<PyString>> for &'_ Py<PyString> {
}

#[cfg(test)]
#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))]
mod tests {
use super::*;
use crate::{PyObject, ToPyObject};
Expand All @@ -511,17 +504,19 @@ mod tests {
fn test_to_str_utf8() {
Python::with_gil(|py| {
let s = "ascii 🐈";
let obj: PyObject = PyString::new(py, s).into();
let py_string: &PyString = obj.downcast(py).unwrap();
let py_string = PyString::new_bound(py, s);
assert_eq!(s, py_string.to_str().unwrap());
})
}

#[test]
fn test_to_str_surrogate() {
Python::with_gil(|py| {
let obj: PyObject = py.eval(r"'\ud800'", None, None).unwrap().into();
let py_string: &PyString = obj.downcast(py).unwrap();
let py_string = py
.eval_bound(r"'\ud800'", None, None)
.unwrap()
.downcast_into::<PyString>()
.unwrap();
assert!(py_string.to_str().is_err());
})
}
Expand All @@ -530,8 +525,7 @@ mod tests {
fn test_to_str_unicode() {
Python::with_gil(|py| {
let s = "哈哈🐈";
let obj: PyObject = PyString::new(py, s).into();
let py_string: &PyString = obj.downcast(py).unwrap();
let py_string = PyString::new_bound(py, s);
assert_eq!(s, py_string.to_str().unwrap());
})
}
Expand All @@ -548,7 +542,7 @@ mod tests {
#[test]
fn test_encode_utf8_surrogate() {
Python::with_gil(|py| {
let obj: PyObject = py.eval(r"'\ud800'", None, None).unwrap().into();
let obj: PyObject = py.eval_bound(r"'\ud800'", None, None).unwrap().into();
assert!(obj
.bind(py)
.downcast::<PyString>()
Expand All @@ -561,11 +555,12 @@ mod tests {
#[test]
fn test_to_string_lossy() {
Python::with_gil(|py| {
let obj: PyObject = py
.eval(r"'🐈 Hello \ud800World'", None, None)
let py_string = py
.eval_bound(r"'🐈 Hello \ud800World'", None, None)
.unwrap()
.into();
let py_string: &PyString = obj.downcast(py).unwrap();
.downcast_into::<PyString>()
.unwrap();

assert_eq!(py_string.to_string_lossy(), "🐈 Hello ���World");
})
}
Expand All @@ -574,7 +569,7 @@ mod tests {
fn test_debug_string() {
Python::with_gil(|py| {
let v = "Hello\n".to_object(py);
let s: &PyString = v.downcast(py).unwrap();
let s = v.downcast_bound::<PyString>(py).unwrap();
assert_eq!(format!("{:?}", s), "'Hello\\n'");
})
}
Expand All @@ -583,7 +578,7 @@ mod tests {
fn test_display_string() {
Python::with_gil(|py| {
let v = "Hello\n".to_object(py);
let s: &PyString = v.downcast(py).unwrap();
let s = v.downcast_bound::<PyString>(py).unwrap();
assert_eq!(format!("{}", s), "Hello\n");
})
}
Expand All @@ -592,7 +587,7 @@ mod tests {
#[cfg(not(Py_LIMITED_API))]
fn test_string_data_ucs1() {
Python::with_gil(|py| {
let s = PyString::new(py, "hello, world");
let s = PyString::new_bound(py, "hello, world");
let data = unsafe { s.data().unwrap() };

assert_eq!(data, PyStringData::Ucs1(b"hello, world"));
Expand All @@ -615,11 +610,13 @@ mod tests {
)
};
assert!(!ptr.is_null());
let s: &PyString = unsafe { py.from_owned_ptr(ptr) };
let s = unsafe { ptr.assume_owned(py).downcast_into_unchecked::<PyString>() };
let data = unsafe { s.data().unwrap() };
assert_eq!(data, PyStringData::Ucs1(b"f\xfe"));
let err = data.to_string(py).unwrap_err();
assert!(err.get_type(py).is(py.get_type::<PyUnicodeDecodeError>()));
assert!(err
.get_type_bound(py)
.is(&py.get_type_bound::<PyUnicodeDecodeError>()));
assert!(err
.to_string()
.contains("'utf-8' codec can't decode byte 0xfe in position 1"));
Expand All @@ -631,7 +628,7 @@ mod tests {
#[cfg(not(Py_LIMITED_API))]
fn test_string_data_ucs2() {
Python::with_gil(|py| {
let s = py.eval("'foo\\ud800'", None, None).unwrap();
let s = py.eval_bound("'foo\\ud800'", None, None).unwrap();
let py_string = s.downcast::<PyString>().unwrap();
let data = unsafe { py_string.data().unwrap() };

Expand All @@ -657,11 +654,13 @@ mod tests {
)
};
assert!(!ptr.is_null());
let s: &PyString = unsafe { py.from_owned_ptr(ptr) };
let s = unsafe { ptr.assume_owned(py).downcast_into_unchecked::<PyString>() };
let data = unsafe { s.data().unwrap() };
assert_eq!(data, PyStringData::Ucs2(&[0xff22, 0xd800]));
let err = data.to_string(py).unwrap_err();
assert!(err.get_type(py).is(py.get_type::<PyUnicodeDecodeError>()));
assert!(err
.get_type_bound(py)
.is(&py.get_type_bound::<PyUnicodeDecodeError>()));
assert!(err
.to_string()
.contains("'utf-16' codec can't decode bytes in position 0-3"));
Expand All @@ -674,7 +673,7 @@ mod tests {
fn test_string_data_ucs4() {
Python::with_gil(|py| {
let s = "哈哈🐈";
let py_string = PyString::new(py, s);
let py_string = PyString::new_bound(py, s);
let data = unsafe { py_string.data().unwrap() };

assert_eq!(data, PyStringData::Ucs4(&[21704, 21704, 128008]));
Expand All @@ -696,11 +695,13 @@ mod tests {
)
};
assert!(!ptr.is_null());
let s: &PyString = unsafe { py.from_owned_ptr(ptr) };
let s = unsafe { ptr.assume_owned(py).downcast_into_unchecked::<PyString>() };
let data = unsafe { s.data().unwrap() };
assert_eq!(data, PyStringData::Ucs4(&[0x20000, 0xd800]));
let err = data.to_string(py).unwrap_err();
assert!(err.get_type(py).is(py.get_type::<PyUnicodeDecodeError>()));
assert!(err
.get_type_bound(py)
.is(&py.get_type_bound::<PyUnicodeDecodeError>()));
assert!(err
.to_string()
.contains("'utf-32' codec can't decode bytes in position 0-7"));
Expand All @@ -711,15 +712,15 @@ mod tests {
#[test]
fn test_intern_string() {
Python::with_gil(|py| {
let py_string1 = PyString::intern(py, "foo");
let py_string1 = PyString::intern_bound(py, "foo");
assert_eq!(py_string1.to_str().unwrap(), "foo");

let py_string2 = PyString::intern(py, "foo");
let py_string2 = PyString::intern_bound(py, "foo");
assert_eq!(py_string2.to_str().unwrap(), "foo");

assert_eq!(py_string1.as_ptr(), py_string2.as_ptr());

let py_string3 = PyString::intern(py, "bar");
let py_string3 = PyString::intern_bound(py, "bar");
assert_eq!(py_string3.to_str().unwrap(), "bar");

assert_ne!(py_string1.as_ptr(), py_string3.as_ptr());
Expand All @@ -730,7 +731,7 @@ mod tests {
fn test_py_to_str_utf8() {
Python::with_gil(|py| {
let s = "ascii 🐈";
let py_string: Py<PyString> = PyString::new(py, s).into_py(py);
let py_string: Py<PyString> = PyString::new_bound(py, s).into_py(py);

#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
assert_eq!(s, py_string.to_str(py).unwrap());
Expand All @@ -742,8 +743,11 @@ mod tests {
#[test]
fn test_py_to_str_surrogate() {
Python::with_gil(|py| {
let py_string: Py<PyString> =
py.eval(r"'\ud800'", None, None).unwrap().extract().unwrap();
let py_string: Py<PyString> = py
.eval_bound(r"'\ud800'", None, None)
.unwrap()
.extract()
.unwrap();

#[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
assert!(py_string.to_str(py).is_err());
Expand All @@ -756,7 +760,7 @@ mod tests {
fn test_py_to_string_lossy() {
Python::with_gil(|py| {
let py_string: Py<PyString> = py
.eval(r"'🐈 Hello \ud800World'", None, None)
.eval_bound(r"'🐈 Hello \ud800World'", None, None)
.unwrap()
.extract()
.unwrap();
Expand Down

0 comments on commit d3fb38c

Please sign in to comment.