@@ -22,6 +22,7 @@ Specifically, the following implementation is generated:
22
22
23
23
``` rust
24
24
use pyo3 :: prelude :: * ;
25
+ use pyo3 :: types :: PyAny ;
25
26
26
27
/// Class for demonstration
27
28
struct MyClass {
@@ -33,9 +34,10 @@ impl pyo3::pyclass::PyClassAlloc for MyClass {}
33
34
34
35
unsafe impl pyo3 :: PyTypeInfo for MyClass {
35
36
type Type = MyClass ;
36
- type BaseType = pyo3 :: types :: PyAny ;
37
- type ConcreteLayout = pyo3 :: PyCell <Self >;
38
- type Initializer = pyo3 :: PyClassInitializer <Self >;
37
+ type BaseType = PyAny ;
38
+ type BaseLayout = pyo3 :: pycell :: PyCellBase <PyAny >;
39
+ type Layout = PyCell <Self >;
40
+ type Initializer = PyClassInitializer <Self >;
39
41
40
42
const NAME : & 'static str = " MyClass" ;
41
43
const MODULE : Option <& 'static str > = None ;
@@ -53,6 +55,7 @@ unsafe impl pyo3::PyTypeInfo for MyClass {
53
55
impl pyo3 :: pyclass :: PyClass for MyClass {
54
56
type Dict = pyo3 :: pyclass_slots :: PyClassDummySlot ;
55
57
type WeakRef = pyo3 :: pyclass_slots :: PyClassDummySlot ;
58
+ type BaseNativeType = PyAny ;
56
59
}
57
60
58
61
impl pyo3 :: IntoPy <PyObject > for MyClass {
@@ -105,47 +108,58 @@ fn mymodule(_py: Python, m: &PyModule) -> PyResult<()> {
105
108
}
106
109
```
107
110
108
- ## Get Python objects from ` pyclass `
109
- You sometimes need to convert your ` pyclass ` into a Python object in Rust code (e.g., for testing it).
111
+ ## PyCell and interior mutability
112
+ You sometimes need to convert your ` pyclass ` into a Python object and access it
113
+ from Rust code (e.g., for testing it).
114
+ ` PyCell ` is our primary interface for that.
110
115
111
- For getting * GIL-bounded* (i.e., with ` 'py ` lifetime) references of ` pyclass ` ,
112
- you can use ` PyCell<T> ` .
113
- Or you can use ` Py<T> ` directly, for * not-GIL-bounded* references.
116
+ ` PyCell<T: PyClass> ` is always allocated in the Python heap, so we don't have the ownership of it.
117
+ We can get ` &PyCell<T> ` , not ` PyCell<T> ` .
114
118
115
- ### ` PyCell `
116
- ` PyCell ` represents the actual layout of ` pyclass ` on the Python heap.
119
+ Thus, to mutate data behind ` &PyCell ` safely, we employs
120
+ [ Interior Mutability Pattern] ( https://doc.rust-lang.org/book/ch15-05-interior-mutability.html )
121
+ like [ std::cell::RefCell] ( https://doc.rust-lang.org/std/cell/struct.RefCell.html ) .
117
122
118
- If you want to instantiate ` pyclass ` in Python and get the reference,
119
- you can use ` PyCell::new_ref ` or ` PyCell::new_mut ` .
123
+ Users who are familiar with ` RefCell ` can use ` PyCell ` just like ` RefCell ` .
124
+
125
+ For users who doesn't know ` RefCell ` well, we repeat the Rust's borrowing rule here:
126
+ - At any given time, you can have either (but not both of) one mutable reference or any number of immutable references.
127
+ - References must always be valid.
128
+ ` PyCell ` ensures these borrowing rules by managing a reference counter.
129
+
130
+ TODO: link to the API document
120
131
121
132
``` rust
122
133
# use pyo3 :: prelude :: * ;
123
134
# use pyo3 :: types :: PyDict ;
124
135
# use pyo3 :: PyCell ;
125
136
#[pyclass]
126
137
struct MyClass {
138
+ #[pyo3(get)]
127
139
num : i32 ,
128
140
debug : bool ,
129
141
}
130
142
let gil = Python :: acquire_gil ();
131
143
let py = gil . python ();
132
- let obj = PyCell :: new_ref (py , MyClass { num : 3 , debug : true }). unwrap ();
133
- // You can use deref
134
- assert_eq! (obj . num, 3 );
135
- let dict = PyDict :: new (py );
136
- // You can treat a `&PyCell` as a normal Python object
137
- dict . set_item (" obj" , obj ). unwrap ();
138
-
139
- // return &mut PyCell<MyClass>
140
- let obj = PyCell :: new_mut (py , MyClass { num : 3 , debug : true }). unwrap ();
141
- obj . num = 5 ;
144
+ let obj = PyCell :: new (py , MyClass { num : 3 , debug : true }). unwrap ();
145
+ {
146
+ let obj_ref = obj . borrow (); // Get PyRef
147
+ assert_eq! (obj_ref . num, 3 );
148
+ // You cannot get PyRefMut unless all PyRef drop
149
+ assert! (obj . try_borrow_mut (). is_err ());
150
+ }
151
+ {
152
+ let mut obj_mut = obj . borrow_mut (); // Get PyRefMut
153
+ obj_mut . num = 5 ;
154
+ // You cannot get PyRef unless all PyRefMut drop
155
+ assert! (obj . try_borrow (). is_err ());
156
+ }
157
+ // You can convert `&PyCell` to Python object
158
+ pyo3 :: py_run! (py , obj , " assert obj.num == 5" )
142
159
```
143
160
144
- ### ` Py `
145
-
146
- ` Py ` is an object wrapper which stores an object longer than the GIL lifetime.
147
-
148
- You can use it to avoid lifetime problems.
161
+ ` &PyCell<T> ` is bouded by the same lifetime as ` GILGuard ` .
162
+ To avoid this you can use ` Py<T> ` , which stores an object longer than the GIL lifetime.
149
163
``` rust
150
164
# use pyo3 :: prelude :: * ;
151
165
#[pyclass]
@@ -159,7 +173,9 @@ fn return_myclass() -> Py<MyClass> {
159
173
}
160
174
let gil = Python :: acquire_gil ();
161
175
let obj = return_myclass ();
162
- assert_eq! (obj . as_ref (gil . python ()). num, 1 );
176
+ let cell = obj . as_ref (gil . python ()); // AsPyRef::as_ref returns &PyCell
177
+ let obj_ref = cell . borrow (); // Get PyRef<T>
178
+ assert_eq! (obj_ref . num, 1 );
163
179
```
164
180
165
181
## Customizing the class
@@ -228,6 +244,9 @@ baseclass of `T`.
228
244
But for more deeply nested inheritance, you have to return ` PyClassInitializer<T> `
229
245
explicitly.
230
246
247
+ To get a parent class from child, use ` PyRef<T> ` instead of ` &self ` ,
248
+ or ` PyRefMut<T> ` instead of ` &mut self ` .
249
+
231
250
``` rust
232
251
# use pyo3 :: prelude :: * ;
233
252
use pyo3 :: PyCell ;
@@ -261,8 +280,9 @@ impl SubClass {
261
280
(SubClass { val2 : 15 }, BaseClass :: new ())
262
281
}
263
282
264
- fn method2 (self_ : & PyCell <Self >) -> PyResult <usize > {
265
- self_ . get_super (). method (). map (| x | x * self_ . val2)
283
+ fn method2 (self_ : PyRef <Self >) -> PyResult <usize > {
284
+ let super_ = self_ . as_super (); // Get &BaseClass
285
+ super_ . method (). map (| x | x * self_ . val2)
266
286
}
267
287
}
268
288
@@ -279,16 +299,17 @@ impl SubSubClass {
279
299
. add_subclass (SubSubClass {val3 : 20 })
280
300
}
281
301
282
- fn method3 (self_ : & PyCell <Self >) -> PyResult <usize > {
283
- let super_ = self_ . get_super ();
284
- SubClass :: method2 (super_ ). map (| x | x * self_ . val3)
302
+ fn method3 (self_ : PyRef <Self >) -> PyResult <usize > {
303
+ let v = self_ . val3;
304
+ let super_ = self_ . into_super (); // Get PyRef<SubClass>
305
+ SubClass :: method2 (super_ ). map (| x | x * v )
285
306
}
286
307
}
287
308
288
309
289
310
# let gil = Python :: acquire_gil ();
290
311
# let py = gil . python ();
291
- # let subsub = pyo3 :: PyCell :: new_ref (py , SubSubClass :: new ()). unwrap ();
312
+ # let subsub = pyo3 :: PyCell :: new (py , SubSubClass :: new ()). unwrap ();
292
313
# pyo3 :: py_run! (py , subsub , " assert subsub.method3() == 3000" )
293
314
```
294
315
@@ -761,8 +782,8 @@ struct GCTracked {} // Fails because it does not implement PyGCProtocol
761
782
Iterators can be defined using the
762
783
[ ` PyIterProtocol ` ] ( https://docs.rs/pyo3/latest/pyo3/class/iter/trait.PyIterProtocol.html ) trait.
763
784
It includes two methods ` __iter__ ` and ` __next__ ` :
764
- * ` fn __iter__(slf: &mut PyCell <Self>) -> PyResult<impl IntoPy<PyObject>> `
765
- * ` fn __next__(slf: &mut PyCell <Self>) -> PyResult<Option<impl IntoPy<PyObject>>> `
785
+ * ` fn __iter__(slf: PyRefMut <Self>) -> PyResult<impl IntoPy<PyObject>> `
786
+ * ` fn __next__(slf: PyRefMut <Self>) -> PyResult<Option<impl IntoPy<PyObject>>> `
766
787
767
788
Returning ` Ok(None) ` from ` __next__ ` indicates that that there are no further items.
768
789
@@ -779,10 +800,10 @@ struct MyIterator {
779
800
780
801
#[pyproto]
781
802
impl PyIterProtocol for MyIterator {
782
- fn __iter__ (slf : & mut PyCell <Self >) -> PyResult <Py <MyIterator >> {
803
+ fn __iter__ (mut slf : PyRefMut <Self >) -> PyResult <Py <MyIterator >> {
783
804
Ok (slf . into ())
784
805
}
785
- fn __next__ (slf : & mut PyCell <Self >) -> PyResult <Option <PyObject >> {
806
+ fn __next__ (mut slf : PyRefMut <Self >) -> PyResult <Option <PyObject >> {
786
807
Ok (slf . iter. next ())
787
808
}
788
809
}
0 commit comments