Skip to content

Commit

Permalink
Merge pull request #1511 from PyO3/use-with-gil-in-docs
Browse files Browse the repository at this point in the history
Use with_gil instead of acquire_gil in examples
  • Loading branch information
messense authored Mar 20, 2021
2 parents 3fa10a3 + aedd635 commit f909e66
Show file tree
Hide file tree
Showing 18 changed files with 269 additions and 291 deletions.
89 changes: 43 additions & 46 deletions guide/src/class.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,25 @@ struct MyClass {
num: i32,
debug: bool,
}
let gil = Python::acquire_gil();
let py = gil.python();
let obj = PyCell::new(py, MyClass { num: 3, debug: true }).unwrap();
{
let obj_ref = obj.borrow(); // Get PyRef
assert_eq!(obj_ref.num, 3);
// You cannot get PyRefMut unless all PyRefs are dropped
assert!(obj.try_borrow_mut().is_err());
}
{
let mut obj_mut = obj.borrow_mut(); // Get PyRefMut
obj_mut.num = 5;
// You cannot get any other refs until the PyRefMut is dropped
assert!(obj.try_borrow().is_err());
assert!(obj.try_borrow_mut().is_err());
}

// You can convert `&PyCell` to a Python object
pyo3::py_run!(py, obj, "assert obj.num == 5")
Python::with_gil(|py| {
let obj = PyCell::new(py, MyClass { num: 3, debug: true }).unwrap();
{
let obj_ref = obj.borrow(); // Get PyRef
assert_eq!(obj_ref.num, 3);
// You cannot get PyRefMut unless all PyRefs are dropped
assert!(obj.try_borrow_mut().is_err());
}
{
let mut obj_mut = obj.borrow_mut(); // Get PyRefMut
obj_mut.num = 5;
// You cannot get any other refs until the PyRefMut is dropped
assert!(obj.try_borrow().is_err());
assert!(obj.try_borrow_mut().is_err());
}

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

`&PyCell<T>` is bounded by the same lifetime as a [`GILGuard`].
Expand All @@ -118,15 +118,14 @@ struct MyClass {
num: i32,
}
fn return_myclass() -> Py<MyClass> {
let gil = Python::acquire_gil();
let py = gil.python();
Py::new(py, MyClass { num: 1 }).unwrap()
Python::with_gil(|py| Py::new(py, MyClass { num: 1 }).unwrap())
}
let gil = Python::acquire_gil();
let obj = return_myclass();
let cell = obj.as_ref(gil.python()); // Py<MyClass>::as_ref returns &PyCell<MyClass>
let obj_ref = cell.borrow(); // Get PyRef<T>
assert_eq!(obj_ref.num, 1);
Python::with_gil(|py|{
let cell = obj.as_ref(py); // Py<MyClass>::as_ref returns &PyCell<MyClass>
let obj_ref = cell.borrow(); // Get PyRef<T>
assert_eq!(obj_ref.num, 1);
});
```

## Customizing the class
Expand Down Expand Up @@ -261,10 +260,10 @@ impl SubSubClass {
SubClass::method2(super_).map(|x| x * v)
}
}
# let gil = Python::acquire_gil();
# let py = gil.python();
# let subsub = pyo3::PyCell::new(py, SubSubClass::new()).unwrap();
# pyo3::py_run!(py, subsub, "assert subsub.method3() == 3000")
# Python::with_gil(|py| {
# let subsub = pyo3::PyCell::new(py, SubSubClass::new()).unwrap();
# pyo3::py_run!(py, subsub, "assert subsub.method3() == 3000")
# });
```

You can also inherit native types such as `PyDict`, if they implement
Expand All @@ -274,8 +273,7 @@ However, because of some technical problems, we don't currently provide safe upc
that inherit native types. Even in such cases, you can unsafely get a base class by raw pointer conversion.

```rust
# #[cfg(Py_LIMITED_API)] fn main() {}
# #[cfg(not(Py_LIMITED_API))] fn main() {
# #[cfg(not(Py_LIMITED_API))] {
# use pyo3::prelude::*;
use pyo3::types::PyDict;
use pyo3::{AsPyPointer, PyNativeType};
Expand All @@ -300,10 +298,10 @@ impl DictWithCounter {
dict.set_item(key, value)
}
}
# let gil = Python::acquire_gil();
# let py = gil.python();
# let cnt = pyo3::PyCell::new(py, DictWithCounter::new()).unwrap();
# pyo3::py_run!(py, cnt, "cnt.set('abc', 10); assert cnt['abc'] == 10")
# 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")
# });
# }
```

Expand Down Expand Up @@ -563,10 +561,10 @@ impl MyClass {
}
}

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

Note that unlike class variables defined in Python code, class attributes defined in Rust cannot
Expand Down Expand Up @@ -712,8 +710,7 @@ This simple technique works for the case when there is zero or one implementatio
The `#[pyclass]` macro expands to roughly the code seen below. The `PyClassImplCollector` is the type used internally by PyO3 for dtolnay specialization:

```rust
# #[cfg(not(feature = "multiple-pymethods"))]
# {
# #[cfg(not(feature = "multiple-pymethods"))] {
# use pyo3::prelude::*;
// Note: the implementation differs slightly with the `multiple-pymethods` feature enabled.

Expand Down Expand Up @@ -808,10 +805,10 @@ impl pyo3::class::impl_::PyClassImpl for MyClass {
collector.buffer_procs()
}
}
# let gil = Python::acquire_gil();
# let py = gil.python();
# let cls = py.get_type::<MyClass>();
# pyo3::py_run!(py, cls, "assert cls.__name__ == 'MyClass'")
# Python::with_gil(|py| {
# let cls = py.get_type::<MyClass>();
# pyo3::py_run!(py, cls, "assert cls.__name__ == 'MyClass'")
# });
# }
```

Expand Down
17 changes: 6 additions & 11 deletions guide/src/class/protocols.md
Original file line number Diff line number Diff line change
Expand Up @@ -275,17 +275,12 @@ impl PyIterProtocol for Container {
}
}

# let gil = Python::acquire_gil();
# let py = gil.python();
# let inst = pyo3::PyCell::new(
# py,
# Container {
# iter: vec![1, 2, 3, 4],
# },
# )
# .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]");
# 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]");
# });
```

For more details on Python's iteration protocols, check out [the "Iterator Types" section of the library
Expand Down
27 changes: 10 additions & 17 deletions guide/src/exception.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,11 @@ use pyo3::exceptions::PyException;

create_exception!(mymodule, CustomError, PyException);

fn main() {
let gil = Python::acquire_gil();
let py = gil.python();
Python::with_gil(|py| {
let ctx = [("CustomError", py.get_type::<CustomError>())].into_py_dict(py);

py.run("assert str(CustomError) == \"<class 'mymodule.CustomError'>\"", None, Some(&ctx)).unwrap();
py.run("assert CustomError('oops').args == ('oops',)", None, Some(&ctx)).unwrap();
}
pyo3::py_run!(py, *ctx, "assert str(CustomError) == \"<class 'mymodule.CustomError'>\"");
pyo3::py_run!(py, *ctx, "assert CustomError('oops').args == ('oops',)");
});
```

When using PyO3 to create an extension module, you can add the new exception to
Expand Down Expand Up @@ -58,13 +55,11 @@ To raise an exception, first you need to obtain an exception type and construct
use pyo3::{Python, PyErr};
use pyo3::exceptions::PyTypeError;

fn main() {
let gil = Python::acquire_gil();
let py = gil.python();
Python::with_gil(|py| {
PyTypeError::new_err("Error").restore(py);
assert!(PyErr::occurred(py));
drop(PyErr::fetch(py));
}
});
```

From `pyfunction`s and `pyclass` methods, returning an `Err(PyErr)` is enough;
Expand Down Expand Up @@ -103,14 +98,12 @@ In PyO3 every native type has access to the [`PyAny::is_instance`] method which
use pyo3::Python;
use pyo3::types::{PyBool, PyList};

fn main() {
let gil = Python::acquire_gil();
let py = gil.python();
Python::with_gil(|py| {
assert!(PyBool::new(py, true).is_instance::<PyBool>().unwrap());
let list = PyList::new(py, &[1, 2, 3, 4]);
assert!(!list.is_instance::<PyBool>().unwrap());
assert!(list.is_instance::<PyList>().unwrap());
}
});
```
[`PyAny::is_instance`] calls the underlying [`PyType::is_instance`](https://docs.rs/pyo3/latest/pyo3/types/struct.PyType.html#method.is_instance)
method to do the actual work.
Expand All @@ -120,10 +113,10 @@ To check the type of an exception, you can similarly do:
```rust
# use pyo3::exceptions::PyTypeError;
# use pyo3::prelude::*;
# let gil = Python::acquire_gil();
# let py = gil.python();
# Python::with_gil(|py| {
# let err = PyTypeError::new_err(());
err.is_instance::<PyTypeError>(py);
# });
```

## Handling Rust errors
Expand Down
56 changes: 26 additions & 30 deletions guide/src/migration.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,22 +266,16 @@ To migrate, just pass a `py` argument to any calls to these methods.

Before:
```rust,compile_fail
use pyo3::prelude::*;

let gil = Python::acquire_gil();
let py = gil.python();

# pyo3::Python::with_gil(|py| {
py.None().get_refcnt();
# })
```

After:
```rust
use pyo3::prelude::*;

let gil = Python::acquire_gil();
let py = gil.python();

# pyo3::Python::with_gil(|py| {
py.None().get_refcnt(py);
# })
```

## from 0.9.* to 0.10
Expand All @@ -296,18 +290,20 @@ Before:
```rust,compile_fail
use pyo3::ObjectProtocol;
let gil = pyo3::Python::acquire_gil();
let obj = gil.python().eval("lambda: 'Hi :)'", None, None).unwrap();
# pyo3::Python::with_gil(|py| {
let obj = py.eval("lambda: 'Hi :)'", None, None).unwrap();
let hi: &pyo3::types::PyString = obj.call0().unwrap().downcast().unwrap();
assert_eq!(hi.len().unwrap(), 5);
# })
```

After:
```rust
let gil = pyo3::Python::acquire_gil();
let obj = gil.python().eval("lambda: 'Hi :)'", None, None).unwrap();
# pyo3::Python::with_gil(|py| {
let obj = py.eval("lambda: 'Hi :)'", None, None).unwrap();
let hi: &pyo3::types::PyString = obj.call0().unwrap().downcast().unwrap();
assert_eq!(hi.len().unwrap(), 5);
# })
```

### No `#![feature(specialization)]` in user code
Expand Down Expand Up @@ -380,16 +376,16 @@ impl Names {
self.names.append(&mut other.names)
}
}
# let gil = Python::acquire_gil();
# let py = gil.python();
# let names = PyCell::new(py, Names::new()).unwrap();
# pyo3::py_run!(py, names, r"
# try:
# names.merge(names)
# assert False, 'Unreachable'
# except RuntimeError as e:
# assert str(e) == 'Already borrowed'
# ");
# Python::with_gil(|py| {
# let names = PyCell::new(py, Names::new()).unwrap();
# pyo3::py_run!(py, names, r"
# try:
# names.merge(names)
# assert False, 'Unreachable'
# except RuntimeError as e:
# assert str(e) == 'Already borrowed'
# ");
# })
```
`Names` has a `merge` method, which takes `&mut self` and another argument of type `&mut Self`.
Given this `#[pyclass]`, calling `names.merge(names)` in Python raises
Expand All @@ -410,20 +406,20 @@ Before:
# use pyo3::prelude::*;
# #[pyclass]
# struct MyClass {}
let gil = Python::acquire_gil();
let py = gil.python();
# Python::with_gil(|py| {
let obj_ref = PyRef::new(py, MyClass {}).unwrap();
# })
```

After:
```rust
# use pyo3::prelude::*;
# #[pyclass]
# struct MyClass {}
let gil = Python::acquire_gil();
let py = gil.python();
# Python::with_gil(|py| {
let obj = PyCell::new(py, MyClass {}).unwrap();
let obj_ref = obj.borrow();
# })
```

#### Object extraction
Expand All @@ -445,8 +441,7 @@ After:
# use pyo3::types::IntoPyDict;
# #[pyclass] #[derive(Clone)] struct MyClass {}
# #[pymethods] impl MyClass { #[new]fn new() -> Self { MyClass {} }}
# let gil = Python::acquire_gil();
# let py = gil.python();
# 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();
Expand All @@ -458,6 +453,7 @@ let obj_cloned: MyClass = obj.extract().unwrap(); // extracted by cloning the ob
// we need to drop obj_ref before we can extract a PyRefMut due to Rust's rules of references
}
let obj_ref_mut: PyRefMut<MyClass> = obj.extract().unwrap();
# })
```


Expand Down
Loading

0 comments on commit f909e66

Please sign in to comment.