From 33dc33ececc49581450867a1993eec6ac14d8a1c Mon Sep 17 00:00:00 2001 From: Icxolu <10486322+Icxolu@users.noreply.github.com> Date: Mon, 5 Feb 2024 19:13:02 +0100 Subject: [PATCH] port `Python::eval` to `Bound` API --- README.md | 4 +- guide/src/conversions/traits.md | 2 +- guide/src/memory.md | 22 +++++----- guide/src/migration.md | 6 +-- guide/src/python_from_rust.md | 4 +- src/buffer.rs | 8 ++-- src/conversions/chrono.rs | 5 ++- src/conversions/num_bigint.rs | 6 ++- src/conversions/std/array.rs | 9 +++-- src/conversions/std/num.rs | 14 ++++--- src/conversions/std/slice.rs | 15 ++++--- src/exceptions.rs | 24 ++++++----- src/ffi/tests.rs | 5 ++- src/gil.rs | 5 ++- src/lib.rs | 4 +- src/marker.rs | 72 ++++++++++++++++++++++++--------- src/types/iterator.rs | 4 +- src/types/mod.rs | 4 +- tests/test_datetime.rs | 17 ++++---- tests/test_frompyobject.rs | 16 ++++---- tests/test_inheritance.rs | 2 +- 21 files changed, 149 insertions(+), 99 deletions(-) diff --git a/README.md b/README.md index 98eeeb80..606ef926 100644 --- a/README.md +++ b/README.md @@ -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(()) diff --git a/guide/src/conversions/traits.md b/guide/src/conversions/traits.md index 4f3342f2..71e823f8 100644 --- a/guide/src/conversions/traits.md +++ b/guide/src/conversions/traits.md @@ -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"); diff --git a/guide/src/memory.md b/guide/src/memory.md index f9201e3f..2f5e5d9b 100644 --- a/guide/src/memory.md +++ b/guide/src/memory.md @@ -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::()?; 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::()?; 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::()?; 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::()?; 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 = py.eval("\"Hello World!\"", None, None)?.extract()?; - println!("Python says: {}", hello.as_ref(py)); + let hello: Py = 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 = 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 = - 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 = - 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. diff --git a/guide/src/migration.md b/guide/src/migration.md index 6a151b99..dce6bdda 100644 --- a/guide/src/migration.md +++ b/guide/src/migration.md @@ -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 {} diff --git a/guide/src/python_from_rust.md b/guide/src/python_from_rust.md index 7b364ec5..2f3be53a 100644 --- a/guide/src/python_from_rust.md +++ b/guide/src/python_from_rust.md @@ -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"). diff --git a/src/buffer.rs b/src/buffer.rs index d18f05e2..7360ef67 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -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 = PyBuffer::get(bytes).unwrap(); + let bytes = py.eval_bound("b'abcde'", None, None).unwrap(); + let buffer: PyBuffer = 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"); diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index 6e529918..83f9830b 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -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(); diff --git a/src/conversions/num_bigint.rs b/src/conversions/num_bigint.rs index 1d536623..ebda6c85 100644 --- a/src/conversions/num_bigint.rs +++ b/src/conversions/num_bigint.rs @@ -261,6 +261,8 @@ fn int_n_bits(long: &Bound<'_, PyLong>) -> PyResult { #[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(); }); } diff --git a/src/conversions/std/array.rs b/src/conversions/std/array.rs index 0ea16713..016ab0a6 100644 --- a/src/conversions/std/array.rs +++ b/src/conversions/std/array.rs @@ -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::().unwrap(); v.extract::<[i32; 1]>().unwrap_err(); }); diff --git a/src/conversions/std/num.rs b/src/conversions/std/num.rs index 82f63016..2d20915d 100644 --- a/src/conversions/std/num.rs +++ b/src/conversions/std/num.rs @@ -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::().unwrap_err(); assert!(err.is_instance_of::(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::().unwrap_err(); assert!(err.is_instance_of::(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::().unwrap_err(); assert!(err.is_instance_of::(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::().unwrap_err(); assert!(err.is_instance_of::(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::().unwrap_err(); assert!(err.is_instance_of::(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::().unwrap_err(); assert!(err.is_instance_of::(py)); }) diff --git a/src/conversions/std/slice.rs b/src/conversions/std/slice.rs index 378c02c4..2d46abe2 100644 --- a/src/conversions/std/slice.rs +++ b/src/conversions/std/slice.rs @@ -61,12 +61,15 @@ impl IntoPy> 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::>().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::>().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::>() .unwrap_err(); diff --git a/src/exceptions.rs b/src/exceptions.rs index 19f59742..daea55e5 100644 --- a/src/exceptions.rs +++ b/src/exceptions.rs @@ -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::(); - 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, ""); + 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::(); - 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::(); - 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, ""); + 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::(); - 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, ""); + 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")) diff --git a/src/ffi/tests.rs b/src/ffi/tests.rs index 0e0d3688..fc10bf05 100644 --- a/src/ffi/tests.rs +++ b/src/ffi/tests.rs @@ -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(); diff --git a/src/gil.rs b/src/gil.rs index d346ad95..61d69ed9 100644 --- a/src/gil.rs +++ b/src/gil.rs @@ -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); diff --git a/src/lib.rs b/src/lib.rs index 3f91eb56..13b027d3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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(()) diff --git a/src/marker.rs b/src/marker.rs index 1987ffae..5addc33c 100644 --- a/src/marker.rs +++ b/src/marker.rs @@ -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::()?; +/// 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 = 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> { 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> { 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(); diff --git a/src/types/iterator.rs b/src/types/iterator.rs index e2703286..4ea83271 100644 --- a/src/types/iterator.rs +++ b/src/types/iterator.rs @@ -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> = list /// .iter()? -/// .map(|i| i.and_then(PyAny::extract::)) +/// .map(|i| i.and_then(|i|i.extract::())) /// .collect(); /// let sum: usize = numbers?.iter().sum(); /// assert_eq!(sum, 10); diff --git a/src/types/mod.rs b/src/types/mod.rs index f802348f..88e78a16 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -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::()?; /// -/// for (key, value) in dict { +/// for (key, value) in dict.iter() { /// println!("key: {}, value: {}", key, value); /// } /// diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index 32163abe..bfde37d3 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -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(); diff --git a/tests/test_frompyobject.rs b/tests/test_frompyobject.rs index c6f6e148..82e91b84 100644 --- a/tests/test_frompyobject.rs +++ b/tests/test_frompyobject.rs @@ -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); diff --git a/tests/test_inheritance.rs b/tests/test_inheritance.rs index d1cfe628..16f87df9 100644 --- a/tests/test_inheritance.rs +++ b/tests/test_inheritance.rs @@ -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();