Skip to content

Commit

Permalink
add bound dict constructors & py.run variants
Browse files Browse the repository at this point in the history
  • Loading branch information
davidhewitt committed Dec 31, 2023
1 parent 9ce82d0 commit 5e036fa
Show file tree
Hide file tree
Showing 92 changed files with 894 additions and 729 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,7 @@ To see unreleased changes, please see the [CHANGELOG on the main branch guide](h
### Added

- `module` argument to `pyclass` macro. [#499](https://github.com/PyO3/pyo3/pull/499)
- `py_run!` macro [#512](https://github.com/PyO3/pyo3/pull/512)
- `py_run_bound!` macro [#512](https://github.com/PyO3/pyo3/pull/512)
- Use existing fields and methods before calling custom **getattr**. [#505](https://github.com/PyO3/pyo3/pull/505)
- `PyBytes` can now be indexed just like `Vec<u8>`
- Implement `IntoPy<PyObject>` for `PyRef` and `PyRefMut`.
Expand Down
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_bound(py);
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
26 changes: 13 additions & 13 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ Python::with_gil(|py| {
}

// You can convert `&PyCell` to a Python object
pyo3::py_run!(py, obj, "assert obj.num == 5");
pyo3::py_run_bound!(py, obj, "assert obj.num == 5");
});
```

Expand Down Expand Up @@ -352,12 +352,12 @@ impl SubSubClass {
}
# Python::with_gil(|py| {
# let subsub = pyo3::PyCell::new(py, SubSubClass::new()).unwrap();
# pyo3::py_run!(py, subsub, "assert subsub.method3() == 3000");
# pyo3::py_run_bound!(py, subsub, "assert subsub.method3() == 3000");
# let subsub = SubSubClass::factory_method(py, 2).unwrap();
# let subsubsub = SubSubClass::factory_method(py, 3).unwrap();
# let cls = py.get_type::<SubSubClass>();
# pyo3::py_run!(py, subsub cls, "assert not isinstance(subsub, cls)");
# pyo3::py_run!(py, subsubsub cls, "assert isinstance(subsubsub, cls)");
# pyo3::py_run_bound!(py, subsub cls, "assert not isinstance(subsub, cls)");
# pyo3::py_run_bound!(py, subsubsub cls, "assert isinstance(subsubsub, cls)");
# });
```

Expand Down Expand Up @@ -396,7 +396,7 @@ impl DictWithCounter {
}
# Python::with_gil(|py| {
# let cnt = pyo3::PyCell::new(py, DictWithCounter::new()).unwrap();
# pyo3::py_run!(py, cnt, "cnt.set('abc', 10); assert cnt['abc'] == 10")
# pyo3::py_run_bound!(py, cnt, "cnt.set('abc', 10); assert cnt['abc'] == 10")
# });
# }
```
Expand Down Expand Up @@ -452,7 +452,7 @@ impl MyDict {
}
# Python::with_gil(|py| {
# let cls = py.get_type::<MyDict>();
# pyo3::py_run!(py, cls, "cls(a=1, b=2)")
# pyo3::py_run_bound!(py, cls, "cls(a=1, b=2)")
# });
# }
```
Expand Down Expand Up @@ -721,7 +721,7 @@ impl MyClass {

Python::with_gil(|py| {
let my_class = py.get_type::<MyClass>();
pyo3::py_run!(py, my_class, "assert my_class.my_attribute == 'hello'")
pyo3::py_run_bound!(py, my_class, "assert my_class.my_attribute == 'hello'")
});
```

Expand Down Expand Up @@ -974,7 +974,7 @@ Python::with_gil(|py| {
let x = Py::new(py, MyEnum::Variant).unwrap();
let y = Py::new(py, MyEnum::OtherVariant).unwrap();
let cls = py.get_type::<MyEnum>();
pyo3::py_run!(py, x y cls, r#"
pyo3::py_run_bound!(py, x y cls, r#"
assert x == cls.Variant
assert y == cls.OtherVariant
assert x != y
Expand All @@ -995,7 +995,7 @@ enum MyEnum {
Python::with_gil(|py| {
let cls = py.get_type::<MyEnum>();
let x = MyEnum::Variant as i32; // The exact value is assigned by the compiler.
pyo3::py_run!(py, cls x, r#"
pyo3::py_run_bound!(py, cls x, r#"
assert int(cls.Variant) == x
assert int(cls.OtherVariant) == 10
assert cls.OtherVariant == 10 # You can also compare against int.
Expand All @@ -1017,7 +1017,7 @@ enum MyEnum{
Python::with_gil(|py| {
let cls = py.get_type::<MyEnum>();
let x = Py::new(py, MyEnum::Variant).unwrap();
pyo3::py_run!(py, cls x, r#"
pyo3::py_run_bound!(py, cls x, r#"
assert repr(x) == 'MyEnum.Variant'
assert repr(cls.OtherVariant) == 'MyEnum.OtherVariant'
"#)
Expand All @@ -1042,7 +1042,7 @@ impl MyEnum {

Python::with_gil(|py| {
let cls = py.get_type::<MyEnum>();
pyo3::py_run!(py, cls, "assert repr(cls.Answer) == '42'")
pyo3::py_run_bound!(py, cls, "assert repr(cls.Answer) == '42'")
})
```

Expand All @@ -1059,7 +1059,7 @@ enum MyEnum {
Python::with_gil(|py| {
let x = Py::new(py, MyEnum::Variant).unwrap();
let cls = py.get_type::<MyEnum>();
pyo3::py_run!(py, x cls, r#"
pyo3::py_run_bound!(py, x cls, r#"
assert repr(x) == 'RenamedEnum.UPPERCASE'
assert x == cls.UPPERCASE
"#)
Expand Down Expand Up @@ -1187,7 +1187,7 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {

# Python::with_gil(|py| {
# let cls = py.get_type::<MyClass>();
# pyo3::py_run!(py, cls, "assert cls.__name__ == 'MyClass'")
# pyo3::py_run_bound!(py, cls, "assert cls.__name__ == 'MyClass'")
# });
# }
```
Expand Down
2 changes: 1 addition & 1 deletion guide/src/class/numeric.md
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ fn my_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> {
# let globals = PyModule::import(py, "__main__")?.dict();
# globals.set_item("Number", Number::type_object(py))?;
#
# py.run(SCRIPT, Some(globals), None)?;
# py.run_bound(SCRIPT, Some(&globals.as_borrowed()), None)?;
# Ok(())
# })
# }
Expand Down
4 changes: 2 additions & 2 deletions guide/src/class/protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,8 @@ impl Container {
# Python::with_gil(|py| {
# let container = Container { iter: vec![1, 2, 3, 4] };
# let inst = pyo3::PyCell::new(py, container).unwrap();
# pyo3::py_run!(py, inst, "assert list(inst) == [1, 2, 3, 4]");
# pyo3::py_run!(py, inst, "assert list(iter(iter(inst))) == [1, 2, 3, 4]");
# pyo3::py_run_bound!(py, inst, "assert list(inst) == [1, 2, 3, 4]");
# pyo3::py_run_bound!(py, inst, "assert list(iter(iter(inst))) == [1, 2, 3, 4]");
# });
```

Expand Down
4 changes: 2 additions & 2 deletions guide/src/conversions/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ struct RustyStruct {
# use pyo3::types::PyDict;
# fn main() -> PyResult<()> {
# Python::with_gil(|py| -> PyResult<()> {
# let dict = PyDict::new(py);
# let dict = PyDict::new_bound(py);
# dict.set_item("my_string", "test")?;
#
# let rustystruct: RustyStruct = dict.extract()?;
Expand Down 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
2 changes: 1 addition & 1 deletion guide/src/ecosystem/logging.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ fn main() -> PyResult<()> {

// Log some messages from Python
Python::with_gil(|py| {
py.run(
py.run_bound(
"
import logging
logging.error('Something bad happened')
Expand Down
6 changes: 3 additions & 3 deletions guide/src/exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,13 @@ use pyo3::exceptions::PyException;
create_exception!(mymodule, CustomError, PyException);

Python::with_gil(|py| {
let ctx = [("CustomError", py.get_type::<CustomError>())].into_py_dict(py);
pyo3::py_run!(
let ctx = [("CustomError", py.get_type::<CustomError>())].into_py_dict_bound(py);
pyo3::py_run_bound!(
py,
*ctx,
"assert str(CustomError) == \"<class 'mymodule.CustomError'>\""
);
pyo3::py_run!(py, *ctx, "assert CustomError('oops').args == ('oops',)");
pyo3::py_run_bound!(py, *ctx, "assert CustomError('oops').args == ('oops',)");
});
```

Expand Down
2 changes: 1 addition & 1 deletion guide/src/features.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ This feature enables a dependency on the `pyo3-macros` crate, which provides the
- `#[pymethods]`
- `#[derive(FromPyObject)]`

It also provides the `py_run!` macro.
It also provides the `py_run_bound!` macro.

These macros require a number of dependencies which may not be needed by users who just need PyO3 for Python FFI. Disabling this feature enables faster builds for those users, as these dependencies will not be built if this feature is disabled.

Expand Down
16 changes: 8 additions & 8 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: Bound<'_, PyString> = py.eval_bound("\"Hello World!\"", None, None)?.downcast_into()?;
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: Bound<'_, PyString> = py.eval_bound("\"Hello World!\"", None, None)?.downcast_into()?;
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: Bound<'_, PyString> = py.eval_bound("\"Hello World!\"", None, None)?.downcast_into()?;
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: Bound<'_, PyString> = py.eval_bound("\"Hello World!\"", None, None)?.downcast_into()?;
println!("Python says: {}", hello);
}
Ok(())
Expand Down Expand Up @@ -144,7 +144,7 @@ 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()?;
let hello: Py<PyString> = py.eval_bound("\"Hello World!\"", None, None)?.extract()?;
println!("Python says: {}", hello.as_ref(py));
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,7 +197,7 @@ 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| {
Expand All @@ -219,7 +219,7 @@ 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| {
Expand Down
14 changes: 7 additions & 7 deletions guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ Python::with_gil(|py| {

After:

```rust
```rust,ignore
use pyo3::prelude::*;
use pyo3::exceptions::PyTypeError;
use pyo3::types::{PyDict, IntoPyDict};
Expand Down Expand Up @@ -394,7 +394,7 @@ fn raise_err() -> anyhow::Result<()> {
# fn main() {
Python::with_gil(|py| {
let rs_func = wrap_pyfunction!(raise_err, py).unwrap();
pyo3::py_run!(
pyo3::py_run_bound!(
py,
rs_func,
r"
Expand Down Expand Up @@ -1191,7 +1191,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,ignore
use pyo3::ObjectProtocol;
# pyo3::Python::with_gil(|py| {
Expand All @@ -1202,7 +1202,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 @@ -1283,7 +1283,7 @@ impl Names {
}
# Python::with_gil(|py| {
# let names = PyCell::new(py, Names::new()).unwrap();
# pyo3::py_run!(py, names, r"
# pyo3::py_run_bound!(py, names, r"
# try:
# names.merge(names)
# assert False, 'Unreachable'
Expand Down Expand Up @@ -1348,8 +1348,8 @@ After:
# #[pymethods] impl MyClass { #[new]fn new() -> Self { MyClass {} }}
# Python::with_gil(|py| {
# let typeobj = py.get_type::<MyClass>();
# let d = [("c", typeobj)].into_py_dict(py);
# let create_obj = || py.eval("c()", None, Some(d)).unwrap();
# let d = [("c", typeobj)].into_py_dict_bound(py);
# let create_obj = || py.eval_bound("c()", None, Some(&d)).unwrap().into_gil_ref();
let obj: &PyAny = create_obj();
let obj_cell: &PyCell<MyClass> = obj.extract().unwrap();
let obj_cloned: MyClass = obj.extract().unwrap(); // extracted by cloning the object
Expand Down
4 changes: 2 additions & 2 deletions guide/src/module.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,9 @@ fn func() -> String {
# use pyo3::wrap_pymodule;
# use pyo3::types::IntoPyDict;
# 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_bound(py);
#
# 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();
# })
```

Expand Down
Loading

0 comments on commit 5e036fa

Please sign in to comment.