11use crate :: err:: { self , PyDowncastError , PyErr , PyResult } ;
2+ use crate :: ffi_ptr_ext:: FfiPtrExt ;
23use crate :: pycell:: { PyBorrowError , PyBorrowMutError , PyCell } ;
34use crate :: pyclass:: boolean_struct:: { False , True } ;
45use crate :: type_object:: HasPyGilRef ;
@@ -66,6 +67,43 @@ pub unsafe trait PyNativeType: Sized {
6667#[ repr( transparent) ]
6768pub struct Bound < ' py , T > ( Python < ' py > , ManuallyDrop < Py < T > > ) ;
6869
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+
69107impl < ' py > Bound < ' py , PyAny > {
70108 /// Constructs a new Bound from a pointer. Panics if ptr is null.
71109 ///
@@ -101,6 +139,149 @@ impl<'py> Bound<'py, PyAny> {
101139 }
102140}
103141
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+
104285impl < ' py , T > std:: fmt:: Debug for Bound < ' py , T > {
105286 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> Result < ( ) , std:: fmt:: Error > {
106287 let any = self . as_any ( ) ;
@@ -1768,12 +1949,11 @@ a = A()
17681949
17691950 use super :: * ;
17701951
1771- #[ crate :: pyclass]
1772- #[ pyo3( crate = "crate" ) ]
1952+ #[ crate :: pyclass( crate = "crate" ) ]
17731953 struct SomeClass ( i32 ) ;
17741954
17751955 #[ test]
1776- fn instance_borrow_methods ( ) {
1956+ fn py_borrow_methods ( ) {
17771957 // More detailed tests of the underlying semantics in pycell.rs
17781958 Python :: with_gil ( |py| {
17791959 let instance = Py :: new ( py, SomeClass ( 0 ) ) . unwrap ( ) ;
@@ -1791,6 +1971,40 @@ a = A()
17911971 } )
17921972 }
17931973
1974+ #[ test]
1975+ fn bound_borrow_methods ( ) {
1976+ // More detailed tests of the underlying semantics in pycell.rs
1977+ Python :: with_gil ( |py| {
1978+ let instance = Bound :: new ( py, SomeClass ( 0 ) ) . unwrap ( ) ;
1979+ assert_eq ! ( instance. borrow( ) . 0 , 0 ) ;
1980+ assert_eq ! ( instance. try_borrow( ) . unwrap( ) . 0 , 0 ) ;
1981+ assert_eq ! ( instance. borrow_mut( ) . 0 , 0 ) ;
1982+ assert_eq ! ( instance. try_borrow_mut( ) . unwrap( ) . 0 , 0 ) ;
1983+
1984+ instance. borrow_mut ( ) . 0 = 123 ;
1985+
1986+ assert_eq ! ( instance. borrow( ) . 0 , 123 ) ;
1987+ assert_eq ! ( instance. try_borrow( ) . unwrap( ) . 0 , 123 ) ;
1988+ assert_eq ! ( instance. borrow_mut( ) . 0 , 123 ) ;
1989+ assert_eq ! ( instance. try_borrow_mut( ) . unwrap( ) . 0 , 123 ) ;
1990+ } )
1991+ }
1992+
1993+ #[ crate :: pyclass( frozen, crate = "crate" ) ]
1994+ struct FrozenClass ( i32 ) ;
1995+
1996+ #[ test]
1997+ fn test_frozen_get ( ) {
1998+ Python :: with_gil ( |py| {
1999+ for i in 0 ..10 {
2000+ let instance = Py :: new ( py, FrozenClass ( i) ) . unwrap ( ) ;
2001+ assert_eq ! ( instance. get( ) . 0 , i) ;
2002+
2003+ assert_eq ! ( instance. bind( py) . get( ) . 0 , i) ;
2004+ }
2005+ } )
2006+ }
2007+
17942008 #[ test]
17952009 #[ allow( deprecated) ]
17962010 fn cell_tryfrom ( ) {
0 commit comments