port `Python::eval` to `Bound` API

This commit is contained in:
Icxolu 2024-02-05 19:13:02 +01:00
parent 9bb001108b
commit 33dc33ecec
21 changed files with 149 additions and 99 deletions

View File

@ -152,9 +152,9 @@ fn main() -> PyResult<()> {
let sys = py.import("sys")?; let sys = py.import("sys")?;
let version: String = sys.getattr("version")?.extract()?; let version: String = sys.getattr("version")?.extract()?;
let locals = [("os", py.import("os")?)].into_py_dict(py); let locals = [("os", py.import("os")?)].into_py_dict(py).as_borrowed();
let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'"; let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'";
let user: String = py.eval(code, None, Some(&locals))?.extract()?; let user: String = py.eval_bound(code, None, Some(&locals))?.extract()?;
println!("Hello {}, I'm Python {}", user, version); println!("Hello {}, I'm Python {}", user, version);
Ok(()) Ok(())

View File

@ -155,7 +155,7 @@ struct RustyStruct {
# #
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
# let py_dict = py.eval("{'foo': 'foo', 'bar': 'bar', 'foobar': 'foobar'}", None, None)?; # let py_dict = py.eval_bound("{'foo': 'foo', 'bar': 'bar', 'foobar': 'foobar'}", None, None)?;
# let rustystruct: RustyStruct = py_dict.extract()?; # let rustystruct: RustyStruct = py_dict.extract()?;
# assert_eq!(rustystruct.foo, "foo"); # assert_eq!(rustystruct.foo, "foo");
# assert_eq!(rustystruct.bar, "bar"); # assert_eq!(rustystruct.bar, "bar");

View File

@ -27,7 +27,7 @@ very simple and easy-to-understand programs like this:
# 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 hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?; let hello = py.eval_bound("\"Hello World!\"", None, None)?.downcast_into::<PyString>()?;
println!("Python says: {}", hello); println!("Python says: {}", hello);
Ok(()) Ok(())
})?; })?;
@ -48,7 +48,7 @@ of the time we don't have to think about this, but consider the following:
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> { Python::with_gil(|py| -> PyResult<()> {
for _ in 0..10 { for _ in 0..10 {
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?; let hello = py.eval_bound("\"Hello World!\"", None, None)?.downcast_into::<PyString>()?;
println!("Python says: {}", hello); println!("Python says: {}", hello);
} }
// There are 10 copies of `hello` on Python's heap here. // There are 10 copies of `hello` on Python's heap here.
@ -76,7 +76,7 @@ is to acquire and release the GIL with each iteration of the loop.
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
for _ in 0..10 { for _ in 0..10 {
Python::with_gil(|py| -> PyResult<()> { Python::with_gil(|py| -> PyResult<()> {
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?; let hello = py.eval_bound("\"Hello World!\"", None, None)?.downcast_into::<PyString>()?;
println!("Python says: {}", hello); println!("Python says: {}", hello);
Ok(()) Ok(())
})?; // only one copy of `hello` at a time })?; // only one copy of `hello` at a time
@ -97,7 +97,7 @@ Python::with_gil(|py| -> PyResult<()> {
for _ in 0..10 { for _ in 0..10 {
let pool = unsafe { py.new_pool() }; let pool = unsafe { py.new_pool() };
let py = pool.python(); let py = pool.python();
let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?; let hello = py.eval_bound("\"Hello World!\"", None, None)?.downcast_into::<PyString>()?;
println!("Python says: {}", hello); println!("Python says: {}", hello);
} }
Ok(()) Ok(())
@ -144,8 +144,8 @@ reference count reaches zero? It depends whether or not we are holding the GIL.
# 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 hello: Py<PyString> = py.eval("\"Hello World!\"", None, None)?.extract()?; let hello: Py<PyString> = py.eval_bound("\"Hello World!\"", None, None)?.extract()?;
println!("Python says: {}", hello.as_ref(py)); println!("Python says: {}", hello.bind(py));
Ok(()) Ok(())
})?; })?;
# Ok(()) # Ok(())
@ -166,7 +166,7 @@ we are *not* holding the GIL?
# use pyo3::types::PyString; # use pyo3::types::PyString;
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
let hello: Py<PyString> = Python::with_gil(|py| { let hello: Py<PyString> = Python::with_gil(|py| {
py.eval("\"Hello World!\"", None, None)?.extract() py.eval_bound("\"Hello World!\"", None, None)?.extract()
})?; })?;
// Do some stuff... // Do some stuff...
// Now sometime later in the program we want to access `hello`. // Now sometime later in the program we want to access `hello`.
@ -197,11 +197,11 @@ We can avoid the delay in releasing memory if we are careful to drop the
# use pyo3::types::PyString; # use pyo3::types::PyString;
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
let hello: Py<PyString> = let hello: Py<PyString> =
Python::with_gil(|py| py.eval("\"Hello World!\"", None, None)?.extract())?; Python::with_gil(|py| py.eval_bound("\"Hello World!\"", None, None)?.extract())?;
// Do some stuff... // Do some stuff...
// Now sometime later in the program: // Now sometime later in the program:
Python::with_gil(|py| { Python::with_gil(|py| {
println!("Python says: {}", hello.as_ref(py)); println!("Python says: {}", hello.bind(py));
drop(hello); // Memory released here. drop(hello); // Memory released here.
}); });
# Ok(()) # Ok(())
@ -219,11 +219,11 @@ until the GIL is dropped.
# use pyo3::types::PyString; # use pyo3::types::PyString;
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
let hello: Py<PyString> = let hello: Py<PyString> =
Python::with_gil(|py| py.eval("\"Hello World!\"", None, None)?.extract())?; Python::with_gil(|py| py.eval_bound("\"Hello World!\"", None, None)?.extract())?;
// Do some stuff... // Do some stuff...
// Now sometime later in the program: // Now sometime later in the program:
Python::with_gil(|py| { Python::with_gil(|py| {
println!("Python says: {}", hello.into_ref(py)); println!("Python says: {}", hello.into_bound(py));
// Memory not released yet. // Memory not released yet.
// Do more stuff... // Do more stuff...
// Memory released here at end of `with_gil()` closure. // Memory released here at end of `with_gil()` closure.

View File

@ -1201,7 +1201,7 @@ all you need to do is remove `ObjectProtocol` from your code.
Or if you use `ObjectProtocol` by `use pyo3::prelude::*`, you have to do nothing. Or if you use `ObjectProtocol` by `use pyo3::prelude::*`, you have to do nothing.
Before: Before:
```rust,compile_fail ```rust,compile_fail,ignore
use pyo3::ObjectProtocol; use pyo3::ObjectProtocol;
# pyo3::Python::with_gil(|py| { # pyo3::Python::with_gil(|py| {
@ -1212,7 +1212,7 @@ assert_eq!(hi.len().unwrap(), 5);
``` ```
After: After:
```rust ```rust,ignore
# pyo3::Python::with_gil(|py| { # pyo3::Python::with_gil(|py| {
let obj = py.eval("lambda: 'Hi :)'", None, None).unwrap(); let obj = py.eval("lambda: 'Hi :)'", None, None).unwrap();
let hi: &pyo3::types::PyString = obj.call0().unwrap().downcast().unwrap(); let hi: &pyo3::types::PyString = obj.call0().unwrap().downcast().unwrap();
@ -1351,7 +1351,7 @@ let obj_ref_mut: &mut MyClass = obj.extract().unwrap();
``` ```
After: After:
```rust ```rust,ignore
# use pyo3::prelude::*; # use pyo3::prelude::*;
# use pyo3::types::IntoPyDict; # use pyo3::types::IntoPyDict;
# #[pyclass] #[derive(Clone)] struct MyClass {} # #[pyclass] #[derive(Clone)] struct MyClass {}

View File

@ -157,7 +157,7 @@ use pyo3::prelude::*;
# fn main() -> Result<(), ()> { # fn main() -> Result<(), ()> {
Python::with_gil(|py| { Python::with_gil(|py| {
let result = py let result = py
.eval("[i * 10 for i in range(5)]", None, None) .eval_bound("[i * 10 for i in range(5)]", None, None)
.map_err(|e| { .map_err(|e| {
e.print_and_set_sys_last_vars(py); e.print_and_set_sys_last_vars(py);
})?; })?;
@ -466,7 +466,7 @@ class House(object):
house.call_method0("__enter__").unwrap(); house.call_method0("__enter__").unwrap();
let result = py.eval("undefined_variable + 1", None, None); let result = py.eval_bound("undefined_variable + 1", None, None);
// If the eval threw an exception we'll pass it through to the context manager. // If the eval threw an exception we'll pass it through to the context manager.
// Otherwise, __exit__ is called with empty arguments (Python "None"). // Otherwise, __exit__ is called with empty arguments (Python "None").

View File

@ -694,8 +694,8 @@ mod tests {
#[test] #[test]
fn test_debug() { fn test_debug() {
Python::with_gil(|py| { Python::with_gil(|py| {
let bytes = py.eval("b'abcde'", None, None).unwrap(); let bytes = py.eval_bound("b'abcde'", None, None).unwrap();
let buffer: PyBuffer<u8> = PyBuffer::get(bytes).unwrap(); let buffer: PyBuffer<u8> = PyBuffer::get(bytes.as_gil_ref()).unwrap();
let expected = format!( let expected = format!(
concat!( concat!(
"PyBuffer {{ buf: {:?}, obj: {:?}, ", "PyBuffer {{ buf: {:?}, obj: {:?}, ",
@ -857,8 +857,8 @@ mod tests {
#[test] #[test]
fn test_bytes_buffer() { fn test_bytes_buffer() {
Python::with_gil(|py| { Python::with_gil(|py| {
let bytes = py.eval("b'abcde'", None, None).unwrap(); let bytes = py.eval_bound("b'abcde'", None, None).unwrap();
let buffer = PyBuffer::get(bytes).unwrap(); let buffer = PyBuffer::get(bytes.as_gil_ref()).unwrap();
assert_eq!(buffer.dimensions(), 1); assert_eq!(buffer.dimensions(), 1);
assert_eq!(buffer.item_count(), 5); assert_eq!(buffer.item_count(), 5);
assert_eq!(buffer.format().to_str().unwrap(), "B"); assert_eq!(buffer.format().to_str().unwrap(), "B");

View File

@ -1095,6 +1095,7 @@ mod tests {
mod proptests { mod proptests {
use super::*; use super::*;
use crate::tests::common::CatchWarnings; use crate::tests::common::CatchWarnings;
use crate::types::any::PyAnyMethods;
use crate::types::IntoPyDict; use crate::types::IntoPyDict;
use proptest::prelude::*; use proptest::prelude::*;
@ -1105,9 +1106,9 @@ mod tests {
fn test_pyo3_offset_fixed_frompyobject_created_in_python(timestamp in 0..(i32::MAX as i64), timedelta in -86399i32..=86399i32) { fn test_pyo3_offset_fixed_frompyobject_created_in_python(timestamp in 0..(i32::MAX as i64), timedelta in -86399i32..=86399i32) {
Python::with_gil(|py| { Python::with_gil(|py| {
let globals = [("datetime", py.import("datetime").unwrap())].into_py_dict(py); let globals = [("datetime", py.import("datetime").unwrap())].into_py_dict(py).as_borrowed();
let code = format!("datetime.datetime.fromtimestamp({}).replace(tzinfo=datetime.timezone(datetime.timedelta(seconds={})))", timestamp, timedelta); let code = format!("datetime.datetime.fromtimestamp({}).replace(tzinfo=datetime.timezone(datetime.timedelta(seconds={})))", timestamp, timedelta);
let t = py.eval(&code, Some(globals), None).unwrap(); let t = py.eval_bound(&code, Some(&globals), None).unwrap();
// Get ISO 8601 string from python // Get ISO 8601 string from python
let py_iso_str = t.call_method0("isoformat").unwrap(); let py_iso_str = t.call_method0("isoformat").unwrap();

View File

@ -261,6 +261,8 @@ fn int_n_bits(long: &Bound<'_, PyLong>) -> PyResult<usize> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use self::{any::PyAnyMethods, dict::PyDictMethods};
use super::*; use super::*;
use crate::types::{PyDict, PyModule}; use crate::types::{PyDict, PyModule};
use indoc::indoc; use indoc::indoc;
@ -340,9 +342,9 @@ mod tests {
fn convert_index_class() { fn convert_index_class() {
Python::with_gil(|py| { Python::with_gil(|py| {
let index = python_index_class(py); let index = python_index_class(py);
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("index", index).unwrap(); locals.set_item("index", index).unwrap();
let ob = py.eval("index.C(10)", None, Some(locals)).unwrap(); let ob = py.eval_bound("index.C(10)", None, Some(&locals)).unwrap();
let _: BigInt = ob.extract().unwrap(); let _: BigInt = ob.extract().unwrap();
}); });
} }

View File

@ -130,6 +130,7 @@ mod tests {
sync::atomic::{AtomicUsize, Ordering}, sync::atomic::{AtomicUsize, Ordering},
}; };
use crate::types::any::PyAnyMethods;
use crate::{types::PyList, IntoPy, PyResult, Python, ToPyObject}; use crate::{types::PyList, IntoPy, PyResult, Python, ToPyObject};
#[test] #[test]
@ -157,7 +158,7 @@ mod tests {
fn test_extract_bytearray_to_array() { fn test_extract_bytearray_to_array() {
Python::with_gil(|py| { Python::with_gil(|py| {
let v: [u8; 33] = py let v: [u8; 33] = py
.eval( .eval_bound(
"bytearray(b'abcabcabcabcabcabcabcabcabcabcabc')", "bytearray(b'abcabcabcabcabcabcabcabcabcabcabc')",
None, None,
None, None,
@ -173,7 +174,7 @@ mod tests {
fn test_extract_small_bytearray_to_array() { fn test_extract_small_bytearray_to_array() {
Python::with_gil(|py| { Python::with_gil(|py| {
let v: [u8; 3] = py let v: [u8; 3] = py
.eval("bytearray(b'abc')", None, None) .eval_bound("bytearray(b'abc')", None, None)
.unwrap() .unwrap()
.extract() .extract()
.unwrap(); .unwrap();
@ -197,7 +198,7 @@ mod tests {
fn test_extract_invalid_sequence_length() { fn test_extract_invalid_sequence_length() {
Python::with_gil(|py| { Python::with_gil(|py| {
let v: PyResult<[u8; 3]> = py let v: PyResult<[u8; 3]> = py
.eval("bytearray(b'abcdefg')", None, None) .eval_bound("bytearray(b'abcdefg')", None, None)
.unwrap() .unwrap()
.extract(); .extract();
assert_eq!( assert_eq!(
@ -223,7 +224,7 @@ mod tests {
#[test] #[test]
fn test_extract_non_iterable_to_array() { fn test_extract_non_iterable_to_array() {
Python::with_gil(|py| { Python::with_gil(|py| {
let v = py.eval("42", None, None).unwrap(); let v = py.eval_bound("42", None, None).unwrap();
v.extract::<i32>().unwrap(); v.extract::<i32>().unwrap();
v.extract::<[i32; 1]>().unwrap_err(); v.extract::<[i32; 1]>().unwrap_err();
}); });

View File

@ -371,6 +371,8 @@ nonzero_int_impl!(NonZeroUsize, usize);
#[cfg(test)] #[cfg(test)]
mod test_128bit_integers { mod test_128bit_integers {
use super::*; use super::*;
use crate::types::any::PyAnyMethods;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use crate::types::PyDict; use crate::types::PyDict;
@ -474,7 +476,7 @@ mod test_128bit_integers {
#[test] #[test]
fn test_i128_overflow() { fn test_i128_overflow() {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = py.eval("(1 << 130) * -1", None, None).unwrap(); let obj = py.eval_bound("(1 << 130) * -1", None, None).unwrap();
let err = obj.extract::<i128>().unwrap_err(); let err = obj.extract::<i128>().unwrap_err();
assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py)); assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
}) })
@ -483,7 +485,7 @@ mod test_128bit_integers {
#[test] #[test]
fn test_u128_overflow() { fn test_u128_overflow() {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = py.eval("1 << 130", None, None).unwrap(); let obj = py.eval_bound("1 << 130", None, None).unwrap();
let err = obj.extract::<u128>().unwrap_err(); let err = obj.extract::<u128>().unwrap_err();
assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py)); assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
}) })
@ -527,7 +529,7 @@ mod test_128bit_integers {
#[test] #[test]
fn test_nonzero_i128_overflow() { fn test_nonzero_i128_overflow() {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = py.eval("(1 << 130) * -1", None, None).unwrap(); let obj = py.eval_bound("(1 << 130) * -1", None, None).unwrap();
let err = obj.extract::<NonZeroI128>().unwrap_err(); let err = obj.extract::<NonZeroI128>().unwrap_err();
assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py)); assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
}) })
@ -536,7 +538,7 @@ mod test_128bit_integers {
#[test] #[test]
fn test_nonzero_u128_overflow() { fn test_nonzero_u128_overflow() {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = py.eval("1 << 130", None, None).unwrap(); let obj = py.eval_bound("1 << 130", None, None).unwrap();
let err = obj.extract::<NonZeroU128>().unwrap_err(); let err = obj.extract::<NonZeroU128>().unwrap_err();
assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py)); assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
}) })
@ -545,7 +547,7 @@ mod test_128bit_integers {
#[test] #[test]
fn test_nonzero_i128_zero_value() { fn test_nonzero_i128_zero_value() {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = py.eval("0", None, None).unwrap(); let obj = py.eval_bound("0", None, None).unwrap();
let err = obj.extract::<NonZeroI128>().unwrap_err(); let err = obj.extract::<NonZeroI128>().unwrap_err();
assert!(err.is_instance_of::<crate::exceptions::PyValueError>(py)); assert!(err.is_instance_of::<crate::exceptions::PyValueError>(py));
}) })
@ -554,7 +556,7 @@ mod test_128bit_integers {
#[test] #[test]
fn test_nonzero_u128_zero_value() { fn test_nonzero_u128_zero_value() {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = py.eval("0", None, None).unwrap(); let obj = py.eval_bound("0", None, None).unwrap();
let err = obj.extract::<NonZeroU128>().unwrap_err(); let err = obj.extract::<NonZeroU128>().unwrap_err();
assert!(err.is_instance_of::<crate::exceptions::PyValueError>(py)); assert!(err.is_instance_of::<crate::exceptions::PyValueError>(py));
}) })

View File

@ -61,12 +61,15 @@ impl IntoPy<Py<PyAny>> for Cow<'_, [u8]> {
mod tests { mod tests {
use std::borrow::Cow; use std::borrow::Cow;
use crate::{types::PyBytes, Python, ToPyObject}; use crate::{
types::{any::PyAnyMethods, PyBytes},
Python, ToPyObject,
};
#[test] #[test]
fn test_extract_bytes() { fn test_extract_bytes() {
Python::with_gil(|py| { Python::with_gil(|py| {
let py_bytes = py.eval("b'Hello Python'", None, None).unwrap(); let py_bytes = py.eval_bound("b'Hello Python'", None, None).unwrap();
let bytes: &[u8] = py_bytes.extract().unwrap(); let bytes: &[u8] = py_bytes.extract().unwrap();
assert_eq!(bytes, b"Hello Python"); assert_eq!(bytes, b"Hello Python");
}); });
@ -75,15 +78,17 @@ mod tests {
#[test] #[test]
fn test_cow_impl() { fn test_cow_impl() {
Python::with_gil(|py| { Python::with_gil(|py| {
let bytes = py.eval(r#"b"foobar""#, None, None).unwrap(); let bytes = py.eval_bound(r#"b"foobar""#, None, None).unwrap();
let cow = bytes.extract::<Cow<'_, [u8]>>().unwrap(); let cow = bytes.extract::<Cow<'_, [u8]>>().unwrap();
assert_eq!(cow, Cow::<[u8]>::Borrowed(b"foobar")); assert_eq!(cow, Cow::<[u8]>::Borrowed(b"foobar"));
let byte_array = py.eval(r#"bytearray(b"foobar")"#, None, None).unwrap(); let byte_array = py
.eval_bound(r#"bytearray(b"foobar")"#, None, None)
.unwrap();
let cow = byte_array.extract::<Cow<'_, [u8]>>().unwrap(); let cow = byte_array.extract::<Cow<'_, [u8]>>().unwrap();
assert_eq!(cow, Cow::<[u8]>::Owned(b"foobar".to_vec())); assert_eq!(cow, Cow::<[u8]>::Owned(b"foobar".to_vec()));
let something_else_entirely = py.eval("42", None, None).unwrap(); let something_else_entirely = py.eval_bound("42", None, None).unwrap();
something_else_entirely something_else_entirely
.extract::<Cow<'_, [u8]>>() .extract::<Cow<'_, [u8]>>()
.unwrap_err(); .unwrap_err();

View File

@ -800,8 +800,9 @@ pub mod socket {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
use crate::types::any::PyAnyMethods;
use crate::types::{IntoPyDict, PyDict}; use crate::types::{IntoPyDict, PyDict};
use crate::{PyErr, Python}; use crate::{PyErr, PyNativeType, Python};
import_exception!(socket, gaierror); import_exception!(socket, gaierror);
import_exception!(email.errors, MessageError); import_exception!(email.errors, MessageError);
@ -863,13 +864,14 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
let error_type = py.get_type::<CustomError>(); let error_type = py.get_type::<CustomError>();
let ctx = [("CustomError", error_type)].into_py_dict(py); let ctx = [("CustomError", error_type)].into_py_dict(py).as_borrowed();
let type_description: String = py let type_description: String = py
.eval("str(CustomError)", None, Some(ctx)) .eval_bound("str(CustomError)", None, Some(&ctx))
.unwrap() .unwrap()
.extract() .extract()
.unwrap(); .unwrap();
assert_eq!(type_description, "<class 'mymodule.CustomError'>"); assert_eq!(type_description, "<class 'mymodule.CustomError'>");
let ctx = ctx.as_gil_ref();
py.run( py.run(
"assert CustomError('oops').args == ('oops',)", "assert CustomError('oops').args == ('oops',)",
None, None,
@ -886,9 +888,9 @@ mod tests {
create_exception!(mymodule.exceptions, CustomError, PyException); create_exception!(mymodule.exceptions, CustomError, PyException);
Python::with_gil(|py| { Python::with_gil(|py| {
let error_type = py.get_type::<CustomError>(); let error_type = py.get_type::<CustomError>();
let ctx = [("CustomError", error_type)].into_py_dict(py); let ctx = [("CustomError", error_type)].into_py_dict(py).as_borrowed();
let type_description: String = py let type_description: String = py
.eval("str(CustomError)", None, Some(ctx)) .eval_bound("str(CustomError)", None, Some(&ctx))
.unwrap() .unwrap()
.extract() .extract()
.unwrap(); .unwrap();
@ -905,13 +907,14 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
let error_type = py.get_type::<CustomError>(); let error_type = py.get_type::<CustomError>();
let ctx = [("CustomError", error_type)].into_py_dict(py); let ctx = [("CustomError", error_type)].into_py_dict(py).as_borrowed();
let type_description: String = py let type_description: String = py
.eval("str(CustomError)", None, Some(ctx)) .eval_bound("str(CustomError)", None, Some(&ctx))
.unwrap() .unwrap()
.extract() .extract()
.unwrap(); .unwrap();
assert_eq!(type_description, "<class 'mymodule.CustomError'>"); assert_eq!(type_description, "<class 'mymodule.CustomError'>");
let ctx = ctx.as_gil_ref();
py.run( py.run(
"assert CustomError('oops').args == ('oops',)", "assert CustomError('oops').args == ('oops',)",
None, None,
@ -934,13 +937,14 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
let error_type = py.get_type::<CustomError>(); let error_type = py.get_type::<CustomError>();
let ctx = [("CustomError", error_type)].into_py_dict(py); let ctx = [("CustomError", error_type)].into_py_dict(py).as_borrowed();
let type_description: String = py let type_description: String = py
.eval("str(CustomError)", None, Some(ctx)) .eval_bound("str(CustomError)", None, Some(&ctx))
.unwrap() .unwrap()
.extract() .extract()
.unwrap(); .unwrap();
assert_eq!(type_description, "<class 'mymodule.CustomError'>"); assert_eq!(type_description, "<class 'mymodule.CustomError'>");
let ctx = ctx.as_gil_ref();
py.run( py.run(
"assert CustomError('oops').args == ('oops',)", "assert CustomError('oops').args == ('oops',)",
None, None,
@ -1077,7 +1081,7 @@ mod tests {
PyErr::from_value(PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap()) PyErr::from_value(PyUnicodeDecodeError::new_utf8(py, invalid_utf8, err).unwrap())
}); });
test_exception!(PyUnicodeEncodeError, |py| py test_exception!(PyUnicodeEncodeError, |py| py
.eval("chr(40960).encode('ascii')", None, None) .eval_bound("chr(40960).encode('ascii')", None, None)
.unwrap_err()); .unwrap_err());
test_exception!(PyUnicodeTranslateError, |_| { test_exception!(PyUnicodeTranslateError, |_| {
PyUnicodeTranslateError::new_err(("\u{3042}", 0, 1, "ouch")) PyUnicodeTranslateError::new_err(("\u{3042}", 0, 1, "ouch"))

View File

@ -1,9 +1,10 @@
use crate::ffi::*; use crate::ffi::*;
use crate::types::any::PyAnyMethods;
use crate::Python; use crate::Python;
#[cfg(not(Py_LIMITED_API))] #[cfg(not(Py_LIMITED_API))]
use crate::{ use crate::{
types::{any::PyAnyMethods, PyDict, PyString}, types::{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)))]
@ -293,7 +294,7 @@ fn test_get_tzinfo() {
#[test] #[test]
fn test_inc_dec_ref() { fn test_inc_dec_ref() {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = py.eval("object()", None, None).unwrap(); let obj = py.eval_bound("object()", None, None).unwrap();
let ref_count = obj.get_refcnt(); let ref_count = obj.get_refcnt();
let ptr = obj.as_ptr(); let ptr = obj.as_ptr();

View File

@ -507,6 +507,7 @@ fn decrement_gil_count() {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::{gil_is_acquired, GILPool, GIL_COUNT, OWNED_OBJECTS, POOL}; use super::{gil_is_acquired, GILPool, GIL_COUNT, OWNED_OBJECTS, POOL};
use crate::types::any::PyAnyMethods;
use crate::{ffi, gil, PyObject, Python, ToPyObject}; use crate::{ffi, gil, PyObject, Python, ToPyObject};
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use parking_lot::{const_mutex, Condvar, Mutex}; use parking_lot::{const_mutex, Condvar, Mutex};
@ -518,7 +519,7 @@ mod tests {
let pool = unsafe { py.new_pool() }; let pool = unsafe { py.new_pool() };
let py = pool.python(); let py = pool.python();
let obj = py.eval("object()", None, None).unwrap(); let obj = py.eval_bound("object()", None, None).unwrap();
obj.to_object(py) obj.to_object(py)
} }
@ -735,7 +736,7 @@ mod tests {
fn dropping_gil_does_not_invalidate_references() { fn dropping_gil_does_not_invalidate_references() {
// Acquiring GIL for the second time should be safe - see #864 // Acquiring GIL for the second time should be safe - see #864
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = Python::with_gil(|_| py.eval("object()", None, None).unwrap()); let obj = Python::with_gil(|_| py.eval_bound("object()", None, None).unwrap());
// After gil2 drops, obj should still have a reference count of one // After gil2 drops, obj should still have a reference count of one
assert_eq!(obj.get_refcnt(), 1); assert_eq!(obj.get_refcnt(), 1);

View File

@ -221,9 +221,9 @@
//! let sys = py.import("sys")?; //! let sys = py.import("sys")?;
//! let version: String = sys.getattr("version")?.extract()?; //! let version: String = sys.getattr("version")?.extract()?;
//! //!
//! let locals = [("os", py.import("os")?)].into_py_dict(py); //! let locals = [("os", py.import("os")?)].into_py_dict(py).as_borrowed();
//! let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'"; //! let code = "os.getenv('USER') or os.getenv('USERNAME') or 'Unknown'";
//! let user: String = py.eval(code, None, Some(&locals))?.extract()?; //! let user: String = py.eval_bound(code, None, Some(&locals))?.extract()?;
//! //!
//! println!("Hello {}, I'm Python {}", user, version); //! println!("Hello {}, I'm Python {}", user, version);
//! Ok(()) //! Ok(())

View File

@ -117,14 +117,19 @@
//! [`Rc`]: std::rc::Rc //! [`Rc`]: std::rc::Rc
//! [`Py`]: crate::Py //! [`Py`]: crate::Py
use crate::err::{self, PyDowncastError, PyErr, PyResult}; use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::gil::{GILGuard, GILPool, SuspendGIL}; use crate::gil::{GILGuard, GILPool, SuspendGIL};
use crate::impl_::not_send::NotSend; use crate::impl_::not_send::NotSend;
use crate::py_result_ext::PyResultExt;
use crate::type_object::HasPyGilRef; use crate::type_object::HasPyGilRef;
use crate::types::any::PyAnyMethods;
use crate::types::{ use crate::types::{
PyAny, PyDict, PyEllipsis, PyModule, PyNone, PyNotImplemented, PyString, PyType, PyAny, PyDict, PyEllipsis, PyModule, PyNone, PyNotImplemented, PyString, PyType,
}; };
use crate::version::PythonVersionInfo; use crate::version::PythonVersionInfo;
use crate::{ffi, FromPyPointer, IntoPy, Py, PyObject, PyTypeCheck, PyTypeInfo}; use crate::{
ffi, Bound, FromPyPointer, IntoPy, Py, PyNativeType, PyObject, PyTypeCheck, PyTypeInfo,
};
use std::ffi::{CStr, CString}; use std::ffi::{CStr, CString};
use std::marker::PhantomData; use std::marker::PhantomData;
use std::os::raw::c_int; use std::os::raw::c_int;
@ -358,8 +363,8 @@ pub use nightly::Ungil;
/// # fn main () -> PyResult<()> { /// # fn main () -> PyResult<()> {
/// Python::with_gil(|py| -> PyResult<()> { /// Python::with_gil(|py| -> PyResult<()> {
/// for _ in 0..10 { /// for _ in 0..10 {
/// let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?; /// let hello = py.eval_bound("\"Hello World!\"", None, None)?.downcast_into::<PyString>()?;
/// println!("Python says: {}", hello.to_str()?); /// println!("Python says: {}", hello.to_cow()?);
/// // Normally variables in a loop scope are dropped here, but `hello` is a reference to /// // Normally variables in a loop scope are dropped here, but `hello` is a reference to
/// // something owned by the Python interpreter. Dropping this reference does nothing. /// // something owned by the Python interpreter. Dropping this reference does nothing.
/// } /// }
@ -417,7 +422,7 @@ impl Python<'_> {
/// ///
/// # fn main() -> PyResult<()> { /// # fn main() -> PyResult<()> {
/// Python::with_gil(|py| -> PyResult<()> { /// Python::with_gil(|py| -> PyResult<()> {
/// let x: i32 = py.eval("5", None, None)?.extract()?; /// let x: i32 = py.eval_bound("5", None, None)?.extract()?;
/// assert_eq!(x, 5); /// assert_eq!(x, 5);
/// Ok(()) /// Ok(())
/// }) /// })
@ -546,6 +551,28 @@ impl<'py> Python<'py> {
f() f()
} }
/// Deprecated version of [`Python::eval_bound`]
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`Python::eval` will be replaced by `Python::eval_bound` in a future PyO3 version"
)
)]
pub fn eval(
self,
code: &str,
globals: Option<&'py PyDict>,
locals: Option<&'py PyDict>,
) -> PyResult<&'py PyAny> {
self.eval_bound(
code,
globals.map(PyNativeType::as_borrowed).as_deref(),
locals.map(PyNativeType::as_borrowed).as_deref(),
)
.map(Bound::into_gil_ref)
}
/// Evaluates a Python expression in the given context and returns the result. /// Evaluates a Python expression in the given context and returns the result.
/// ///
/// If `globals` is `None`, it defaults to Python module `__main__`. /// If `globals` is `None`, it defaults to Python module `__main__`.
@ -559,17 +586,17 @@ impl<'py> Python<'py> {
/// ``` /// ```
/// # use pyo3::prelude::*; /// # use pyo3::prelude::*;
/// # Python::with_gil(|py| { /// # Python::with_gil(|py| {
/// let result = py.eval("[i * 10 for i in range(5)]", None, None).unwrap(); /// let result = py.eval_bound("[i * 10 for i in range(5)]", None, None).unwrap();
/// let res: Vec<i64> = result.extract().unwrap(); /// let res: Vec<i64> = result.extract().unwrap();
/// assert_eq!(res, vec![0, 10, 20, 30, 40]) /// assert_eq!(res, vec![0, 10, 20, 30, 40])
/// # }); /// # });
/// ``` /// ```
pub fn eval( pub fn eval_bound(
self, self,
code: &str, code: &str,
globals: Option<&PyDict>, globals: Option<&Bound<'py, PyDict>>,
locals: Option<&PyDict>, locals: Option<&Bound<'py, PyDict>>,
) -> PyResult<&'py PyAny> { ) -> PyResult<Bound<'py, PyAny>> {
self.run_code(code, ffi::Py_eval_input, globals, locals) self.run_code(code, ffi::Py_eval_input, globals, locals)
} }
@ -613,7 +640,12 @@ impl<'py> Python<'py> {
globals: Option<&PyDict>, globals: Option<&PyDict>,
locals: Option<&PyDict>, locals: Option<&PyDict>,
) -> PyResult<()> { ) -> PyResult<()> {
let res = self.run_code(code, ffi::Py_file_input, globals, locals); let res = self.run_code(
code,
ffi::Py_file_input,
globals.map(PyNativeType::as_borrowed).as_deref(),
locals.map(PyNativeType::as_borrowed).as_deref(),
);
res.map(|obj| { res.map(|obj| {
debug_assert!(obj.is_none()); debug_assert!(obj.is_none());
}) })
@ -630,9 +662,9 @@ impl<'py> Python<'py> {
self, self,
code: &str, code: &str,
start: c_int, start: c_int,
globals: Option<&PyDict>, globals: Option<&Bound<'py, PyDict>>,
locals: Option<&PyDict>, locals: Option<&Bound<'py, PyDict>>,
) -> PyResult<&'py PyAny> { ) -> PyResult<Bound<'py, PyAny>> {
let code = CString::new(code)?; let code = CString::new(code)?;
unsafe { unsafe {
let mptr = ffi::PyImport_AddModule("__main__\0".as_ptr() as *const _); let mptr = ffi::PyImport_AddModule("__main__\0".as_ptr() as *const _);
@ -675,7 +707,7 @@ impl<'py> Python<'py> {
let res_ptr = ffi::PyEval_EvalCode(code_obj, globals, locals); let res_ptr = ffi::PyEval_EvalCode(code_obj, globals, locals);
ffi::Py_DECREF(code_obj); ffi::Py_DECREF(code_obj);
self.from_owned_ptr_or_err(res_ptr) res_ptr.assume_owned_or_err(self).downcast_into_unchecked()
} }
} }
@ -1077,18 +1109,18 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
// Make sure builtin names are accessible // Make sure builtin names are accessible
let v: i32 = py let v: i32 = py
.eval("min(1, 2)", None, None) .eval_bound("min(1, 2)", None, None)
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.unwrap() .unwrap()
.extract() .extract()
.unwrap(); .unwrap();
assert_eq!(v, 1); assert_eq!(v, 1);
let d = [("foo", 13)].into_py_dict(py); let d = [("foo", 13)].into_py_dict(py).as_borrowed();
// Inject our own global namespace // Inject our own global namespace
let v: i32 = py let v: i32 = py
.eval("foo + 29", Some(d), None) .eval_bound("foo + 29", Some(&d), None)
.unwrap() .unwrap()
.extract() .extract()
.unwrap(); .unwrap();
@ -1096,7 +1128,7 @@ mod tests {
// Inject our own local namespace // Inject our own local namespace
let v: i32 = py let v: i32 = py
.eval("foo + 29", None, Some(d)) .eval_bound("foo + 29", None, Some(&d))
.unwrap() .unwrap()
.extract() .extract()
.unwrap(); .unwrap();
@ -1104,7 +1136,7 @@ mod tests {
// Make sure builtin names are still accessible when using a local namespace // Make sure builtin names are still accessible when using a local namespace
let v: i32 = py let v: i32 = py
.eval("min(foo, 2)", None, Some(d)) .eval_bound("min(foo, 2)", None, Some(&d))
.unwrap() .unwrap()
.extract() .extract()
.unwrap(); .unwrap();
@ -1193,7 +1225,7 @@ mod tests {
assert_eq!(py.Ellipsis().to_string(), "Ellipsis"); assert_eq!(py.Ellipsis().to_string(), "Ellipsis");
let v = py let v = py
.eval("...", None, None) .eval_bound("...", None, None)
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.unwrap(); .unwrap();

View File

@ -14,10 +14,10 @@ use crate::{
/// ///
/// # fn main() -> PyResult<()> { /// # fn main() -> PyResult<()> {
/// Python::with_gil(|py| -> PyResult<()> { /// Python::with_gil(|py| -> PyResult<()> {
/// let list = py.eval("iter([1, 2, 3, 4])", None, None)?; /// let list = py.eval_bound("iter([1, 2, 3, 4])", None, None)?;
/// let numbers: PyResult<Vec<usize>> = list /// let numbers: PyResult<Vec<usize>> = list
/// .iter()? /// .iter()?
/// .map(|i| i.and_then(PyAny::extract::<usize>)) /// .map(|i| i.and_then(|i|i.extract::<usize>()))
/// .collect(); /// .collect();
/// let sum: usize = numbers?.iter().sum(); /// let sum: usize = numbers?.iter().sum();
/// assert_eq!(sum, 10); /// assert_eq!(sum, 10);

View File

@ -59,9 +59,9 @@ pub use self::typeobject::PyType;
/// ///
/// # pub fn main() -> PyResult<()> { /// # pub fn main() -> PyResult<()> {
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let dict: &PyDict = py.eval("{'a':'b', 'c':'d'}", None, None)?.downcast()?; /// let dict = py.eval_bound("{'a':'b', 'c':'d'}", None, None)?.downcast_into::<PyDict>()?;
/// ///
/// for (key, value) in dict { /// for (key, value) in dict.iter() {
/// println!("key: {}, value: {}", key, value); /// println!("key: {}, value: {}", key, value);
/// } /// }
/// ///

View File

@ -4,11 +4,11 @@ use pyo3::prelude::*;
use pyo3::types::{timezone_utc, IntoPyDict, PyDate, PyDateTime, PyTime}; use pyo3::types::{timezone_utc, IntoPyDict, PyDate, PyDateTime, PyTime};
use pyo3_ffi::PyDateTime_IMPORT; use pyo3_ffi::PyDateTime_IMPORT;
fn _get_subclasses<'p>( fn _get_subclasses<'py>(
py: Python<'p>, py: Python<'py>,
py_type: &str, py_type: &str,
args: &str, args: &str,
) -> PyResult<(&'p PyAny, &'p PyAny, &'p PyAny)> { ) -> PyResult<(Bound<'py, PyAny>, Bound<'py, PyAny>, Bound<'py, PyAny>)> {
// Import the class from Python and create some subclasses // Import the class from Python and create some subclasses
let datetime = py.import("datetime")?; let datetime = py.import("datetime")?;
@ -21,14 +21,15 @@ fn _get_subclasses<'p>(
py.run(&make_subclass_py, None, Some(locals))?; py.run(&make_subclass_py, None, Some(locals))?;
py.run(make_sub_subclass_py, None, Some(locals))?; py.run(make_sub_subclass_py, None, Some(locals))?;
let locals = locals.as_borrowed();
// Construct an instance of the base class // Construct an instance of the base class
let obj = py.eval(&format!("{}({})", py_type, args), None, Some(locals))?; let obj = py.eval_bound(&format!("{}({})", py_type, args), None, Some(&locals))?;
// Construct an instance of the subclass // Construct an instance of the subclass
let sub_obj = py.eval(&format!("Subklass({})", args), None, Some(locals))?; let sub_obj = py.eval_bound(&format!("Subklass({})", args), None, Some(&locals))?;
// Construct an instance of the sub-subclass // Construct an instance of the sub-subclass
let sub_sub_obj = py.eval(&format!("SubSubklass({})", args), None, Some(locals))?; let sub_sub_obj = py.eval_bound(&format!("SubSubklass({})", args), None, Some(&locals))?;
Ok((obj, sub_obj, sub_sub_obj)) Ok((obj, sub_obj, sub_sub_obj))
} }
@ -122,10 +123,10 @@ fn test_datetime_utc() {
let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, Some(utc)).unwrap(); let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, Some(utc)).unwrap();
let locals = [("dt", dt)].into_py_dict(py); let locals = [("dt", dt)].into_py_dict(py).as_borrowed();
let offset: f32 = py let offset: f32 = py
.eval("dt.utcoffset().total_seconds()", None, Some(locals)) .eval_bound("dt.utcoffset().total_seconds()", None, Some(&locals))
.unwrap() .unwrap()
.extract() .extract()
.unwrap(); .unwrap();

View File

@ -486,14 +486,14 @@ pub struct Zap {
fn test_from_py_with() { fn test_from_py_with() {
Python::with_gil(|py| { Python::with_gil(|py| {
let py_zap = py let py_zap = py
.eval( .eval_bound(
r#"{"name": "whatever", "my_object": [1, 2, 3]}"#, r#"{"name": "whatever", "my_object": [1, 2, 3]}"#,
None, None,
None, None,
) )
.expect("failed to create dict"); .expect("failed to create dict");
let zap = Zap::extract(py_zap).unwrap(); let zap = Zap::extract_bound(&py_zap).unwrap();
assert_eq!(zap.name, "whatever"); assert_eq!(zap.name, "whatever");
assert_eq!(zap.some_object_length, 3usize); assert_eq!(zap.some_object_length, 3usize);
@ -507,10 +507,10 @@ pub struct ZapTuple(String, #[pyo3(from_py_with = "PyAny::len")] usize);
fn test_from_py_with_tuple_struct() { fn test_from_py_with_tuple_struct() {
Python::with_gil(|py| { Python::with_gil(|py| {
let py_zap = py let py_zap = py
.eval(r#"("whatever", [1, 2, 3])"#, None, None) .eval_bound(r#"("whatever", [1, 2, 3])"#, None, None)
.expect("failed to create tuple"); .expect("failed to create tuple");
let zap = ZapTuple::extract(py_zap).unwrap(); let zap = ZapTuple::extract_bound(&py_zap).unwrap();
assert_eq!(zap.0, "whatever"); assert_eq!(zap.0, "whatever");
assert_eq!(zap.1, 3usize); assert_eq!(zap.1, 3usize);
@ -521,10 +521,10 @@ fn test_from_py_with_tuple_struct() {
fn test_from_py_with_tuple_struct_error() { fn test_from_py_with_tuple_struct_error() {
Python::with_gil(|py| { Python::with_gil(|py| {
let py_zap = py let py_zap = py
.eval(r#"("whatever", [1, 2, 3], "third")"#, None, None) .eval_bound(r#"("whatever", [1, 2, 3], "third")"#, None, None)
.expect("failed to create tuple"); .expect("failed to create tuple");
let f = ZapTuple::extract(py_zap); let f = ZapTuple::extract_bound(&py_zap);
assert!(f.is_err()); assert!(f.is_err());
assert_eq!( assert_eq!(
@ -544,10 +544,10 @@ pub enum ZapEnum {
fn test_from_py_with_enum() { fn test_from_py_with_enum() {
Python::with_gil(|py| { Python::with_gil(|py| {
let py_zap = py let py_zap = py
.eval(r#"("whatever", [1, 2, 3])"#, None, None) .eval_bound(r#"("whatever", [1, 2, 3])"#, None, None)
.expect("failed to create tuple"); .expect("failed to create tuple");
let zap = ZapEnum::extract(py_zap).unwrap(); let zap = ZapEnum::extract_bound(&py_zap).unwrap();
let expected_zap = ZapEnum::Zip(2); let expected_zap = ZapEnum::Zip(2);
assert_eq!(zap, expected_zap); assert_eq!(zap, expected_zap);

View File

@ -240,7 +240,7 @@ mod inheriting_native_type {
let dict_sub = pyo3::Py::new(py, DictWithName::new()).unwrap(); let dict_sub = pyo3::Py::new(py, DictWithName::new()).unwrap();
assert_eq!(dict_sub.get_refcnt(py), 1); assert_eq!(dict_sub.get_refcnt(py), 1);
let item = py.eval("object()", None, None).unwrap(); let item = &py.eval_bound("object()", None, None).unwrap();
assert_eq!(item.get_refcnt(), 1); assert_eq!(item.get_refcnt(), 1);
dict_sub.as_ref(py).set_item("foo", item).unwrap(); dict_sub.as_ref(py).set_item("foo", item).unwrap();