Merge pull request #3816 from Icxolu/python-run

port `Python::run` to `Bound` API
This commit is contained in:
David Hewitt 2024-02-09 21:42:51 +00:00 committed by GitHub
commit 45f2b0aba5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
25 changed files with 198 additions and 148 deletions

View File

@ -387,10 +387,10 @@ fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
# #
# fn main() -> PyResult<()> { # fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> { # Python::with_gil(|py| -> PyResult<()> {
# let globals = PyModule::import(py, "__main__")?.dict(); # let globals = PyModule::import(py, "__main__")?.dict().as_borrowed();
# globals.set_item("Number", Number::type_object_bound(py))?; # globals.set_item("Number", Number::type_object_bound(py))?;
# #
# py.run(SCRIPT, Some(globals), None)?; # py.run_bound(SCRIPT, Some(&globals), None)?;
# Ok(()) # Ok(())
# }) # })
# } # }

View File

@ -93,9 +93,9 @@ fn func() -> String {
# use pyo3::wrap_pymodule; # use pyo3::wrap_pymodule;
# use pyo3::types::IntoPyDict; # use pyo3::types::IntoPyDict;
# let parent_module = wrap_pymodule!(parent_module)(py); # let parent_module = wrap_pymodule!(parent_module)(py);
# let ctx = [("parent_module", parent_module)].into_py_dict(py); # let ctx = [("parent_module", parent_module)].into_py_dict(py).as_borrowed();
# #
# py.run("assert parent_module.child_module.func() == 'func'", None, Some(&ctx)).unwrap(); # py.run_bound("assert parent_module.child_module.func() == 'func'", None, Some(&ctx)).unwrap();
# }) # })
``` ```

View File

@ -290,7 +290,7 @@ fn foo(_py: Python<'_>, foo_module: &PyModule) -> PyResult<()> {
fn main() -> PyResult<()> { fn main() -> PyResult<()> {
pyo3::append_to_inittab!(foo); pyo3::append_to_inittab!(foo);
Python::with_gil(|py| Python::run(py, "import foo; foo.add_one(6)", None, None)) Python::with_gil(|py| Python::run_bound(py, "import foo; foo.add_one(6)", None, None))
} }
``` ```
@ -321,7 +321,7 @@ fn main() -> PyResult<()> {
py_modules.set_item("foo", foo_module)?; py_modules.set_item("foo", foo_module)?;
// Now we can import + run our python code // Now we can import + run our python code
Python::run(py, "import foo; foo.add_one(6)", None, None) Python::run_bound(py, "import foo; foo.add_one(6)", None, None)
}) })
} }
``` ```

View File

@ -147,8 +147,8 @@ mod test_anyhow {
let pyerr = PyErr::from(err); let pyerr = PyErr::from(err);
Python::with_gil(|py| { Python::with_gil(|py| {
let locals = [("err", pyerr)].into_py_dict(py); let locals = [("err", pyerr)].into_py_dict(py).as_borrowed();
let pyerr = py.run("raise err", None, Some(locals)).unwrap_err(); let pyerr = py.run_bound("raise err", None, Some(&locals)).unwrap_err();
assert_eq!(pyerr.value(py).to_string(), expected_contents); assert_eq!(pyerr.value(py).to_string(), expected_contents);
}) })
} }
@ -164,8 +164,8 @@ mod test_anyhow {
let pyerr = PyErr::from(err); let pyerr = PyErr::from(err);
Python::with_gil(|py| { Python::with_gil(|py| {
let locals = [("err", pyerr)].into_py_dict(py); let locals = [("err", pyerr)].into_py_dict(py).as_borrowed();
let pyerr = py.run("raise err", None, Some(locals)).unwrap_err(); let pyerr = py.run_bound("raise err", None, Some(&locals)).unwrap_err();
assert_eq!(pyerr.value(py).to_string(), expected_contents); assert_eq!(pyerr.value(py).to_string(), expected_contents);
}) })
} }

View File

@ -574,12 +574,15 @@ mod tests {
// tzdata there to make this work. // tzdata there to make this work.
#[cfg(all(Py_3_9, not(target_os = "windows")))] #[cfg(all(Py_3_9, not(target_os = "windows")))]
fn test_zoneinfo_is_not_fixed_offset() { fn test_zoneinfo_is_not_fixed_offset() {
use crate::types::any::PyAnyMethods;
use crate::types::dict::PyDictMethods;
Python::with_gil(|py| { Python::with_gil(|py| {
let locals = crate::types::PyDict::new(py); let locals = crate::types::PyDict::new_bound(py);
py.run( py.run_bound(
"import zoneinfo; zi = zoneinfo.ZoneInfo('Europe/London')", "import zoneinfo; zi = zoneinfo.ZoneInfo('Europe/London')",
None, None,
Some(locals), Some(&locals),
) )
.unwrap(); .unwrap();
let result: PyResult<FixedOffset> = locals.get_item("zi").unwrap().unwrap().extract(); let result: PyResult<FixedOffset> = locals.get_item("zi").unwrap().unwrap().extract();

View File

@ -152,8 +152,8 @@ mod tests {
let pyerr = PyErr::from(err); let pyerr = PyErr::from(err);
Python::with_gil(|py| { Python::with_gil(|py| {
let locals = [("err", pyerr)].into_py_dict(py); let locals = [("err", pyerr)].into_py_dict(py).as_borrowed();
let pyerr = py.run("raise err", None, Some(locals)).unwrap_err(); let pyerr = py.run_bound("raise err", None, Some(&locals)).unwrap_err();
assert_eq!(pyerr.value(py).to_string(), expected_contents); assert_eq!(pyerr.value(py).to_string(), expected_contents);
}) })
} }
@ -169,8 +169,8 @@ mod tests {
let pyerr = PyErr::from(err); let pyerr = PyErr::from(err);
Python::with_gil(|py| { Python::with_gil(|py| {
let locals = [("err", pyerr)].into_py_dict(py); let locals = [("err", pyerr)].into_py_dict(py).as_borrowed();
let pyerr = py.run("raise err", None, Some(locals)).unwrap_err(); let pyerr = py.run_bound("raise err", None, Some(&locals)).unwrap_err();
assert_eq!(pyerr.value(py).to_string(), expected_contents); assert_eq!(pyerr.value(py).to_string(), expected_contents);
}) })
} }

View File

@ -108,6 +108,8 @@ impl IntoPy<PyObject> for Decimal {
mod test_rust_decimal { mod test_rust_decimal {
use super::*; use super::*;
use crate::err::PyErr; use crate::err::PyErr;
use crate::types::any::PyAnyMethods;
use crate::types::dict::PyDictMethods;
use crate::types::PyDict; use crate::types::PyDict;
use rust_decimal::Decimal; use rust_decimal::Decimal;
@ -121,16 +123,16 @@ mod test_rust_decimal {
Python::with_gil(|py| { Python::with_gil(|py| {
let rs_orig = $rs; let rs_orig = $rs;
let rs_dec = rs_orig.into_py(py); let rs_dec = rs_orig.into_py(py);
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("rs_dec", &rs_dec).unwrap(); locals.set_item("rs_dec", &rs_dec).unwrap();
// Checks if Rust Decimal -> Python Decimal conversion is correct // Checks if Rust Decimal -> Python Decimal conversion is correct
py.run( py.run_bound(
&format!( &format!(
"import decimal\npy_dec = decimal.Decimal({})\nassert py_dec == rs_dec", "import decimal\npy_dec = decimal.Decimal({})\nassert py_dec == rs_dec",
$py $py
), ),
None, None,
Some(locals), Some(&locals),
) )
.unwrap(); .unwrap();
// Checks if Python Decimal -> Rust Decimal conversion is correct // Checks if Python Decimal -> Rust Decimal conversion is correct
@ -163,13 +165,13 @@ mod test_rust_decimal {
let num = Decimal::from_parts(lo, mid, high, negative, scale); let num = Decimal::from_parts(lo, mid, high, negative, scale);
Python::with_gil(|py| { Python::with_gil(|py| {
let rs_dec = num.into_py(py); let rs_dec = num.into_py(py);
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("rs_dec", &rs_dec).unwrap(); locals.set_item("rs_dec", &rs_dec).unwrap();
py.run( py.run_bound(
&format!( &format!(
"import decimal\npy_dec = decimal.Decimal(\"{}\")\nassert py_dec == rs_dec", "import decimal\npy_dec = decimal.Decimal(\"{}\")\nassert py_dec == rs_dec",
num), num),
None, Some(locals)).unwrap(); None, Some(&locals)).unwrap();
let roundtripped: Decimal = rs_dec.extract(py).unwrap(); let roundtripped: Decimal = rs_dec.extract(py).unwrap();
assert_eq!(num, roundtripped); assert_eq!(num, roundtripped);
}) })
@ -189,11 +191,11 @@ mod test_rust_decimal {
#[test] #[test]
fn test_nan() { fn test_nan() {
Python::with_gil(|py| { Python::with_gil(|py| {
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
py.run( py.run_bound(
"import decimal\npy_dec = decimal.Decimal(\"NaN\")", "import decimal\npy_dec = decimal.Decimal(\"NaN\")",
None, None,
Some(locals), Some(&locals),
) )
.unwrap(); .unwrap();
let py_dec = locals.get_item("py_dec").unwrap().unwrap(); let py_dec = locals.get_item("py_dec").unwrap().unwrap();
@ -205,11 +207,11 @@ mod test_rust_decimal {
#[test] #[test]
fn test_infinity() { fn test_infinity() {
Python::with_gil(|py| { Python::with_gil(|py| {
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
py.run( py.run_bound(
"import decimal\npy_dec = decimal.Decimal(\"Infinity\")", "import decimal\npy_dec = decimal.Decimal(\"Infinity\")",
None, None,
Some(locals), Some(&locals),
) )
.unwrap(); .unwrap();
let py_dec = locals.get_item("py_dec").unwrap().unwrap(); let py_dec = locals.get_item("py_dec").unwrap().unwrap();

View File

@ -376,6 +376,9 @@ mod test_128bit_integers {
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use crate::types::PyDict; use crate::types::PyDict;
#[cfg(not(target_arch = "wasm32"))]
use crate::types::dict::PyDictMethods;
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
use proptest::prelude::*; use proptest::prelude::*;
@ -385,9 +388,9 @@ mod test_128bit_integers {
fn test_i128_roundtrip(x: i128) { fn test_i128_roundtrip(x: i128) {
Python::with_gil(|py| { Python::with_gil(|py| {
let x_py = x.into_py(py); let x_py = x.into_py(py);
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("x_py", x_py.clone_ref(py)).unwrap(); locals.set_item("x_py", x_py.clone_ref(py)).unwrap();
py.run(&format!("assert x_py == {}", x), None, Some(locals)).unwrap(); py.run_bound(&format!("assert x_py == {}", x), None, Some(&locals)).unwrap();
let roundtripped: i128 = x_py.extract(py).unwrap(); let roundtripped: i128 = x_py.extract(py).unwrap();
assert_eq!(x, roundtripped); assert_eq!(x, roundtripped);
}) })
@ -401,9 +404,9 @@ mod test_128bit_integers {
) { ) {
Python::with_gil(|py| { Python::with_gil(|py| {
let x_py = x.into_py(py); let x_py = x.into_py(py);
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("x_py", x_py.clone_ref(py)).unwrap(); locals.set_item("x_py", x_py.clone_ref(py)).unwrap();
py.run(&format!("assert x_py == {}", x), None, Some(locals)).unwrap(); py.run_bound(&format!("assert x_py == {}", x), None, Some(&locals)).unwrap();
let roundtripped: NonZeroI128 = x_py.extract(py).unwrap(); let roundtripped: NonZeroI128 = x_py.extract(py).unwrap();
assert_eq!(x, roundtripped); assert_eq!(x, roundtripped);
}) })
@ -416,9 +419,9 @@ mod test_128bit_integers {
fn test_u128_roundtrip(x: u128) { fn test_u128_roundtrip(x: u128) {
Python::with_gil(|py| { Python::with_gil(|py| {
let x_py = x.into_py(py); let x_py = x.into_py(py);
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("x_py", x_py.clone_ref(py)).unwrap(); locals.set_item("x_py", x_py.clone_ref(py)).unwrap();
py.run(&format!("assert x_py == {}", x), None, Some(locals)).unwrap(); py.run_bound(&format!("assert x_py == {}", x), None, Some(&locals)).unwrap();
let roundtripped: u128 = x_py.extract(py).unwrap(); let roundtripped: u128 = x_py.extract(py).unwrap();
assert_eq!(x, roundtripped); assert_eq!(x, roundtripped);
}) })
@ -432,9 +435,9 @@ mod test_128bit_integers {
) { ) {
Python::with_gil(|py| { Python::with_gil(|py| {
let x_py = x.into_py(py); let x_py = x.into_py(py);
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("x_py", x_py.clone_ref(py)).unwrap(); locals.set_item("x_py", x_py.clone_ref(py)).unwrap();
py.run(&format!("assert x_py == {}", x), None, Some(locals)).unwrap(); py.run_bound(&format!("assert x_py == {}", x), None, Some(&locals)).unwrap();
let roundtripped: NonZeroU128 = x_py.extract(py).unwrap(); let roundtripped: NonZeroU128 = x_py.extract(py).unwrap();
assert_eq!(x, roundtripped); assert_eq!(x, roundtripped);
}) })

View File

@ -1030,7 +1030,7 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
let err = py let err = py
.run("raise Exception('banana')", None, None) .run_bound("raise Exception('banana')", None, None)
.expect_err("raising should have given us an error"); .expect_err("raising should have given us an error");
let debug_str = format!("{:?}", err); let debug_str = format!("{:?}", err);
@ -1055,7 +1055,7 @@ mod tests {
fn err_display() { fn err_display() {
Python::with_gil(|py| { Python::with_gil(|py| {
let err = py let err = py
.run("raise Exception('banana')", None, None) .run_bound("raise Exception('banana')", None, None)
.expect_err("raising should have given us an error"); .expect_err("raising should have given us an error");
assert_eq!(err.to_string(), "Exception: banana"); assert_eq!(err.to_string(), "Exception: banana");
}); });
@ -1102,12 +1102,12 @@ mod tests {
fn test_pyerr_cause() { fn test_pyerr_cause() {
Python::with_gil(|py| { Python::with_gil(|py| {
let err = py let err = py
.run("raise Exception('banana')", None, None) .run_bound("raise Exception('banana')", None, None)
.expect_err("raising should have given us an error"); .expect_err("raising should have given us an error");
assert!(err.cause(py).is_none()); assert!(err.cause(py).is_none());
let err = py let err = py
.run( .run_bound(
"raise Exception('banana') from Exception('apple')", "raise Exception('banana') from Exception('apple')",
None, None,
None, None,

View File

@ -165,18 +165,18 @@ macro_rules! import_exception {
/// # fn main() -> PyResult<()> { /// # fn main() -> PyResult<()> {
/// # Python::with_gil(|py| -> PyResult<()> { /// # Python::with_gil(|py| -> PyResult<()> {
/// # let fun = wrap_pyfunction!(raise_myerror, py)?; /// # let fun = wrap_pyfunction!(raise_myerror, py)?;
/// # let locals = pyo3::types::PyDict::new(py); /// # let locals = pyo3::types::PyDict::new_bound(py);
/// # locals.set_item("MyError", py.get_type::<MyError>())?; /// # locals.set_item("MyError", py.get_type::<MyError>())?;
/// # locals.set_item("raise_myerror", fun)?; /// # locals.set_item("raise_myerror", fun)?;
/// # /// #
/// # py.run( /// # py.run_bound(
/// # "try: /// # "try:
/// # raise_myerror() /// # raise_myerror()
/// # except MyError as e: /// # except MyError as e:
/// # assert e.__doc__ == 'Some description.' /// # assert e.__doc__ == 'Some description.'
/// # assert str(e) == 'Some error happened.'", /// # assert str(e) == 'Some error happened.'",
/// # None, /// # None,
/// # Some(locals), /// # Some(&locals),
/// # )?; /// # )?;
/// # /// #
/// # Ok(()) /// # Ok(())
@ -338,7 +338,7 @@ use pyo3::prelude::*;
use pyo3::exceptions::Py", $name, "; use pyo3::exceptions::Py", $name, ";
Python::with_gil(|py| { Python::with_gil(|py| {
let result: PyResult<()> = py.run(\"raise ", $name, "\", None, None); let result: PyResult<()> = py.run_bound(\"raise ", $name, "\", None, None);
let error_type = match result { let error_type = match result {
Ok(_) => \"Not an error\", Ok(_) => \"Not an error\",
@ -816,7 +816,7 @@ mod tests {
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.expect("could not import socket"); .expect("could not import socket");
let d = PyDict::new(py); let d = PyDict::new_bound(py);
d.set_item("socket", socket) d.set_item("socket", socket)
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.expect("could not setitem"); .expect("could not setitem");
@ -825,7 +825,7 @@ mod tests {
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.expect("could not setitem"); .expect("could not setitem");
py.run("assert isinstance(exc, socket.gaierror)", None, Some(d)) py.run_bound("assert isinstance(exc, socket.gaierror)", None, Some(&d))
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.expect("assertion failed"); .expect("assertion failed");
}); });
@ -840,7 +840,7 @@ mod tests {
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.expect("could not import email"); .expect("could not import email");
let d = PyDict::new(py); let d = PyDict::new_bound(py);
d.set_item("email", email) d.set_item("email", email)
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.expect("could not setitem"); .expect("could not setitem");
@ -848,10 +848,10 @@ mod tests {
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.expect("could not setitem"); .expect("could not setitem");
py.run( py.run_bound(
"assert isinstance(exc, email.errors.MessageError)", "assert isinstance(exc, email.errors.MessageError)",
None, None,
Some(d), Some(&d),
) )
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.expect("assertion failed"); .expect("assertion failed");
@ -871,14 +871,13 @@ mod tests {
.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_bound(
py.run(
"assert CustomError('oops').args == ('oops',)", "assert CustomError('oops').args == ('oops',)",
None, None,
Some(ctx), Some(&ctx),
) )
.unwrap(); .unwrap();
py.run("assert CustomError.__doc__ is None", None, Some(ctx)) py.run_bound("assert CustomError.__doc__ is None", None, Some(&ctx))
.unwrap(); .unwrap();
}); });
} }
@ -914,15 +913,18 @@ mod tests {
.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_bound(
py.run(
"assert CustomError('oops').args == ('oops',)", "assert CustomError('oops').args == ('oops',)",
None, None,
Some(ctx), Some(&ctx),
)
.unwrap();
py.run_bound(
"assert CustomError.__doc__ == 'Some docs'",
None,
Some(&ctx),
) )
.unwrap(); .unwrap();
py.run("assert CustomError.__doc__ == 'Some docs'", None, Some(ctx))
.unwrap();
}); });
} }
@ -944,17 +946,16 @@ mod tests {
.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_bound(
py.run(
"assert CustomError('oops').args == ('oops',)", "assert CustomError('oops').args == ('oops',)",
None, None,
Some(ctx), Some(&ctx),
) )
.unwrap(); .unwrap();
py.run( py.run_bound(
"assert CustomError.__doc__ == 'Some more docs'", "assert CustomError.__doc__ == 'Some more docs'",
None, None,
Some(ctx), Some(&ctx),
) )
.unwrap(); .unwrap();
}); });
@ -964,7 +965,7 @@ mod tests {
fn native_exception_debug() { fn native_exception_debug() {
Python::with_gil(|py| { Python::with_gil(|py| {
let exc = py let exc = py
.run("raise Exception('banana')", None, None) .run_bound("raise Exception('banana')", None, None)
.expect_err("raising should have given us an error") .expect_err("raising should have given us an error")
.into_value(py) .into_value(py)
.into_ref(py); .into_ref(py);
@ -979,7 +980,7 @@ mod tests {
fn native_exception_display() { fn native_exception_display() {
Python::with_gil(|py| { Python::with_gil(|py| {
let exc = py let exc = py
.run("raise Exception('banana')", None, None) .run_bound("raise Exception('banana')", None, None)
.expect_err("raising should have given us an error") .expect_err("raising should have given us an error")
.into_value(py) .into_value(py)
.into_ref(py); .into_ref(py);
@ -996,7 +997,7 @@ mod tests {
Python::with_gil(|py| { Python::with_gil(|py| {
let exc = py let exc = py
.run( .run_bound(
"raise Exception('banana') from TypeError('peach')", "raise Exception('banana') from TypeError('peach')",
None, None,
None, None,

View File

@ -20,12 +20,12 @@ fn test_datetime_fromtimestamp() {
PyDateTime_IMPORT(); PyDateTime_IMPORT();
py.from_owned_ptr(PyDateTime_FromTimestamp(args.as_ptr())) py.from_owned_ptr(PyDateTime_FromTimestamp(args.as_ptr()))
}; };
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("dt", dt).unwrap(); locals.set_item("dt", dt).unwrap();
py.run( py.run_bound(
"import datetime; assert dt == datetime.datetime.fromtimestamp(100)", "import datetime; assert dt == datetime.datetime.fromtimestamp(100)",
None, None,
Some(locals), Some(&locals),
) )
.unwrap(); .unwrap();
}) })
@ -41,12 +41,12 @@ fn test_date_fromtimestamp() {
PyDateTime_IMPORT(); PyDateTime_IMPORT();
py.from_owned_ptr(PyDate_FromTimestamp(args.as_ptr())) py.from_owned_ptr(PyDate_FromTimestamp(args.as_ptr()))
}; };
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("dt", dt).unwrap(); locals.set_item("dt", dt).unwrap();
py.run( py.run_bound(
"import datetime; assert dt == datetime.date.fromtimestamp(100)", "import datetime; assert dt == datetime.date.fromtimestamp(100)",
None, None,
Some(locals), Some(&locals),
) )
.unwrap(); .unwrap();
}) })
@ -61,12 +61,12 @@ fn test_utc_timezone() {
PyDateTime_IMPORT(); PyDateTime_IMPORT();
py.from_borrowed_ptr(PyDateTime_TimeZone_UTC()) py.from_borrowed_ptr(PyDateTime_TimeZone_UTC())
}; };
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("utc_timezone", utc_timezone).unwrap(); locals.set_item("utc_timezone", utc_timezone).unwrap();
py.run( py.run_bound(
"import datetime; assert utc_timezone is datetime.timezone.utc", "import datetime; assert utc_timezone is datetime.timezone.utc",
None, None,
Some(locals), Some(&locals),
) )
.unwrap(); .unwrap();
}) })

View File

@ -75,7 +75,7 @@ fn gil_is_acquired() -> bool {
/// ///
/// # fn main() -> PyResult<()> { /// # fn main() -> PyResult<()> {
/// pyo3::prepare_freethreaded_python(); /// pyo3::prepare_freethreaded_python();
/// Python::with_gil(|py| py.run("print('Hello World')", None, None)) /// Python::with_gil(|py| py.run_bound("print('Hello World')", None, None))
/// # } /// # }
/// ``` /// ```
#[cfg(not(PyPy))] #[cfg(not(PyPy))]
@ -118,7 +118,7 @@ pub fn prepare_freethreaded_python() {
/// ```rust /// ```rust
/// unsafe { /// unsafe {
/// pyo3::with_embedded_python_interpreter(|py| { /// pyo3::with_embedded_python_interpreter(|py| {
/// if let Err(e) = py.run("print('Hello World')", None, None) { /// if let Err(e) = py.run_bound("print('Hello World')", None, None) {
/// // We must make sure to not return a `PyErr`! /// // We must make sure to not return a `PyErr`!
/// e.print(py); /// e.print(py);
/// } /// }

View File

@ -104,12 +104,13 @@ macro_rules! py_run_impl {
}}; }};
($py:expr, *$dict:expr, $code:expr) => {{ ($py:expr, *$dict:expr, $code:expr) => {{
use ::std::option::Option::*; use ::std::option::Option::*;
if let ::std::result::Result::Err(e) = $py.run($code, None, Some($dict)) { use $crate::PyNativeType;
if let ::std::result::Result::Err(e) = $py.run_bound($code, None, Some(&$dict.as_borrowed())) {
e.print($py); e.print($py);
// So when this c api function the last line called printed the error to stderr, // So when this c api function the last line called printed the error to stderr,
// the output is only written into a buffer which is never flushed because we // the output is only written into a buffer which is never flushed because we
// panic before flushing. This is where this hack comes into place // panic before flushing. This is where this hack comes into place
$py.run("import sys; sys.stderr.flush()", None, None) $py.run_bound("import sys; sys.stderr.flush()", None, None)
.unwrap(); .unwrap();
::std::panic!("{}", $code) ::std::panic!("{}", $code)
} }

View File

@ -600,6 +600,27 @@ impl<'py> Python<'py> {
self.run_code(code, ffi::Py_eval_input, globals, locals) self.run_code(code, ffi::Py_eval_input, globals, locals)
} }
/// Deprecated version of [`Python::run_bound`]
#[cfg_attr(
not(feature = "gil-refs"),
deprecated(
since = "0.21.0",
note = "`Python::run` will be replaced by `Python::run_bound` in a future PyO3 version"
)
)]
pub fn run(
self,
code: &str,
globals: Option<&PyDict>,
locals: Option<&PyDict>,
) -> PyResult<()> {
self.run_bound(
code,
globals.map(PyNativeType::as_borrowed).as_deref(),
locals.map(PyNativeType::as_borrowed).as_deref(),
)
}
/// Executes one or more Python statements in the given context. /// Executes one or more Python statements in the given context.
/// ///
/// If `globals` is `None`, it defaults to Python module `__main__`. /// If `globals` is `None`, it defaults to Python module `__main__`.
@ -615,37 +636,32 @@ impl<'py> Python<'py> {
/// types::{PyBytes, PyDict}, /// types::{PyBytes, PyDict},
/// }; /// };
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let locals = PyDict::new(py); /// let locals = PyDict::new_bound(py);
/// py.run( /// py.run_bound(
/// r#" /// r#"
/// import base64 /// import base64
/// s = 'Hello Rust!' /// s = 'Hello Rust!'
/// ret = base64.b64encode(s.encode('utf-8')) /// ret = base64.b64encode(s.encode('utf-8'))
/// "#, /// "#,
/// None, /// None,
/// Some(locals), /// Some(&locals),
/// ) /// )
/// .unwrap(); /// .unwrap();
/// let ret = locals.get_item("ret").unwrap().unwrap(); /// let ret = locals.get_item("ret").unwrap().unwrap();
/// let b64: &PyBytes = ret.downcast().unwrap(); /// let b64 = ret.downcast::<PyBytes>().unwrap();
/// assert_eq!(b64.as_bytes(), b"SGVsbG8gUnVzdCE="); /// assert_eq!(b64.as_bytes(), b"SGVsbG8gUnVzdCE=");
/// }); /// });
/// ``` /// ```
/// ///
/// You can use [`py_run!`](macro.py_run.html) for a handy alternative of `run` /// You can use [`py_run!`](macro.py_run.html) for a handy alternative of `run`
/// if you don't need `globals` and unwrapping is OK. /// if you don't need `globals` and unwrapping is OK.
pub fn run( pub fn run_bound(
self, self,
code: &str, code: &str,
globals: Option<&PyDict>, globals: Option<&Bound<'py, PyDict>>,
locals: Option<&PyDict>, locals: Option<&Bound<'py, PyDict>>,
) -> PyResult<()> { ) -> PyResult<()> {
let res = self.run_code( let res = self.run_code(code, ffi::Py_file_input, globals, locals);
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());
}) })
@ -1235,9 +1251,11 @@ mod tests {
#[test] #[test]
fn test_py_run_inserts_globals() { fn test_py_run_inserts_globals() {
use crate::types::dict::PyDictMethods;
Python::with_gil(|py| { Python::with_gil(|py| {
let namespace = PyDict::new(py); let namespace = PyDict::new_bound(py);
py.run("class Foo: pass", Some(namespace), Some(namespace)) py.run_bound("class Foo: pass", Some(&namespace), Some(&namespace))
.unwrap(); .unwrap();
assert!(matches!(namespace.get_item("Foo"), Ok(Some(..)))); assert!(matches!(namespace.get_item("Foo"), Ok(Some(..))));
assert!(matches!(namespace.get_item("__builtins__"), Ok(Some(..)))); assert!(matches!(namespace.get_item("__builtins__"), Ok(Some(..))));

View File

@ -40,7 +40,8 @@ mod inner {
}}; }};
// Case2: dict & no err_msg // Case2: dict & no err_msg
($py:expr, *$dict:expr, $code:expr, $err:ident) => {{ ($py:expr, *$dict:expr, $code:expr, $err:ident) => {{
let res = $py.run($code, None, Some($dict)); use pyo3::PyNativeType;
let res = $py.run_bound($code, None, Some(&$dict.as_borrowed()));
let err = res.expect_err(&format!("Did not raise {}", stringify!($err))); let err = res.expect_err(&format!("Did not raise {}", stringify!($err)));
if !err.matches($py, $py.get_type::<pyo3::exceptions::$err>()) { if !err.matches($py, $py.get_type::<pyo3::exceptions::$err>()) {
panic!("Expected {} but got {:?}", stringify!($err), err) panic!("Expected {} but got {:?}", stringify!($err), err)

View File

@ -197,10 +197,10 @@ impl PyByteArray {
/// # fn main() -> PyResult<()> { /// # fn main() -> PyResult<()> {
/// # Python::with_gil(|py| -> PyResult<()> { /// # Python::with_gil(|py| -> PyResult<()> {
/// # let fun = wrap_pyfunction!(a_valid_function, py)?; /// # let fun = wrap_pyfunction!(a_valid_function, py)?;
/// # let locals = pyo3::types::PyDict::new(py); /// # let locals = pyo3::types::PyDict::new_bound(py);
/// # locals.set_item("a_valid_function", fun)?; /// # locals.set_item("a_valid_function", fun)?;
/// # /// #
/// # py.run( /// # py.run_bound(
/// # r#"b = bytearray(b"hello world") /// # r#"b = bytearray(b"hello world")
/// # a_valid_function(b) /// # a_valid_function(b)
/// # /// #
@ -209,7 +209,7 @@ impl PyByteArray {
/// # except RuntimeError as e: /// # except RuntimeError as e:
/// # assert str(e) == 'input is not long enough'"#, /// # assert str(e) == 'input is not long enough'"#,
/// # None, /// # None,
/// # Some(locals), /// # Some(&locals),
/// # )?; /// # )?;
/// # /// #
/// # Ok(()) /// # Ok(())
@ -359,10 +359,10 @@ pub trait PyByteArrayMethods<'py> {
/// # fn main() -> PyResult<()> { /// # fn main() -> PyResult<()> {
/// # Python::with_gil(|py| -> PyResult<()> { /// # Python::with_gil(|py| -> PyResult<()> {
/// # let fun = wrap_pyfunction!(a_valid_function, py)?; /// # let fun = wrap_pyfunction!(a_valid_function, py)?;
/// # let locals = pyo3::types::PyDict::new(py); /// # let locals = pyo3::types::PyDict::new_bound(py);
/// # locals.set_item("a_valid_function", fun)?; /// # locals.set_item("a_valid_function", fun)?;
/// # /// #
/// # py.run( /// # py.run_bound(
/// # r#"b = bytearray(b"hello world") /// # r#"b = bytearray(b"hello world")
/// # a_valid_function(b) /// # a_valid_function(b)
/// # /// #
@ -371,7 +371,7 @@ pub trait PyByteArrayMethods<'py> {
/// # except RuntimeError as e: /// # except RuntimeError as e:
/// # assert str(e) == 'input is not long enough'"#, /// # assert str(e) == 'input is not long enough'"#,
/// # None, /// # None,
/// # Some(locals), /// # Some(&locals),
/// # )?; /// # )?;
/// # /// #
/// # Ok(()) /// # Ok(())

View File

@ -28,7 +28,7 @@ impl PyTraceback {
/// # let result: PyResult<()> = /// # let result: PyResult<()> =
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let err = py /// let err = py
/// .run("raise Exception('banana')", None, None) /// .run_bound("raise Exception('banana')", None, None)
/// .expect_err("raise will create a Python error"); /// .expect_err("raise will create a Python error");
/// ///
/// let traceback = err.traceback_bound(py).expect("raised exception will have a traceback"); /// let traceback = err.traceback_bound(py).expect("raised exception will have a traceback");
@ -71,7 +71,7 @@ pub trait PyTracebackMethods<'py> {
/// # let result: PyResult<()> = /// # let result: PyResult<()> =
/// Python::with_gil(|py| { /// Python::with_gil(|py| {
/// let err = py /// let err = py
/// .run("raise Exception('banana')", None, None) /// .run_bound("raise Exception('banana')", None, None)
/// .expect_err("raise will create a Python error"); /// .expect_err("raise will create a Python error");
/// ///
/// let traceback = err.traceback_bound(py).expect("raised exception will have a traceback"); /// let traceback = err.traceback_bound(py).expect("raised exception will have a traceback");
@ -121,7 +121,7 @@ mod tests {
fn format_traceback() { fn format_traceback() {
Python::with_gil(|py| { Python::with_gil(|py| {
let err = py let err = py
.run("raise Exception('banana')", None, None) .run_bound("raise Exception('banana')", None, None)
.expect_err("raising should have given us an error"); .expect_err("raising should have given us an error");
assert_eq!( assert_eq!(
@ -134,9 +134,9 @@ mod tests {
#[test] #[test]
fn test_err_from_value() { fn test_err_from_value() {
Python::with_gil(|py| { Python::with_gil(|py| {
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
// Produce an error from python so that it has a traceback // Produce an error from python so that it has a traceback
py.run( py.run_bound(
r" r"
try: try:
raise ValueError('raised exception') raise ValueError('raised exception')
@ -144,10 +144,10 @@ except Exception as e:
err = e err = e
", ",
None, None,
Some(locals), Some(&locals),
) )
.unwrap(); .unwrap();
let err = PyErr::from_value(locals.get_item("err").unwrap().unwrap()); let err = PyErr::from_value(locals.get_item("err").unwrap().unwrap().into_gil_ref());
let traceback = err.value(py).getattr("__traceback__").unwrap(); let traceback = err.value(py).getattr("__traceback__").unwrap();
assert!(err.traceback_bound(py).unwrap().is(traceback)); assert!(err.traceback_bound(py).unwrap().is(traceback));
}) })
@ -156,15 +156,15 @@ except Exception as e:
#[test] #[test]
fn test_err_into_py() { fn test_err_into_py() {
Python::with_gil(|py| { Python::with_gil(|py| {
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
// Produce an error from python so that it has a traceback // Produce an error from python so that it has a traceback
py.run( py.run_bound(
r" r"
def f(): def f():
raise ValueError('raised exception') raise ValueError('raised exception')
", ",
None, None,
Some(locals), Some(&locals),
) )
.unwrap(); .unwrap();
let f = locals.get_item("f").unwrap().unwrap(); let f = locals.get_item("f").unwrap().unwrap();

View File

@ -25,6 +25,7 @@ fn test_anyhow_py_function_ok_result() {
#[test] #[test]
fn test_anyhow_py_function_err_result() { fn test_anyhow_py_function_err_result() {
use pyo3::prelude::PyDictMethods;
use pyo3::{pyfunction, types::PyDict, wrap_pyfunction, Python}; use pyo3::{pyfunction, types::PyDict, wrap_pyfunction, Python};
#[pyfunction] #[pyfunction]
@ -34,15 +35,15 @@ fn test_anyhow_py_function_err_result() {
Python::with_gil(|py| { Python::with_gil(|py| {
let func = wrap_pyfunction!(produce_err_result)(py).unwrap(); let func = wrap_pyfunction!(produce_err_result)(py).unwrap();
let locals = PyDict::new(py); let locals = PyDict::new_bound(py);
locals.set_item("func", func).unwrap(); locals.set_item("func", func).unwrap();
py.run( py.run_bound(
r#" r#"
func() func()
"#, "#,
None, None,
Some(locals), Some(&locals),
) )
.unwrap_err(); .unwrap_err();
}); });

View File

@ -18,7 +18,7 @@ fn test_module_append_to_inittab() {
use pyo3::append_to_inittab; use pyo3::append_to_inittab;
append_to_inittab!(module_with_functions); append_to_inittab!(module_with_functions);
Python::with_gil(|py| { Python::with_gil(|py| {
py.run( py.run_bound(
r#" r#"
import module_with_functions import module_with_functions
assert module_with_functions.foo() == 123 assert module_with_functions.foo() == 123

View File

@ -169,9 +169,12 @@ c = Class()
assert c.from_rust is False assert c.from_rust is False
"# "#
); );
let globals = PyModule::import(py, "__main__").unwrap().dict(); let globals = PyModule::import(py, "__main__")
.unwrap()
.dict()
.as_borrowed();
globals.set_item("SuperClass", super_cls).unwrap(); globals.set_item("SuperClass", super_cls).unwrap();
py.run(source, Some(globals), None) py.run_bound(source, Some(&globals), None)
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.unwrap(); .unwrap();
}); });

View File

@ -127,12 +127,12 @@ fn cancelled_coroutine() {
await task await task
asyncio.run(main()) asyncio.run(main())
"#; "#;
let globals = gil.import("__main__").unwrap().dict(); let globals = gil.import("__main__").unwrap().dict().as_borrowed();
globals.set_item("sleep", sleep).unwrap(); globals.set_item("sleep", sleep).unwrap();
let err = gil let err = gil
.run( .run_bound(
&pyo3::unindent::unindent(&handle_windows(test)), &pyo3::unindent::unindent(&handle_windows(test)),
Some(globals), Some(&globals),
None, None,
) )
.unwrap_err(); .unwrap_err();
@ -166,13 +166,13 @@ fn coroutine_cancel_handle() {
return await task return await task
assert asyncio.run(main()) == 0 assert asyncio.run(main()) == 0
"#; "#;
let globals = gil.import("__main__").unwrap().dict(); let globals = gil.import("__main__").unwrap().dict().as_borrowed();
globals globals
.set_item("cancellable_sleep", cancellable_sleep) .set_item("cancellable_sleep", cancellable_sleep)
.unwrap(); .unwrap();
gil.run( gil.run_bound(
&pyo3::unindent::unindent(&handle_windows(test)), &pyo3::unindent::unindent(&handle_windows(test)),
Some(globals), Some(&globals),
None, None,
) )
.unwrap(); .unwrap();
@ -198,11 +198,11 @@ fn coroutine_is_cancelled() {
await task await task
asyncio.run(main()) asyncio.run(main())
"#; "#;
let globals = gil.import("__main__").unwrap().dict(); let globals = gil.import("__main__").unwrap().dict().as_borrowed();
globals.set_item("sleep_loop", sleep_loop).unwrap(); globals.set_item("sleep_loop", sleep_loop).unwrap();
gil.run( gil.run_bound(
&pyo3::unindent::unindent(&handle_windows(test)), &pyo3::unindent::unindent(&handle_windows(test)),
Some(globals), Some(&globals),
None, None,
) )
.unwrap(); .unwrap();

View File

@ -12,14 +12,16 @@ fn _get_subclasses<'py>(
// 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")?;
let locals = [(py_type, datetime.getattr(py_type)?)].into_py_dict(py); let locals = [(py_type, datetime.getattr(py_type)?)]
.into_py_dict(py)
.as_borrowed();
let make_subclass_py = format!("class Subklass({}):\n pass", py_type); let make_subclass_py = format!("class Subklass({}):\n pass", py_type);
let make_sub_subclass_py = "class SubSubklass(Subklass):\n pass"; let make_sub_subclass_py = "class SubSubklass(Subklass):\n pass";
py.run(&make_subclass_py, None, Some(locals))?; py.run_bound(&make_subclass_py, None, Some(&locals))?;
py.run(make_sub_subclass_py, None, Some(locals))?; py.run_bound(make_sub_subclass_py, None, Some(&locals))?;
let locals = locals.as_borrowed(); let locals = locals.as_borrowed();
// Construct an instance of the base class // Construct an instance of the base class

View File

@ -117,7 +117,7 @@ fn gc_integration() {
}); });
Python::with_gil(|py| { Python::with_gil(|py| {
py.run("import gc; gc.collect()", None, None).unwrap(); py.run_bound("import gc; gc.collect()", None, None).unwrap();
assert!(drop_called.load(Ordering::Relaxed)); assert!(drop_called.load(Ordering::Relaxed));
}); });
} }
@ -156,7 +156,7 @@ fn gc_null_traversal() {
obj.borrow_mut(py).cycle = Some(obj.clone_ref(py)); obj.borrow_mut(py).cycle = Some(obj.clone_ref(py));
// the object doesn't have to be cleaned up, it just needs to be traversed. // the object doesn't have to be cleaned up, it just needs to be traversed.
py.run("import gc; gc.collect()", None, None).unwrap(); py.run_bound("import gc; gc.collect()", None, None).unwrap();
}); });
} }
@ -469,7 +469,7 @@ fn drop_during_traversal_with_gil() {
// (but not too many) collections to get `inst` actually dropped. // (but not too many) collections to get `inst` actually dropped.
for _ in 0..10 { for _ in 0..10 {
Python::with_gil(|py| { Python::with_gil(|py| {
py.run("import gc; gc.collect()", None, None).unwrap(); py.run_bound("import gc; gc.collect()", None, None).unwrap();
}); });
} }
assert!(drop_called.load(Ordering::Relaxed)); assert!(drop_called.load(Ordering::Relaxed));
@ -502,7 +502,7 @@ fn drop_during_traversal_without_gil() {
// (but not too many) collections to get `inst` actually dropped. // (but not too many) collections to get `inst` actually dropped.
for _ in 0..10 { for _ in 0..10 {
Python::with_gil(|py| { Python::with_gil(|py| {
py.run("import gc; gc.collect()", None, None).unwrap(); py.run_bound("import gc; gc.collect()", None, None).unwrap();
}); });
} }
assert!(drop_called.load(Ordering::Relaxed)); assert!(drop_called.load(Ordering::Relaxed));

View File

@ -20,12 +20,14 @@ struct SubclassAble {}
#[test] #[test]
fn subclass() { fn subclass() {
Python::with_gil(|py| { Python::with_gil(|py| {
let d = [("SubclassAble", py.get_type::<SubclassAble>())].into_py_dict(py); let d = [("SubclassAble", py.get_type::<SubclassAble>())]
.into_py_dict(py)
.as_borrowed();
py.run( py.run_bound(
"class A(SubclassAble): pass\nassert issubclass(A, SubclassAble)", "class A(SubclassAble): pass\nassert issubclass(A, SubclassAble)",
None, None,
Some(d), Some(&d),
) )
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.unwrap(); .unwrap();
@ -97,9 +99,13 @@ fn call_base_and_sub_methods() {
fn mutation_fails() { fn mutation_fails() {
Python::with_gil(|py| { Python::with_gil(|py| {
let obj = PyCell::new(py, SubClass::new()).unwrap(); let obj = PyCell::new(py, SubClass::new()).unwrap();
let global = Some([("obj", obj)].into_py_dict(py)); let global = [("obj", obj)].into_py_dict(py).as_borrowed();
let e = py let e = py
.run("obj.base_set(lambda: obj.sub_set_and_ret(1))", global, None) .run_bound(
"obj.base_set(lambda: obj.sub_set_and_ret(1))",
Some(&global),
None,
)
.unwrap_err(); .unwrap_err();
assert_eq!(&e.to_string(), "RuntimeError: Already borrowed"); assert_eq!(&e.to_string(), "RuntimeError: Already borrowed");
}); });
@ -271,11 +277,11 @@ mod inheriting_native_type {
fn custom_exception() { fn custom_exception() {
Python::with_gil(|py| { Python::with_gil(|py| {
let cls = py.get_type::<CustomException>(); let cls = py.get_type::<CustomException>();
let dict = [("cls", cls)].into_py_dict(py); let dict = [("cls", cls)].into_py_dict(py).as_borrowed();
let res = py.run( let res = py.run_bound(
"e = cls('hello'); assert str(e) == 'hello'; assert e.context == 'Hello :)'; raise e", "e = cls('hello'); assert str(e) == 'hello'; assert e.context == 'Hello :)'; raise e",
None, None,
Some(dict) Some(&dict)
); );
let err = res.unwrap_err(); let err = res.unwrap_err();
assert!(err.matches(py, cls), "{}", err); assert!(err.matches(py, cls), "{}", err);

View File

@ -687,9 +687,12 @@ if sys.platform == "win32" and sys.version_info >= (3, 8, 0):
asyncio.run(main()) asyncio.run(main())
"#; "#;
let globals = PyModule::import(py, "__main__").unwrap().dict(); let globals = PyModule::import(py, "__main__")
.unwrap()
.dict()
.as_borrowed();
globals.set_item("Once", once).unwrap(); globals.set_item("Once", once).unwrap();
py.run(source, Some(globals), None) py.run_bound(source, Some(&globals), None)
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.unwrap(); .unwrap();
}); });
@ -741,12 +744,15 @@ if sys.platform == "win32" and sys.version_info >= (3, 8, 0):
asyncio.run(main()) asyncio.run(main())
"#; "#;
let globals = PyModule::import(py, "__main__").unwrap().dict(); let globals = PyModule::import(py, "__main__")
.unwrap()
.dict()
.as_borrowed();
globals.set_item("Once", once).unwrap(); globals.set_item("Once", once).unwrap();
globals globals
.set_item("AsyncIterator", py.get_type::<AsyncIterator>()) .set_item("AsyncIterator", py.get_type::<AsyncIterator>())
.unwrap(); .unwrap();
py.run(source, Some(globals), None) py.run_bound(source, Some(&globals), None)
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.unwrap(); .unwrap();
}); });
@ -813,9 +819,12 @@ del c.counter
assert c.counter.count == 1 assert c.counter.count == 1
"# "#
); );
let globals = PyModule::import(py, "__main__").unwrap().dict(); let globals = PyModule::import(py, "__main__")
.unwrap()
.dict()
.as_borrowed();
globals.set_item("Counter", counter).unwrap(); globals.set_item("Counter", counter).unwrap();
py.run(source, Some(globals), None) py.run_bound(source, Some(&globals), None)
.map_err(|e| e.display(py)) .map_err(|e| e.display(py))
.unwrap(); .unwrap();
}); });