Skip to content

Commit

Permalink
Merge pull request #3806 from Icxolu/python-eval
Browse files Browse the repository at this point in the history
port `Python::eval` to `Bound` API
  • Loading branch information
davidhewitt authored Feb 9, 2024
2 parents 9bb0011 + 33dc33e commit 2fedea2
Show file tree
Hide file tree
Showing 21 changed files with 149 additions and 99 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
Expand Down
2 changes: 1 addition & 1 deletion guide/src/conversions/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
22 changes: 11 additions & 11 deletions guide/src/memory.md
Original file line number Diff line number Diff line change
Expand Up @@ -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(())
})?;
Expand All @@ -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.
Expand Down Expand Up @@ -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
Expand All @@ -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(())
Expand Down Expand Up @@ -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(())
Expand All @@ -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`.
Expand Down Expand Up @@ -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(())
Expand All @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -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| {
Expand All @@ -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();
Expand Down Expand Up @@ -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 {}
Expand Down
4 changes: 2 additions & 2 deletions guide/src/python_from_rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -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);
})?;
Expand Down Expand Up @@ -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").
Expand Down
8 changes: 4 additions & 4 deletions src/buffer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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: {:?}, ",
Expand Down Expand Up @@ -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");
Expand Down
5 changes: 3 additions & 2 deletions src/conversions/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::*;

Expand All @@ -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();
Expand Down
6 changes: 4 additions & 2 deletions src/conversions/num_bigint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
});
}
Expand Down
9 changes: 5 additions & 4 deletions src/conversions/std/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ mod tests {
sync::atomic::{AtomicUsize, Ordering},
};

use crate::types::any::PyAnyMethods;
use crate::{types::PyList, IntoPy, PyResult, Python, ToPyObject};

#[test]
Expand Down Expand Up @@ -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,
Expand All @@ -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();
Expand All @@ -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!(
Expand All @@ -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();
});
Expand Down
14 changes: 8 additions & 6 deletions src/conversions/std/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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));
})
Expand All @@ -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));
})
Expand Down Expand Up @@ -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));
})
Expand All @@ -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));
})
Expand All @@ -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));
})
Expand All @@ -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));
})
Expand Down
15 changes: 10 additions & 5 deletions src/conversions/std/slice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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");
});
Expand All @@ -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();
Expand Down
Loading

0 comments on commit 2fedea2

Please sign in to comment.