Skip to content

Commit f9a5f7e

Browse files
committed
Write migration guide for 0.9
1 parent 74b22eb commit f9a5f7e

File tree

4 files changed

+187
-2
lines changed

4 files changed

+187
-2
lines changed

guide/src/SUMMARY.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,5 @@
1212
- [Advanced Topics](advanced.md)
1313
- [Building and Distribution](building_and_distribution.md)
1414
- [PyPy support](pypy.md)
15-
- [Appendix: PyO3 and rust-cpython](rust_cpython.md)
15+
- [Appendix A: PyO3 and rust-cpython](rust_cpython.md)
16+
- [Appendix B: Migration Guide](migration.md)

guide/src/migration.md

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# Appendix B: Migration Guides for major version changes
2+
3+
## from 0.8.* to 0.9
4+
5+
### `#[new]` interface
6+
[`PyRawObject`](https://docs.rs/pyo3/0.8.5/pyo3/type_object/struct.PyRawObject.html)
7+
is now removed and our syntax for constructor changed.
8+
9+
Before:
10+
```compile_fail
11+
#[pyclass]
12+
struct MyClass {}
13+
14+
#[pymethods]
15+
impl MyClass {
16+
#[new]
17+
fn new(obj: &PyRawObject) {
18+
obj.init(MyClass { })
19+
}
20+
}
21+
```
22+
23+
After:
24+
```
25+
# use pyo3::prelude::*;
26+
#[pyclass]
27+
struct MyClass {}
28+
29+
#[pymethods]
30+
impl MyClass {
31+
#[new]
32+
fn new() -> Self {
33+
MyClass {}
34+
}
35+
}
36+
```
37+
38+
Basically you can return `Self` or `Result<Self>` directly.
39+
For more, see [the constructor section](https://pyo3.rs/master/class.html#constructor) of this guide.
40+
41+
### PyCell
42+
PyO3 0.9 introduces [`PyCell`](https://pyo3.rs/master/doc/pyo3/pycell/struct.PyCell.html), which is
43+
a [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) like object wrapper
44+
for dynamically ensuring
45+
[Rust's rule of Reference](https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html#the-rules-of-references).
46+
47+
For `#[pymethods]` or `#[pyfunction]`s, `PyCell` works without any change.
48+
Just throw errors when there happens invalid borrowing.
49+
50+
Here is an example.
51+
```
52+
# use pyo3::prelude::*;
53+
#[pyclass]
54+
struct Names {
55+
names: Vec<String>
56+
}
57+
58+
#[pymethods]
59+
impl Names {
60+
#[new]
61+
fn new() -> Self {
62+
Names { names: vec![] }
63+
}
64+
fn merge(&mut self, other: &mut Names) {
65+
self.names.append(&mut other.names)
66+
}
67+
}
68+
# let gil = Python::acquire_gil();
69+
# let py = gil.python();
70+
# let names = PyCell::new(py, Names::new()).unwrap();
71+
# let borrow_mut_err = py.get_type::<pyo3::pycell::PyBorrowMutError>();
72+
# pyo3::py_run!(py, names borrow_mut_err, r"
73+
# try:
74+
# names.merge(names)
75+
# assert False, 'Unreachable'
76+
# except Exception as e:
77+
# isinstance(e, borrow_mut_err)
78+
# ");
79+
```
80+
`Names` has `merge` method, which takes `&mut self` and `&mut Self`.
81+
Given this `#[pyclass]`, calling `names.merge(names)` in Python raises `PyBorrowMutError` exception,
82+
since it requires two mutable borrows of `names`,
83+
84+
However, for `#[pyproto]` and some functions, you need to manually fix codes.
85+
86+
#### Object creation
87+
We could use the older `PyRef` and `PyRefMut` for object creation, but now they are just
88+
reference wrappers for `PyCell`.
89+
Use `PyCell::new` instead.
90+
91+
Before:
92+
```compile_fail
93+
# use pyo3::prelude::*;
94+
# #[pyclass]
95+
# struct MyClass {}
96+
let gil = Python::acquire_gil();
97+
let py = gil.python();
98+
let obj_ref = PyRef::new(py, MyClass {}).unwrap();
99+
```
100+
101+
After:
102+
```
103+
# use pyo3::prelude::*;
104+
# #[pyclass]
105+
# struct MyClass {}
106+
let gil = Python::acquire_gil();
107+
let py = gil.python();
108+
let obj = PyCell::new(py, MyClass {}).unwrap();
109+
let obj_ref = obj.borrow();
110+
```
111+
112+
#### Object extraction
113+
Now for `T: PyClass`, `&T` and `&mut T` don't have `FromPyObject` implementation.
114+
Instead, you can use `&PyCell`, `PyRef`, and `PyRefMut` for object extraction.
115+
116+
Before:
117+
```ignore
118+
let obj: &PyAny = create_obj();
119+
let obj_ref: &MyClass = obj.extract().unwrap();
120+
let obj_ref_mut: &mut MyClass = obj.extract().unwrap();
121+
```
122+
123+
After:
124+
```
125+
# use pyo3::prelude::*;
126+
# use pyo3::types::{PyAny, IntoPyDict};
127+
# #[pyclass] struct MyClass {}
128+
# #[pymethods] impl MyClass { #[new]fn new() -> Self { MyClass {} }}
129+
# let gil = Python::acquire_gil();
130+
# let py = gil.python();
131+
# let typeobj = py.get_type::<MyClass>();
132+
# let d = [("c", typeobj)].into_py_dict(py);
133+
# let create_obj = || py.eval("c()", None, Some(d)).unwrap();
134+
let obj: &PyAny = create_obj();
135+
let obj_cell: &PyCell<MyClass> = obj.extract().unwrap();
136+
{
137+
let obj_ref: PyRef<MyClass> = obj.extract().unwrap();
138+
// we need to drop obj_ref before taking RefMut
139+
}
140+
let obj_ref_mut: PyRefMut<MyClass> = obj.extract().unwrap();
141+
```
142+
143+
144+
#### `#[pyproto]`
145+
Most of `#[pyproto]` arguments requires [`FromPyObject`] implementation.
146+
So if your protocol methods take `&T` or `&mut T`(where `T: PyClass`),
147+
please use `PyRef` or `PyRefMut` instead.
148+
149+
Before:
150+
```compile_fail
151+
# use pyo3::prelude::*;
152+
# use pyo3::class::PySequenceProtocol;
153+
#[pyclass]
154+
struct ByteSequence {
155+
elements: Vec<u8>,
156+
}
157+
#[pyproto]
158+
impl PySequenceProtocol for ByteSequence {
159+
fn __concat__(&self, other: &Self) -> PyResult<Self> {
160+
let mut elements = self.elements.clone();
161+
elements.extend_from_slice(&other.elements);
162+
Ok(Self { elements })
163+
}
164+
}
165+
```
166+
167+
After:
168+
```
169+
# use pyo3::prelude::*;
170+
# use pyo3::class::PySequenceProtocol;
171+
#[pyclass]
172+
struct ByteSequence {
173+
elements: Vec<u8>,
174+
}
175+
#[pyproto]
176+
impl PySequenceProtocol for ByteSequence {
177+
fn __concat__(&self, other: PyRef<'p, Self>) -> PyResult<Self> {
178+
let mut elements = self.elements.clone();
179+
elements.extend_from_slice(&other.elements);
180+
Ok(Self { elements })
181+
}
182+
}
183+
```

guide/src/rust_cpython.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Appendix: PyO3 and rust-cpython
1+
# Appendix A: PyO3 and rust-cpython
22

33
PyO3 began as fork of [rust-cpython](https://github.com/dgrunwald/rust-cpython) when rust-cpython wasn't maintained. Over the time PyO3 has become fundamentally different from rust-cpython.
44

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,4 +328,5 @@ pub mod doc_test {
328328
doctest!("../guide/src/parallelism.md", guide_parallelism_md);
329329
doctest!("../guide/src/pypy.md", guide_pypy_md);
330330
doctest!("../guide/src/rust_cpython.md", guide_rust_cpython_md);
331+
doctest!("../guide/src/migration.md", guide_migration_md);
331332
}

0 commit comments

Comments
 (0)