@@ -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 :: PyClassShell <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,56 @@ 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 ` ] ( https://pyo3.rs/master/doc/pyo3/pycell/struct.PyCell.html ) 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 ` PyClassShell<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 only get ` &PyCell<T> ` , not ` PyCell<T> ` .
114
118
115
- ### ` PyClassShell `
116
- ` PyClassShell ` represents the actual layout of ` pyclass ` on the Python heap.
119
+ Thus, to mutate data behind ` &PyCell ` safely, we employ the
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 ` PyClassShell::new_ref ` or ` PyClassShell::new_mut ` .
123
+ Users who are familiar with ` RefCell ` can use ` PyCell ` just like ` RefCell ` .
124
+
125
+ For users who are not very familiar with ` RefCell ` , here is a reminder of Rust's rules of borrowing:
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 tracking references at runtime.
120
129
121
130
``` rust
122
131
# use pyo3 :: prelude :: * ;
123
132
# use pyo3 :: types :: PyDict ;
124
- # use pyo3 :: PyClassShell ;
125
133
#[pyclass]
126
134
struct MyClass {
135
+ #[pyo3(get)]
127
136
num : i32 ,
128
137
debug : bool ,
129
138
}
130
139
let gil = Python :: acquire_gil ();
131
140
let py = gil . python ();
132
- let obj = PyClassShell :: 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 `&PyClassShell` as a normal Python object
137
- dict . set_item (" obj" , obj ). unwrap ();
138
-
139
- // return &mut PyClassShell<MyClass>
140
- let obj = PyClassShell :: new_mut (py , MyClass { num : 3 , debug : true }). unwrap ();
141
- obj . num = 5 ;
141
+ let obj = PyCell :: new (py , MyClass { num : 3 , debug : true }). unwrap ();
142
+ {
143
+ let obj_ref = obj . borrow (); // Get PyRef
144
+ assert_eq! (obj_ref . num, 3 );
145
+ // You cannot get PyRefMut unless all PyRefs are dropped
146
+ assert! (obj . try_borrow_mut (). is_err ());
147
+ }
148
+ {
149
+ let mut obj_mut = obj . borrow_mut (); // Get PyRefMut
150
+ obj_mut . num = 5 ;
151
+ // You cannot get any other refs until the PyRefMut is dropped
152
+ assert! (obj . try_borrow (). is_err ());
153
+ assert! (obj . try_borrow_mut (). is_err ());
154
+ }
155
+ // You can convert `&PyCell` to Python object
156
+ pyo3 :: py_run! (py , obj , " assert obj.num == 5" )
142
157
```
143
158
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.
159
+ ` &PyCell<T> ` is bounded by the same lifetime as ` GILGuard ` .
160
+ To avoid this you can use ` Py<T> ` , which stores an object longer than the GIL lifetime.
149
161
``` rust
150
162
# use pyo3 :: prelude :: * ;
151
163
#[pyclass]
@@ -159,7 +171,9 @@ fn return_myclass() -> Py<MyClass> {
159
171
}
160
172
let gil = Python :: acquire_gil ();
161
173
let obj = return_myclass ();
162
- assert_eq! (obj . as_ref (gil . python ()). num, 1 );
174
+ let cell = obj . as_ref (gil . python ()); // AsPyRef::as_ref returns &PyCell
175
+ let obj_ref = cell . borrow (); // Get PyRef<T>
176
+ assert_eq! (obj_ref . num, 1 );
163
177
```
164
178
165
179
## Customizing the class
@@ -228,9 +242,14 @@ baseclass of `T`.
228
242
But for more deeply nested inheritance, you have to return ` PyClassInitializer<T> `
229
243
explicitly.
230
244
245
+ To get a parent class from a child, use ` PyRef<T> ` instead of ` &self ` ,
246
+ or ` PyRefMut<T> ` instead of ` &mut self ` .
247
+ Then you can access a parent class by ` self_.as_ref() ` as ` &Self::BaseClass ` ,
248
+ or by ` self_.into_super() ` as ` PyRef<Self::BaseClass> ` .
249
+
231
250
``` rust
232
251
# use pyo3 :: prelude :: * ;
233
- use pyo3 :: PyClassShell ;
252
+ use pyo3 :: PyCell ;
234
253
235
254
#[pyclass]
236
255
struct BaseClass {
@@ -261,8 +280,9 @@ impl SubClass {
261
280
(SubClass { val2 : 15 }, BaseClass :: new ())
262
281
}
263
282
264
- fn method2 (self_ : & PyClassShell <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_ref (); // Get &BaseClass
285
+ super_ . method (). map (| x | x * self_ . val2)
266
286
}
267
287
}
268
288
@@ -279,29 +299,24 @@ impl SubSubClass {
279
299
. add_subclass (SubSubClass {val3 : 20 })
280
300
}
281
301
282
- fn method3 (self_ : & PyClassShell <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 :: PyClassShell :: 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
295
- To access the super class, you can use either of these two ways:
296
- - Use ` self_: &PyClassShell<Self> ` instead of ` self ` , and call ` get_super() `
297
- - ` ObjectProtocol::get_base `
298
- We recommend ` PyClassShell ` here, since it makes the context much clearer.
299
-
300
-
301
316
If ` SubClass ` does not provide a baseclass initialization, the compilation fails.
302
317
``` compile_fail
303
318
# use pyo3::prelude::*;
304
- use pyo3::PyClassShell ;
319
+ use pyo3::PyCell ;
305
320
306
321
#[pyclass]
307
322
struct BaseClass {
@@ -761,16 +776,16 @@ struct GCTracked {} // Fails because it does not implement PyGCProtocol
761
776
Iterators can be defined using the
762
777
[ ` PyIterProtocol ` ] ( https://docs.rs/pyo3/latest/pyo3/class/iter/trait.PyIterProtocol.html ) trait.
763
778
It includes two methods ` __iter__ ` and ` __next__ ` :
764
- * ` fn __iter__(slf: &mut PyClassShell <Self>) -> PyResult<impl IntoPy<PyObject>> `
765
- * ` fn __next__(slf: &mut PyClassShell <Self>) -> PyResult<Option<impl IntoPy<PyObject>>> `
779
+ * ` fn __iter__(slf: PyRefMut <Self>) -> PyResult<impl IntoPy<PyObject>> `
780
+ * ` fn __next__(slf: PyRefMut <Self>) -> PyResult<Option<impl IntoPy<PyObject>>> `
766
781
767
782
Returning ` Ok(None) ` from ` __next__ ` indicates that that there are no further items.
768
783
769
784
Example:
770
785
771
786
``` rust
772
787
use pyo3 :: prelude :: * ;
773
- use pyo3 :: {PyIterProtocol , PyClassShell };
788
+ use pyo3 :: {PyIterProtocol , PyCell };
774
789
775
790
#[pyclass]
776
791
struct MyIterator {
@@ -779,19 +794,15 @@ struct MyIterator {
779
794
780
795
#[pyproto]
781
796
impl PyIterProtocol for MyIterator {
782
- fn __iter__ (slf : & mut PyClassShell <Self >) -> PyResult <Py <MyIterator >> {
797
+ fn __iter__ (mut slf : PyRefMut <Self >) -> PyResult <Py <MyIterator >> {
783
798
Ok (slf . into ())
784
799
}
785
- fn __next__ (slf : & mut PyClassShell <Self >) -> PyResult <Option <PyObject >> {
800
+ fn __next__ (mut slf : PyRefMut <Self >) -> PyResult <Option <PyObject >> {
786
801
Ok (slf . iter. next ())
787
802
}
788
803
}
789
804
```
790
805
791
- ## Manually implementing pyclass
792
-
793
- TODO: Which traits to implement (basically ` PyTypeCreate: PyObjectAlloc + PyTypeInfo + PyMethodsProtocol + Sized ` ) and what they mean.
794
-
795
806
## How methods are implemented
796
807
797
808
Users should be able to define a ` #[pyclass] ` with or without ` #[pymethods] ` , while PyO3 needs a
0 commit comments