Merge pull request #3806 from Icxolu/python-eval

port `Python::eval` to `Bound` API
This commit is contained in:
David Hewitt 2024-02-09 17:18:57 +00:00 committed by GitHub
commit 2fedea24b3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 149 additions and 99 deletions

View File

@ -152,9 +152,9 @@ fn main() -> PyResult<()> {
let sys = py.import("sys")?;
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 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);
Ok(())

View File

@ -155,7 +155,7 @@ struct RustyStruct {
#
# fn main() -> 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()?;
# assert_eq!(rustystruct.foo, "foo");
# assert_eq!(rustystruct.bar, "bar");

View File

@ -27,7 +27,7 @@ very simple and easy-to-understand programs like this:
# use pyo3::types::PyString;
# fn main() -> 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);
Ok(())
})?;
@ -48,7 +48,7 @@ of the time we don't have to think about this, but consider the following:
# fn main() -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> {
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);
}
// 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<()> {
for _ in 0..10 {
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);
Ok(())
})?; // only one copy of `hello` at a time
@ -97,7 +97,7 @@ Python::with_gil(|py| -> PyResult<()> {
for _ in 0..10 {
let pool = unsafe { py.new_pool() };
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);
}
Ok(())
@ -144,8 +144,8 @@ reference count reaches zero? It depends whether or not we are holding the GIL.
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
Python::with_gil(|py| -> PyResult<()> {
let hello: Py<PyString> = py.eval("\"Hello World!\"", None, None)?.extract()?;
println!("Python says: {}", hello.as_ref(py));
let hello: Py<PyString> = py.eval_bound("\"Hello World!\"", None, None)?.extract()?;
println!("Python says: {}", hello.bind(py));
Ok(())
})?;
# Ok(())
@ -166,7 +166,7 @@ we are *not* holding the GIL?
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
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...
// 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;
# fn main() -> PyResult<()> {
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...
// Now sometime later in the program:
Python::with_gil(|py| {
println!("Python says: {}", hello.as_ref(py));
println!("Python says: {}", hello.bind(py));
drop(hello); // Memory released here.
});
# Ok(())
@ -219,11 +219,11 @@ until the GIL is dropped.
# use pyo3::types::PyString;
# fn main() -> PyResult<()> {
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...
// Now sometime later in the program:
Python::with_gil(|py| {
println!("Python says: {}", hello.into_ref(py));
println!("Python says: {}", hello.into_bound(py));
// Memory not released yet.
// Do more stuff...
// 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.
Before:
```rust,compile_fail
```rust,compile_fail,ignore
use pyo3::ObjectProtocol;
# pyo3::Python::with_gil(|py| {
@ -1212,7 +1212,7 @@ assert_eq!(hi.len().unwrap(), 5);
```
After:
```rust
```rust,ignore
# pyo3::Python::with_gil(|py| {
let obj = py.eval("lambda: 'Hi :)'", None, None).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:
```rust
```rust,ignore
# use pyo3::prelude::*;
# use pyo3::types::IntoPyDict;
# #[pyclass] #[derive(Clone)] struct MyClass {}

View File

@ -157,7 +157,7 @@ use pyo3::prelude::*;
# fn main() -> Result<(), ()> {
Python::with_gil(|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| {
e.print_and_set_sys_last_vars(py);
})?;
@ -466,7 +466,7 @@ class House(object):
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.
// Otherwise, __exit__ is called with empty arguments (Python "None").

View File

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

View File

@ -1095,6 +1095,7 @@ mod tests {
mod proptests {
use super::*;
use crate::tests::common::CatchWarnings;
use crate::types::any::PyAnyMethods;
use crate::types::IntoPyDict;
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) {
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 t = py.eval(&code, Some(globals), None).unwrap();
let t = py.eval_bound(&code, Some(&globals), None).unwrap();
// Get ISO 8601 string from python
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)]
mod tests {
use self::{any::PyAnyMethods, dict::PyDictMethods};
use super::*;
use crate::types::{PyDict, PyModule};
use indoc::indoc;
@ -340,9 +342,9 @@ mod tests {
fn convert_index_class() {
Python::with_gil(|py| {
let index = python_index_class(py);
let locals = PyDict::new(py);
let locals = PyDict::new_bound(py);
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();
});
}

View File

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

View File

@ -371,6 +371,8 @@ nonzero_int_impl!(NonZeroUsize, usize);
#[cfg(test)]
mod test_128bit_integers {
use super::*;
use crate::types::any::PyAnyMethods;
#[cfg(not(target_arch = "wasm32"))]
use crate::types::PyDict;
@ -474,7 +476,7 @@ mod test_128bit_integers {
#[test]
fn test_i128_overflow() {
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();
assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
})
@ -483,7 +485,7 @@ mod test_128bit_integers {
#[test]
fn test_u128_overflow() {
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();
assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
})
@ -527,7 +529,7 @@ mod test_128bit_integers {
#[test]
fn test_nonzero_i128_overflow() {
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();
assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
})
@ -536,7 +538,7 @@ mod test_128bit_integers {
#[test]
fn test_nonzero_u128_overflow() {
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();
assert!(err.is_instance_of::<crate::exceptions::PyOverflowError>(py));
})
@ -545,7 +547,7 @@ mod test_128bit_integers {
#[test]
fn test_nonzero_i128_zero_value() {
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();
assert!(err.is_instance_of::<crate::exceptions::PyValueError>(py));
})
@ -554,7 +556,7 @@ mod test_128bit_integers {
#[test]
fn test_nonzero_u128_zero_value() {
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();
assert!(err.is_instance_of::<crate::exceptions::PyValueError>(py));
})

View File

@ -61,12 +61,15 @@ impl IntoPy<Py<PyAny>> for Cow<'_, [u8]> {
mod tests {
use std::borrow::Cow;
use crate::{types::PyBytes, Python, ToPyObject};
use crate::{
types::{any::PyAnyMethods, PyBytes},
Python, ToPyObject,
};
#[test]
fn test_extract_bytes() {
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();
assert_eq!(bytes, b"Hello Python");
});
@ -75,15 +78,17 @@ mod tests {
#[test]
fn test_cow_impl() {
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();
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();
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
.extract::<Cow<'_, [u8]>>()
.unwrap_err();

View File

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

View File

@ -1,9 +1,10 @@
use crate::ffi::*;
use crate::types::any::PyAnyMethods;
use crate::Python;
#[cfg(not(Py_LIMITED_API))]
use crate::{
types::{any::PyAnyMethods, PyDict, PyString},
types::{PyDict, PyString},
IntoPy, Py, PyAny,
};
#[cfg(not(any(Py_3_12, Py_LIMITED_API)))]
@ -293,7 +294,7 @@ fn test_get_tzinfo() {
#[test]
fn test_inc_dec_ref() {
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 ptr = obj.as_ptr();

View File

@ -507,6 +507,7 @@ fn decrement_gil_count() {
#[cfg(test)]
mod tests {
use super::{gil_is_acquired, GILPool, GIL_COUNT, OWNED_OBJECTS, POOL};
use crate::types::any::PyAnyMethods;
use crate::{ffi, gil, PyObject, Python, ToPyObject};
#[cfg(not(target_arch = "wasm32"))]
use parking_lot::{const_mutex, Condvar, Mutex};
@ -518,7 +519,7 @@ mod tests {
let pool = unsafe { py.new_pool() };
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)
}
@ -735,7 +736,7 @@ mod tests {
fn dropping_gil_does_not_invalidate_references() {
// Acquiring GIL for the second time should be safe - see #864
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
assert_eq!(obj.get_refcnt(), 1);

View File

@ -221,9 +221,9 @@
//! let sys = py.import("sys")?;
//! 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 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);
//! Ok(())

View File

@ -117,14 +117,19 @@
//! [`Rc`]: std::rc::Rc
//! [`Py`]: crate::Py
use crate::err::{self, PyDowncastError, PyErr, PyResult};
use crate::ffi_ptr_ext::FfiPtrExt;
use crate::gil::{GILGuard, GILPool, SuspendGIL};
use crate::impl_::not_send::NotSend;
use crate::py_result_ext::PyResultExt;
use crate::type_object::HasPyGilRef;
use crate::types::any::PyAnyMethods;
use crate::types::{
PyAny, PyDict, PyEllipsis, PyModule, PyNone, PyNotImplemented, PyString, PyType,
};
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::marker::PhantomData;
use std::os::raw::c_int;
@ -358,8 +363,8 @@ pub use nightly::Ungil;
/// # fn main () -> PyResult<()> {
/// Python::with_gil(|py| -> PyResult<()> {
/// for _ in 0..10 {
/// let hello: &PyString = py.eval("\"Hello World!\"", None, None)?.extract()?;
/// println!("Python says: {}", hello.to_str()?);
/// let hello = py.eval_bound("\"Hello World!\"", None, None)?.downcast_into::<PyString>()?;
/// println!("Python says: {}", hello.to_cow()?);
/// // 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.
/// }
@ -417,7 +422,7 @@ impl Python<'_> {
///
/// # fn main() -> 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);
/// Ok(())
/// })
@ -546,6 +551,28 @@ impl<'py> Python<'py> {
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.
///
/// If `globals` is `None`, it defaults to Python module `__main__`.
@ -559,17 +586,17 @@ impl<'py> Python<'py> {
/// ```
/// # use pyo3::prelude::*;
/// # 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();
/// assert_eq!(res, vec![0, 10, 20, 30, 40])
/// # });
/// ```
pub fn eval(
pub fn eval_bound(
self,
code: &str,
globals: Option<&PyDict>,
locals: Option<&PyDict>,
) -> PyResult<&'py PyAny> {
globals: Option<&Bound<'py, PyDict>>,
locals: Option<&Bound<'py, PyDict>>,
) -> PyResult<Bound<'py, PyAny>> {
self.run_code(code, ffi::Py_eval_input, globals, locals)
}
@ -613,7 +640,12 @@ impl<'py> Python<'py> {
globals: Option<&PyDict>,
locals: Option<&PyDict>,
) -> 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| {
debug_assert!(obj.is_none());
})
@ -630,9 +662,9 @@ impl<'py> Python<'py> {
self,
code: &str,
start: c_int,
globals: Option<&PyDict>,
locals: Option<&PyDict>,
) -> PyResult<&'py PyAny> {
globals: Option<&Bound<'py, PyDict>>,
locals: Option<&Bound<'py, PyDict>>,
) -> PyResult<Bound<'py, PyAny>> {
let code = CString::new(code)?;
unsafe {
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);
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| {
// Make sure builtin names are accessible
let v: i32 = py
.eval("min(1, 2)", None, None)
.eval_bound("min(1, 2)", None, None)
.map_err(|e| e.display(py))
.unwrap()
.extract()
.unwrap();
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
let v: i32 = py
.eval("foo + 29", Some(d), None)
.eval_bound("foo + 29", Some(&d), None)
.unwrap()
.extract()
.unwrap();
@ -1096,7 +1128,7 @@ mod tests {
// Inject our own local namespace
let v: i32 = py
.eval("foo + 29", None, Some(d))
.eval_bound("foo + 29", None, Some(&d))
.unwrap()
.extract()
.unwrap();
@ -1104,7 +1136,7 @@ mod tests {
// Make sure builtin names are still accessible when using a local namespace
let v: i32 = py
.eval("min(foo, 2)", None, Some(d))
.eval_bound("min(foo, 2)", None, Some(&d))
.unwrap()
.extract()
.unwrap();
@ -1193,7 +1225,7 @@ mod tests {
assert_eq!(py.Ellipsis().to_string(), "Ellipsis");
let v = py
.eval("...", None, None)
.eval_bound("...", None, None)
.map_err(|e| e.display(py))
.unwrap();

View File

@ -14,10 +14,10 @@ use crate::{
///
/// # fn main() -> 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
/// .iter()?
/// .map(|i| i.and_then(PyAny::extract::<usize>))
/// .map(|i| i.and_then(|i|i.extract::<usize>()))
/// .collect();
/// let sum: usize = numbers?.iter().sum();
/// assert_eq!(sum, 10);

View File

@ -59,9 +59,9 @@ pub use self::typeobject::PyType;
///
/// # pub fn main() -> PyResult<()> {
/// 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);
/// }
///

View File

@ -4,11 +4,11 @@ use pyo3::prelude::*;
use pyo3::types::{timezone_utc, IntoPyDict, PyDate, PyDateTime, PyTime};
use pyo3_ffi::PyDateTime_IMPORT;
fn _get_subclasses<'p>(
py: Python<'p>,
fn _get_subclasses<'py>(
py: Python<'py>,
py_type: &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
let datetime = py.import("datetime")?;
@ -21,14 +21,15 @@ fn _get_subclasses<'p>(
py.run(&make_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
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
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
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))
}
@ -122,10 +123,10 @@ fn test_datetime_utc() {
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
.eval("dt.utcoffset().total_seconds()", None, Some(locals))
.eval_bound("dt.utcoffset().total_seconds()", None, Some(&locals))
.unwrap()
.extract()
.unwrap();

View File

@ -486,14 +486,14 @@ pub struct Zap {
fn test_from_py_with() {
Python::with_gil(|py| {
let py_zap = py
.eval(
.eval_bound(
r#"{"name": "whatever", "my_object": [1, 2, 3]}"#,
None,
None,
)
.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.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() {
Python::with_gil(|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");
let zap = ZapTuple::extract(py_zap).unwrap();
let zap = ZapTuple::extract_bound(&py_zap).unwrap();
assert_eq!(zap.0, "whatever");
assert_eq!(zap.1, 3usize);
@ -521,10 +521,10 @@ fn test_from_py_with_tuple_struct() {
fn test_from_py_with_tuple_struct_error() {
Python::with_gil(|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");
let f = ZapTuple::extract(py_zap);
let f = ZapTuple::extract_bound(&py_zap);
assert!(f.is_err());
assert_eq!(
@ -544,10 +544,10 @@ pub enum ZapEnum {
fn test_from_py_with_enum() {
Python::with_gil(|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");
let zap = ZapEnum::extract(py_zap).unwrap();
let zap = ZapEnum::extract_bound(&py_zap).unwrap();
let expected_zap = ZapEnum::Zip(2);
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();
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);
dict_sub.as_ref(py).set_item("foo", item).unwrap();