@@ -32,7 +32,7 @@ struct LazyTypeObjectInner {
3232 // Threads which have begun initialization of the `tp_dict`. Used for
3333 // reentrant initialization detection.
3434 initializing_threads : Mutex < Vec < ThreadId > > ,
35- tp_dict_filled : GILOnceCell < ( ) > ,
35+ fully_initialized_type : GILOnceCell < Py < PyType > > ,
3636}
3737
3838impl < T > LazyTypeObject < T > {
@@ -43,7 +43,7 @@ impl<T> LazyTypeObject<T> {
4343 LazyTypeObjectInner {
4444 value : GILOnceCell :: new ( ) ,
4545 initializing_threads : Mutex :: new ( Vec :: new ( ) ) ,
46- tp_dict_filled : GILOnceCell :: new ( ) ,
46+ fully_initialized_type : GILOnceCell :: new ( ) ,
4747 } ,
4848 PhantomData ,
4949 )
@@ -52,15 +52,18 @@ impl<T> LazyTypeObject<T> {
5252
5353impl < T : PyClass > LazyTypeObject < T > {
5454 /// Gets the type object contained by this `LazyTypeObject`, initializing it if needed.
55- pub fn get_or_init < ' py > ( & self , py : Python < ' py > ) -> & Bound < ' py , PyType > {
56- self . get_or_try_init ( py) . unwrap_or_else ( |err| {
57- err. print ( py) ;
58- panic ! ( "failed to create type object for {}" , T :: NAME )
59- } )
55+ #[ inline]
56+ pub fn get_or_try_init < ' py > ( & self , py : Python < ' py > ) -> PyResult < & Bound < ' py , PyType > > {
57+ if let Some ( type_object) = self . 0 . fully_initialized_type . get ( py) {
58+ // Fast path
59+ return Ok ( type_object. bind ( py) ) ;
60+ }
61+
62+ self . try_init ( py)
6063 }
6164
62- /// Fallible version of the above.
63- pub ( crate ) fn get_or_try_init < ' py > ( & self , py : Python < ' py > ) -> PyResult < & Bound < ' py , PyType > > {
65+ # [ cold ]
66+ fn try_init < ' py > ( & self , py : Python < ' py > ) -> PyResult < & Bound < ' py , PyType > > {
6467 self . 0
6568 . get_or_try_init ( py, create_type_object :: < T > , T :: NAME , T :: items_iter ( ) )
6669 }
@@ -116,7 +119,7 @@ impl LazyTypeObjectInner {
116119 // `tp_dict`, it can still request the type object through `get_or_init`,
117120 // but the `tp_dict` may appear empty of course.
118121
119- if self . tp_dict_filled . get ( py) . is_some ( ) {
122+ if self . fully_initialized_type . get ( py) . is_some ( ) {
120123 // `tp_dict` is already filled: ok.
121124 return Ok ( ( ) ) ;
122125 }
@@ -184,8 +187,8 @@ impl LazyTypeObjectInner {
184187
185188 // Now we hold the GIL and we can assume it won't be released until we
186189 // return from the function.
187- let result = self . tp_dict_filled . get_or_try_init ( py, move || {
188- let result = initialize_tp_dict ( py, type_object. as_ptr ( ) , items) ;
190+ let result = self . fully_initialized_type . get_or_try_init ( py, move || {
191+ initialize_tp_dict ( py, type_object. as_ptr ( ) , items) ? ;
189192 #[ cfg( Py_3_14 ) ]
190193 if is_immutable_type {
191194 // freeze immutable types after __dict__ is initialized
@@ -216,13 +219,13 @@ impl LazyTypeObjectInner {
216219 self . initializing_threads . lock ( ) . unwrap ( )
217220 } ;
218221 threads. clear ( ) ;
219- result
222+ Ok ( type_object . clone ( ) . unbind ( ) )
220223 } ) ;
221224
222225 if let Err ( err) = result {
223226 return Err ( wrap_in_runtime_error (
224227 py,
225- err. clone_ref ( py ) ,
228+ err,
226229 format ! ( "An error occurred while initializing `{name}.__dict__`" ) ,
227230 ) ) ;
228231 }
@@ -249,6 +252,13 @@ fn initialize_tp_dict(
249252// This is necessary for making static `LazyTypeObject`s
250253unsafe impl < T > Sync for LazyTypeObject < T > { }
251254
255+ /// Used in the macro-expanded implementation of `type_object_raw` for `#[pyclass]` types
256+ #[ cold]
257+ pub fn type_object_init_failed ( py : Python < ' _ > , err : PyErr , type_name : & str ) -> ! {
258+ err. write_unraisable ( py, None ) ;
259+ panic ! ( "failed to create type object for `{type_name}`" )
260+ }
261+
252262#[ cold]
253263fn wrap_in_runtime_error ( py : Python < ' _ > , err : PyErr , message : String ) -> PyErr {
254264 let runtime_err = PyRuntimeError :: new_err ( message) ;
0 commit comments