1
1
use crate :: err:: { self , PyDowncastError , PyErr , PyResult } ;
2
+ use crate :: ffi_ptr_ext:: FfiPtrExt ;
2
3
use crate :: pycell:: { PyBorrowError , PyBorrowMutError , PyCell } ;
3
4
use crate :: pyclass:: boolean_struct:: { False , True } ;
4
5
use crate :: type_object:: HasPyGilRef ;
@@ -66,6 +67,43 @@ pub unsafe trait PyNativeType: Sized {
66
67
#[ repr( transparent) ]
67
68
pub struct Bound < ' py , T > ( Python < ' py > , ManuallyDrop < Py < T > > ) ;
68
69
70
+ impl < ' py , T > Bound < ' py , T >
71
+ where
72
+ T : PyClass ,
73
+ {
74
+ /// Creates a new instance `Bound<T>` of a `#[pyclass]` on the Python heap.
75
+ ///
76
+ /// # Examples
77
+ ///
78
+ /// ```rust
79
+ /// use pyo3::prelude::*;
80
+ ///
81
+ /// #[pyclass]
82
+ /// struct Foo {/* fields omitted */}
83
+ ///
84
+ /// # fn main() -> PyResult<()> {
85
+ /// Python::with_gil(|py| -> PyResult<Py<Foo>> {
86
+ /// let foo: Bound<'_, Foo> = Bound::new(py, Foo {})?;
87
+ /// Ok(foo.into())
88
+ /// })?;
89
+ /// # Ok(())
90
+ /// # }
91
+ /// ```
92
+ pub fn new (
93
+ py : Python < ' py > ,
94
+ value : impl Into < PyClassInitializer < T > > ,
95
+ ) -> PyResult < Bound < ' py , T > > {
96
+ let initializer = value. into ( ) ;
97
+ let obj = initializer. create_cell ( py) ?;
98
+ let ob = unsafe {
99
+ obj. cast :: < ffi:: PyObject > ( )
100
+ . assume_owned ( py)
101
+ . downcast_into_unchecked ( )
102
+ } ;
103
+ Ok ( ob)
104
+ }
105
+ }
106
+
69
107
impl < ' py > Bound < ' py , PyAny > {
70
108
/// Constructs a new Bound from a pointer. Panics if ptr is null.
71
109
///
@@ -101,6 +139,149 @@ impl<'py> Bound<'py, PyAny> {
101
139
}
102
140
}
103
141
142
+ impl < ' py , T > Bound < ' py , T >
143
+ where
144
+ T : PyClass ,
145
+ {
146
+ /// Immutably borrows the value `T`.
147
+ ///
148
+ /// This borrow lasts while the returned [`PyRef`] exists.
149
+ /// Multiple immutable borrows can be taken out at the same time.
150
+ ///
151
+ /// For frozen classes, the simpler [`get`][Self::get] is available.
152
+ ///
153
+ /// # Examples
154
+ ///
155
+ /// ```rust
156
+ /// # use pyo3::prelude::*;
157
+ /// #
158
+ /// #[pyclass]
159
+ /// struct Foo {
160
+ /// inner: u8,
161
+ /// }
162
+ ///
163
+ /// # fn main() -> PyResult<()> {
164
+ /// Python::with_gil(|py| -> PyResult<()> {
165
+ /// let foo: Bound<'_, Foo> = Bound::new(py, Foo { inner: 73 })?;
166
+ /// let inner: &u8 = &foo.borrow().inner;
167
+ ///
168
+ /// assert_eq!(*inner, 73);
169
+ /// Ok(())
170
+ /// })?;
171
+ /// # Ok(())
172
+ /// # }
173
+ /// ```
174
+ ///
175
+ /// # Panics
176
+ ///
177
+ /// Panics if the value is currently mutably borrowed. For a non-panicking variant, use
178
+ /// [`try_borrow`](#method.try_borrow).
179
+ pub fn borrow ( & ' py self ) -> PyRef < ' py , T > {
180
+ self . get_cell ( ) . borrow ( )
181
+ }
182
+
183
+ /// Mutably borrows the value `T`.
184
+ ///
185
+ /// This borrow lasts while the returned [`PyRefMut`] exists.
186
+ ///
187
+ /// # Examples
188
+ ///
189
+ /// ```
190
+ /// # use pyo3::prelude::*;
191
+ /// #
192
+ /// #[pyclass]
193
+ /// struct Foo {
194
+ /// inner: u8,
195
+ /// }
196
+ ///
197
+ /// # fn main() -> PyResult<()> {
198
+ /// Python::with_gil(|py| -> PyResult<()> {
199
+ /// let foo: Bound<'_, Foo> = Bound::new(py, Foo { inner: 73 })?;
200
+ /// foo.borrow_mut().inner = 35;
201
+ ///
202
+ /// assert_eq!(foo.borrow().inner, 35);
203
+ /// Ok(())
204
+ /// })?;
205
+ /// # Ok(())
206
+ /// # }
207
+ /// ```
208
+ ///
209
+ /// # Panics
210
+ /// Panics if the value is currently borrowed. For a non-panicking variant, use
211
+ /// [`try_borrow_mut`](#method.try_borrow_mut).
212
+ pub fn borrow_mut ( & ' py self ) -> PyRefMut < ' py , T >
213
+ where
214
+ T : PyClass < Frozen = False > ,
215
+ {
216
+ self . get_cell ( ) . borrow_mut ( )
217
+ }
218
+
219
+ /// Attempts to immutably borrow the value `T`, returning an error if the value is currently mutably borrowed.
220
+ ///
221
+ /// The borrow lasts while the returned [`PyRef`] exists.
222
+ ///
223
+ /// This is the non-panicking variant of [`borrow`](#method.borrow).
224
+ ///
225
+ /// For frozen classes, the simpler [`get`][Self::get] is available.
226
+ pub fn try_borrow ( & ' py self ) -> Result < PyRef < ' py , T > , PyBorrowError > {
227
+ self . get_cell ( ) . try_borrow ( )
228
+ }
229
+
230
+ /// Attempts to mutably borrow the value `T`, returning an error if the value is currently borrowed.
231
+ ///
232
+ /// The borrow lasts while the returned [`PyRefMut`] exists.
233
+ ///
234
+ /// This is the non-panicking variant of [`borrow_mut`](#method.borrow_mut).
235
+ pub fn try_borrow_mut ( & ' py self ) -> Result < PyRefMut < ' py , T > , PyBorrowMutError >
236
+ where
237
+ T : PyClass < Frozen = False > ,
238
+ {
239
+ self . get_cell ( ) . try_borrow_mut ( )
240
+ }
241
+
242
+ /// Provide an immutable borrow of the value `T` without acquiring the GIL.
243
+ ///
244
+ /// This is available if the class is [`frozen`][macro@crate::pyclass] and [`Sync`].
245
+ ///
246
+ /// # Examples
247
+ ///
248
+ /// ```
249
+ /// use std::sync::atomic::{AtomicUsize, Ordering};
250
+ /// # use pyo3::prelude::*;
251
+ ///
252
+ /// #[pyclass(frozen)]
253
+ /// struct FrozenCounter {
254
+ /// value: AtomicUsize,
255
+ /// }
256
+ ///
257
+ /// Python::with_gil(|py| {
258
+ /// let counter = FrozenCounter { value: AtomicUsize::new(0) };
259
+ ///
260
+ /// let py_counter = Bound::new(py, counter).unwrap();
261
+ ///
262
+ /// py_counter.get().value.fetch_add(1, Ordering::Relaxed);
263
+ /// });
264
+ /// ```
265
+ pub fn get ( & self ) -> & T
266
+ where
267
+ T : PyClass < Frozen = True > + Sync ,
268
+ {
269
+ let cell = self . get_cell ( ) ;
270
+ // SAFETY: The class itself is frozen and `Sync` and we do not access anything but `cell.contents.value`.
271
+ unsafe { & * cell. get_ptr ( ) }
272
+ }
273
+
274
+ fn get_cell ( & ' py self ) -> & ' py PyCell < T > {
275
+ let cell = self . as_ptr ( ) . cast :: < PyCell < T > > ( ) ;
276
+ // SAFETY: Bound<T> is known to contain an object which is laid out in memory as a
277
+ // PyCell<T>.
278
+ //
279
+ // Strictly speaking for now `&'py PyCell<T>` is part of the "GIL Ref" API, so this
280
+ // could use some further refactoring later to avoid going through this reference.
281
+ unsafe { & * cell }
282
+ }
283
+ }
284
+
104
285
impl < ' py , T > std:: fmt:: Debug for Bound < ' py , T > {
105
286
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> Result < ( ) , std:: fmt:: Error > {
106
287
let any = self . as_any ( ) ;
@@ -1757,12 +1938,11 @@ a = A()
1757
1938
1758
1939
use super :: * ;
1759
1940
1760
- #[ crate :: pyclass]
1761
- #[ pyo3( crate = "crate" ) ]
1941
+ #[ crate :: pyclass( crate = "crate" ) ]
1762
1942
struct SomeClass ( i32 ) ;
1763
1943
1764
1944
#[ test]
1765
- fn instance_borrow_methods ( ) {
1945
+ fn py_borrow_methods ( ) {
1766
1946
// More detailed tests of the underlying semantics in pycell.rs
1767
1947
Python :: with_gil ( |py| {
1768
1948
let instance = Py :: new ( py, SomeClass ( 0 ) ) . unwrap ( ) ;
@@ -1780,6 +1960,40 @@ a = A()
1780
1960
} )
1781
1961
}
1782
1962
1963
+ #[ test]
1964
+ fn bound_borrow_methods ( ) {
1965
+ // More detailed tests of the underlying semantics in pycell.rs
1966
+ Python :: with_gil ( |py| {
1967
+ let instance = Bound :: new ( py, SomeClass ( 0 ) ) . unwrap ( ) ;
1968
+ assert_eq ! ( instance. borrow( ) . 0 , 0 ) ;
1969
+ assert_eq ! ( instance. try_borrow( ) . unwrap( ) . 0 , 0 ) ;
1970
+ assert_eq ! ( instance. borrow_mut( ) . 0 , 0 ) ;
1971
+ assert_eq ! ( instance. try_borrow_mut( ) . unwrap( ) . 0 , 0 ) ;
1972
+
1973
+ instance. borrow_mut ( ) . 0 = 123 ;
1974
+
1975
+ assert_eq ! ( instance. borrow( ) . 0 , 123 ) ;
1976
+ assert_eq ! ( instance. try_borrow( ) . unwrap( ) . 0 , 123 ) ;
1977
+ assert_eq ! ( instance. borrow_mut( ) . 0 , 123 ) ;
1978
+ assert_eq ! ( instance. try_borrow_mut( ) . unwrap( ) . 0 , 123 ) ;
1979
+ } )
1980
+ }
1981
+
1982
+ #[ crate :: pyclass( frozen, crate = "crate" ) ]
1983
+ struct FrozenClass ( i32 ) ;
1984
+
1985
+ #[ test]
1986
+ fn test_frozen_get ( ) {
1987
+ Python :: with_gil ( |py| {
1988
+ for i in 0 ..10 {
1989
+ let instance = Py :: new ( py, FrozenClass ( i) ) . unwrap ( ) ;
1990
+ assert_eq ! ( instance. get( ) . 0 , i) ;
1991
+
1992
+ assert_eq ! ( instance. bind( py) . get( ) . 0 , i) ;
1993
+ }
1994
+ } )
1995
+ }
1996
+
1783
1997
#[ test]
1784
1998
#[ allow( deprecated) ]
1785
1999
fn cell_tryfrom ( ) {
0 commit comments