Skip to content

Commit 00ee7c6

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

File tree

4 files changed

+185
-2
lines changed

4 files changed

+185
-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: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
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 raise `PyBorrowMutError` exception,
82+
since it requires two mutable borrows of `names`,
83+
84+
However, for `#[pyproto]` and some functions, you need to rewrite the code manually.
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 = 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_ref: &MyClass = obj.extract().unwrap();
119+
let obj_ref_mut: &mut MyClass = obj.extract().unwrap();
120+
```
121+
122+
After:
123+
```
124+
# use pyo3::prelude::*;
125+
# use pyo3::types::IntoPyDict;
126+
# #[pyclass] struct MyClass {}
127+
# #[pymethods] impl MyClass { #[new]fn new() -> Self { MyClass {} }}
128+
# let gil = Python::acquire_gil();
129+
# let py = gil.python();
130+
# let typeobj = py.get_type::<MyClass>();
131+
# let d = [("c", typeobj)].into_py_dict(py);
132+
# let obj = py.eval("c()", None, Some(d)).unwrap();
133+
let obj_cell: &PyCell<MyClass> = obj.extract().unwrap();
134+
{
135+
let obj_ref: PyRef<MyClass> = obj.extract().unwrap();
136+
// we need to drop obj_ref before taking RefMut
137+
}
138+
let obj_ref_mut: PyRefMut<MyClass> = obj.extract().unwrap();
139+
```
140+
141+
142+
#### `#[pyproto]`
143+
Most of `#[pyproto]` arguments requires [`FromPyObject`] implementation.
144+
So if your protocol methods take `&T` or `&mut T`(where `T: PyClass`),
145+
please use `PyRef` or `PyRefMut` instead.
146+
147+
Before:
148+
```compile_fail
149+
# use pyo3::prelude::*;
150+
# use pyo3::class::PySequenceProtocol;
151+
#[pyclass]
152+
struct ByteSequence {
153+
elements: Vec<u8>,
154+
}
155+
#[pyproto]
156+
impl PySequenceProtocol for ByteSequence {
157+
fn __concat__(&self, other: &Self) -> PyResult<Self> {
158+
let mut elements = self.elements.clone();
159+
elements.extend_from_slice(&other.elements);
160+
Ok(Self { elements })
161+
}
162+
}
163+
```
164+
165+
After:
166+
```
167+
# use pyo3::prelude::*;
168+
# use pyo3::class::PySequenceProtocol;
169+
#[pyclass]
170+
struct ByteSequence {
171+
elements: Vec<u8>,
172+
}
173+
#[pyproto]
174+
impl PySequenceProtocol for ByteSequence {
175+
fn __concat__(&self, other: PyRef<'p, Self>) -> PyResult<Self> {
176+
let mut elements = self.elements.clone();
177+
elements.extend_from_slice(&other.elements);
178+
Ok(Self { elements })
179+
}
180+
}
181+
```

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)