2021-12-03 00:03:32 +00:00
#![ cfg(feature = " macros " ) ]
2018-05-02 18:49:40 +00:00
use pyo3 ::prelude ::* ;
2020-08-07 12:31:17 +00:00
use pyo3 ::types ::PyType ;
use pyo3 ::{ py_run , PyClass } ;
2018-05-02 18:49:40 +00:00
2023-09-24 12:34:53 +00:00
#[ path = " ../src/tests/common.rs " ]
2018-05-02 18:49:40 +00:00
mod common ;
2018-07-08 21:33:48 +00:00
#[ pyclass ]
2018-06-15 19:21:12 +00:00
struct EmptyClass { }
2018-05-02 18:49:40 +00:00
#[ test ]
fn empty_class ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
2024-02-18 18:27:19 +00:00
let typeobj = py . get_type_bound ::< EmptyClass > ( ) ;
2022-07-19 17:34:23 +00:00
// By default, don't allow creating instances from python.
assert! ( typeobj . call ( ( ) , None ) . is_err ( ) ) ;
2018-05-02 18:49:40 +00:00
2022-07-19 17:34:23 +00:00
py_assert! ( py , typeobj , " typeobj.__name__ == 'EmptyClass' " ) ;
} ) ;
2018-05-02 18:49:40 +00:00
}
2021-03-17 16:04:25 +00:00
#[ pyclass ]
struct UnitClass ;
#[ test ]
fn unit_class ( ) {
2021-03-18 13:09:46 +00:00
Python ::with_gil ( | py | {
2024-02-18 18:27:19 +00:00
let typeobj = py . get_type_bound ::< UnitClass > ( ) ;
2021-03-18 13:09:46 +00:00
// By default, don't allow creating instances from python.
assert! ( typeobj . call ( ( ) , None ) . is_err ( ) ) ;
2021-03-17 16:04:25 +00:00
2021-03-18 13:09:46 +00:00
py_assert! ( py , typeobj , " typeobj.__name__ == 'UnitClass' " ) ;
} ) ;
2021-03-17 16:04:25 +00:00
}
2018-05-02 18:49:40 +00:00
/// Line1
///Line2
/// Line3
// this is not doc string
2018-07-08 21:33:48 +00:00
#[ pyclass ]
2020-02-03 08:01:30 +00:00
struct ClassWithDocs {
/// Property field
#[ pyo3(get, set) ]
value : i32 ,
/// Read-only property field
#[ pyo3(get) ]
readonly : i32 ,
/// Write-only property field
#[ pyo3(set) ]
2021-03-06 09:42:50 +00:00
#[ allow(dead_code) ] // Rust detects field is never read
2020-02-03 08:01:30 +00:00
writeonly : i32 ,
}
2018-05-02 18:49:40 +00:00
#[ test ]
fn class_with_docstr ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
2024-02-18 18:27:19 +00:00
let typeobj = py . get_type_bound ::< ClassWithDocs > ( ) ;
2018-06-15 19:21:12 +00:00
py_run! (
py ,
typeobj ,
" assert typeobj.__doc__ == 'Line1 \\ nLine2 \\ n Line3' "
) ;
2020-02-03 08:01:30 +00:00
py_run! (
py ,
typeobj ,
" assert typeobj.value.__doc__ == 'Property field' "
) ;
py_run! (
py ,
typeobj ,
" assert typeobj.readonly.__doc__ == 'Read-only property field' "
) ;
py_run! (
py ,
typeobj ,
" assert typeobj.writeonly.__doc__ == 'Write-only property field' "
) ;
2022-07-19 17:34:23 +00:00
} ) ;
2018-05-02 18:49:40 +00:00
}
2020-12-12 09:50:25 +00:00
#[ pyclass(name = " CustomName " ) ]
2018-06-15 19:21:12 +00:00
struct EmptyClass2 { }
2018-05-02 18:49:40 +00:00
2019-12-17 17:36:56 +00:00
#[ pymethods ]
impl EmptyClass2 {
2021-04-17 21:22:06 +00:00
#[ pyo3(name = " custom_fn " ) ]
2019-12-17 17:36:56 +00:00
fn bar ( & self ) { }
#[ staticmethod ]
2021-04-17 21:22:06 +00:00
#[ pyo3(name = " custom_static " ) ]
2019-12-17 17:36:56 +00:00
fn bar_static ( ) { }
2021-03-18 18:35:17 +00:00
#[ getter ]
2021-04-17 21:22:06 +00:00
#[ pyo3(name = " custom_getter " ) ]
2021-03-18 18:35:17 +00:00
fn foo ( & self ) -> i32 {
5
}
2019-12-17 17:36:56 +00:00
}
2018-05-02 18:49:40 +00:00
#[ test ]
2019-12-17 17:36:56 +00:00
fn custom_names ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
2024-02-18 18:27:19 +00:00
let typeobj = py . get_type_bound ::< EmptyClass2 > ( ) ;
2022-07-19 17:34:23 +00:00
py_assert! ( py , typeobj , " typeobj.__name__ == 'CustomName' " ) ;
py_assert! ( py , typeobj , " typeobj.custom_fn.__name__ == 'custom_fn' " ) ;
py_assert! (
py ,
typeobj ,
" typeobj.custom_static.__name__ == 'custom_static' "
) ;
py_assert! (
py ,
typeobj ,
" typeobj.custom_getter.__name__ == 'custom_getter' "
) ;
py_assert! ( py , typeobj , " not hasattr(typeobj, 'bar') " ) ;
py_assert! ( py , typeobj , " not hasattr(typeobj, 'bar_static') " ) ;
py_assert! ( py , typeobj , " not hasattr(typeobj, 'foo') " ) ;
} ) ;
2018-05-02 18:49:40 +00:00
}
2019-12-17 22:14:28 +00:00
#[ pyclass ]
2020-01-27 08:13:55 +00:00
struct RawIdents {
#[ pyo3(get, set) ]
r#type : i64 ,
}
2019-12-17 22:14:28 +00:00
#[ pymethods ]
impl RawIdents {
2019-12-17 22:58:34 +00:00
fn r #fn ( & self ) { }
2019-12-17 22:14:28 +00:00
}
#[ test ]
fn test_raw_idents ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
2024-02-18 18:27:19 +00:00
let typeobj = py . get_type_bound ::< RawIdents > ( ) ;
2022-07-19 17:34:23 +00:00
py_assert! ( py , typeobj , " not hasattr(typeobj, 'r#fn') " ) ;
py_assert! ( py , typeobj , " hasattr(typeobj, 'fn') " ) ;
py_assert! ( py , typeobj , " hasattr(typeobj, 'type') " ) ;
} ) ;
2018-05-02 18:49:40 +00:00
}
2018-07-08 21:33:48 +00:00
#[ pyclass ]
2018-06-15 19:21:12 +00:00
struct EmptyClassInModule { }
2018-05-02 18:49:40 +00:00
2020-09-01 00:36:26 +00:00
// Ignored because heap types do not show up as being in builtins, instead they
// raise AttributeError:
2022-12-28 19:05:48 +00:00
// https://github.com/python/cpython/blob/v3.11.1/Objects/typeobject.c#L541-L570
2018-05-02 18:49:40 +00:00
#[ test ]
2020-09-01 00:36:26 +00:00
#[ ignore ]
2018-05-02 18:49:40 +00:00
fn empty_class_in_module ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
let module = PyModule ::new ( py , " test_module.nested " ) . unwrap ( ) ;
module . add_class ::< EmptyClassInModule > ( ) . unwrap ( ) ;
2018-05-02 18:49:40 +00:00
2022-07-19 17:34:23 +00:00
let ty = module . getattr ( " EmptyClassInModule " ) . unwrap ( ) ;
assert_eq! (
ty . getattr ( " __name__ " ) . unwrap ( ) . extract ::< String > ( ) . unwrap ( ) ,
" EmptyClassInModule "
) ;
2019-02-01 16:15:33 +00:00
2022-07-19 17:34:23 +00:00
let module : String = ty . getattr ( " __module__ " ) . unwrap ( ) . extract ( ) . unwrap ( ) ;
2019-02-01 16:15:33 +00:00
2022-07-19 17:34:23 +00:00
// Rationale: The class can be added to many modules, but will only be initialized once.
// We currently have no way of determining a canonical module, so builtins is better
// than using whatever calls init first.
assert_eq! ( module , " builtins " ) ;
} ) ;
2018-05-02 18:49:40 +00:00
}
2020-02-07 19:31:13 +00:00
#[ pyclass ]
struct ClassWithObjectField {
2020-05-17 10:45:42 +00:00
// It used to be that PyObject was not supported with (get, set)
// - this test is just ensuring it compiles.
2020-02-07 19:31:13 +00:00
#[ pyo3(get, set) ]
value : PyObject ,
}
#[ pymethods ]
impl ClassWithObjectField {
#[ new ]
fn new ( value : PyObject ) -> ClassWithObjectField {
Self { value }
}
}
#[ test ]
fn class_with_object_field ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
2024-02-18 18:27:19 +00:00
let ty = py . get_type_bound ::< ClassWithObjectField > ( ) ;
2022-07-19 17:34:23 +00:00
py_assert! ( py , ty , " ty(5).value == 5 " ) ;
py_assert! ( py , ty , " ty(None).value == None " ) ;
} ) ;
2020-02-07 19:31:13 +00:00
}
2020-06-29 03:05:50 +00:00
2020-09-01 00:36:26 +00:00
#[ pyclass(unsendable, subclass) ]
2020-06-29 03:05:50 +00:00
struct UnsendableBase {
2020-08-07 12:31:17 +00:00
value : std ::rc ::Rc < usize > ,
2020-06-29 03:05:50 +00:00
}
#[ pymethods ]
impl UnsendableBase {
2020-08-07 12:31:17 +00:00
#[ new ]
fn new ( value : usize ) -> UnsendableBase {
Self {
value : std ::rc ::Rc ::new ( value ) ,
}
}
#[ getter ]
2020-06-29 03:05:50 +00:00
fn value ( & self ) -> usize {
2020-08-07 12:31:17 +00:00
* self . value
2020-06-29 03:05:50 +00:00
}
}
#[ pyclass(extends=UnsendableBase) ]
struct UnsendableChild { }
2020-08-07 12:31:17 +00:00
#[ pymethods ]
impl UnsendableChild {
#[ new ]
fn new ( value : usize ) -> ( UnsendableChild , UnsendableBase ) {
( UnsendableChild { } , UnsendableBase ::new ( value ) )
}
}
fn test_unsendable < T : PyClass + 'static > ( ) -> PyResult < ( ) > {
2023-05-24 09:04:05 +00:00
let obj = Python ::with_gil ( | py | -> PyResult < _ > {
2024-02-18 03:07:48 +00:00
let obj : Py < T > = PyType ::new_bound ::< T > ( py ) . call1 ( ( 5 , ) ) ? . extract ( ) ? ;
2023-05-24 09:04:05 +00:00
// Accessing the value inside this thread should not panic
let caught_panic =
std ::panic ::catch_unwind ( std ::panic ::AssertUnwindSafe ( | | -> PyResult < _ > {
2024-02-18 00:09:56 +00:00
assert_eq! ( obj . getattr ( py , " value " ) ? . extract ::< usize > ( py ) ? , 5 ) ;
2023-05-24 09:04:05 +00:00
Ok ( ( ) )
} ) )
. is_err ( ) ;
assert! ( ! caught_panic ) ;
Ok ( obj )
} ) ? ;
let keep_obj_here = obj . clone ( ) ;
let caught_panic = std ::thread ::spawn ( move | | {
// This access must panic
2020-08-07 12:31:17 +00:00
Python ::with_gil ( | py | {
2023-05-24 09:04:05 +00:00
obj . borrow ( py ) ;
} ) ;
2020-08-07 12:31:17 +00:00
} )
2023-05-24 09:04:05 +00:00
. join ( ) ;
2020-08-07 12:31:17 +00:00
2023-05-24 09:04:05 +00:00
Python ::with_gil ( | _py | drop ( keep_obj_here ) ) ;
2020-08-07 12:31:17 +00:00
2023-05-24 09:04:05 +00:00
if let Err ( err ) = caught_panic {
if let Some ( msg ) = err . downcast_ref ::< String > ( ) {
panic! ( " {} " , msg ) ;
}
}
Ok ( ( ) )
2020-08-07 12:31:17 +00:00
}
2020-06-29 03:05:50 +00:00
/// If a class is marked as `unsendable`, it panics when accessed by another thread.
#[ test ]
2022-06-08 04:59:18 +00:00
#[ cfg_attr(target_arch = " wasm32 " , ignore) ]
2020-08-07 12:31:17 +00:00
#[ should_panic(
2023-08-25 10:22:25 +00:00
expected = " test_class_basics::UnsendableBase is unsendable, but sent to another thread "
2020-08-07 12:31:17 +00:00
) ]
fn panic_unsendable_base ( ) {
test_unsendable ::< UnsendableBase > ( ) . unwrap ( ) ;
}
2020-06-30 08:58:31 +00:00
2020-08-07 12:31:17 +00:00
#[ test ]
2022-06-08 04:59:18 +00:00
#[ cfg_attr(target_arch = " wasm32 " , ignore) ]
2020-08-07 12:31:17 +00:00
#[ should_panic(
2023-08-25 10:22:25 +00:00
expected = " test_class_basics::UnsendableBase is unsendable, but sent to another thread "
2020-08-07 12:31:17 +00:00
) ]
fn panic_unsendable_child ( ) {
test_unsendable ::< UnsendableChild > ( ) . unwrap ( ) ;
2020-06-29 03:05:50 +00:00
}
2021-02-20 15:15:20 +00:00
2024-02-16 00:36:11 +00:00
fn get_length ( obj : & Bound < '_ , PyAny > ) -> PyResult < usize > {
2021-02-20 15:15:20 +00:00
let length = obj . len ( ) ? ;
Ok ( length )
}
#[ pyclass ]
struct ClassWithFromPyWithMethods { }
#[ pymethods ]
impl ClassWithFromPyWithMethods {
fn instance_method ( & self , #[ pyo3(from_py_with = " get_length " ) ] argument : usize ) -> usize {
argument
}
#[ classmethod ]
2024-02-16 00:36:11 +00:00
fn classmethod (
_cls : & Bound < '_ , PyType > ,
#[ pyo3(from_py_with = " Bound::<'_, PyAny>::len " ) ] argument : usize ,
) -> usize {
argument
}
#[ classmethod ]
fn classmethod_gil_ref (
_cls : & PyType ,
#[ pyo3(from_py_with = " PyAny::len " ) ] argument : usize ,
) -> usize {
2021-02-20 15:15:20 +00:00
argument
}
#[ staticmethod ]
fn staticmethod ( #[ pyo3(from_py_with = " get_length " ) ] argument : usize ) -> usize {
argument
}
}
#[ test ]
fn test_pymethods_from_py_with ( ) {
Python ::with_gil ( | py | {
let instance = Py ::new ( py , ClassWithFromPyWithMethods { } ) . unwrap ( ) ;
py_run! (
py ,
instance ,
r #"
arg = { 1 : 1 , 2 : 3 }
assert instance . instance_method ( arg ) = = 2
assert instance . classmethod ( arg ) = = 2
2024-02-16 00:36:11 +00:00
assert instance . classmethod_gil_ref ( arg ) = = 2
2021-02-20 15:15:20 +00:00
assert instance . staticmethod ( arg ) = = 2
" #
) ;
} )
}
2021-03-17 16:04:25 +00:00
#[ pyclass ]
2021-06-04 10:16:25 +00:00
struct TupleClass ( #[ pyo3(get, set, name = " value " ) ] i32 ) ;
2021-03-17 16:04:25 +00:00
#[ test ]
fn test_tuple_struct_class ( ) {
2021-03-18 13:09:46 +00:00
Python ::with_gil ( | py | {
2024-02-18 18:27:19 +00:00
let typeobj = py . get_type_bound ::< TupleClass > ( ) ;
2021-03-18 13:09:46 +00:00
assert! ( typeobj . call ( ( ) , None ) . is_err ( ) ) ;
2021-03-17 16:04:25 +00:00
2021-03-18 13:09:46 +00:00
py_assert! ( py , typeobj , " typeobj.__name__ == 'TupleClass' " ) ;
2021-06-04 10:16:25 +00:00
let instance = Py ::new ( py , TupleClass ( 5 ) ) . unwrap ( ) ;
py_run! (
py ,
instance ,
r #"
assert instance . value = = 5 ;
instance . value = 1234 ;
assert instance . value = = 1234 ;
" #
) ;
assert_eq! ( instance . borrow ( py ) . 0 , 1234 ) ;
2021-03-18 13:09:46 +00:00
} ) ;
2021-03-17 16:04:25 +00:00
}
2021-12-30 12:16:46 +00:00
#[ pyclass(dict, subclass) ]
2022-06-12 13:11:39 +00:00
struct DunderDictSupport {
// Make sure that dict_offset runs with non-zero sized Self
_pad : [ u8 ; 32 ] ,
}
2021-12-30 12:16:46 +00:00
#[ test ]
#[ cfg_attr(all(Py_LIMITED_API, not(Py_3_9)), ignore) ]
fn dunder_dict_support ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
let inst = PyCell ::new (
py ,
DunderDictSupport {
_pad : * b " DEADBEEFDEADBEEFDEADBEEFDEADBEEF " ,
} ,
)
. unwrap ( ) ;
py_run! (
py ,
inst ,
r #"
2021-12-30 12:16:46 +00:00
inst . a = 1
assert inst . a = = 1
" #
2022-07-19 17:34:23 +00:00
) ;
} ) ;
2021-12-30 12:16:46 +00:00
}
// Accessing inst.__dict__ only supported in limited API from Python 3.10
#[ test ]
#[ cfg_attr(all(Py_LIMITED_API, not(Py_3_10)), ignore) ]
fn access_dunder_dict ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
let inst = PyCell ::new (
py ,
DunderDictSupport {
_pad : * b " DEADBEEFDEADBEEFDEADBEEFDEADBEEF " ,
} ,
)
. unwrap ( ) ;
py_run! (
py ,
inst ,
r #"
2021-12-30 12:16:46 +00:00
inst . a = 1
assert inst . __dict__ = = { 'a' : 1 }
" #
2022-07-19 17:34:23 +00:00
) ;
} ) ;
2021-12-30 12:16:46 +00:00
}
// If the base class has dict support, child class also has dict
#[ pyclass(extends=DunderDictSupport) ]
struct InheritDict {
_value : usize ,
}
#[ test ]
#[ cfg_attr(all(Py_LIMITED_API, not(Py_3_9)), ignore) ]
fn inherited_dict ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
let inst = PyCell ::new (
py ,
(
InheritDict { _value : 0 } ,
DunderDictSupport {
_pad : * b " DEADBEEFDEADBEEFDEADBEEFDEADBEEF " ,
} ,
) ,
)
. unwrap ( ) ;
py_run! (
py ,
inst ,
r #"
2021-12-30 12:16:46 +00:00
inst . a = 1
assert inst . a = = 1
" #
2022-07-19 17:34:23 +00:00
) ;
} ) ;
2021-12-30 12:16:46 +00:00
}
#[ pyclass(weakref, dict) ]
2022-06-12 13:11:39 +00:00
struct WeakRefDunderDictSupport {
// Make sure that weaklist_offset runs with non-zero sized Self
_pad : [ u8 ; 32 ] ,
}
2021-12-30 12:16:46 +00:00
#[ test ]
#[ cfg_attr(all(Py_LIMITED_API, not(Py_3_9)), ignore) ]
fn weakref_dunder_dict_support ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
let inst = PyCell ::new (
py ,
WeakRefDunderDictSupport {
_pad : * b " DEADBEEFDEADBEEFDEADBEEFDEADBEEF " ,
} ,
)
. unwrap ( ) ;
py_run! (
py ,
inst ,
" import weakref; assert weakref.ref(inst)() is inst; inst.a = 1; assert inst.a == 1 "
) ;
} ) ;
2021-12-30 12:16:46 +00:00
}
#[ pyclass(weakref, subclass) ]
2022-06-12 13:11:39 +00:00
struct WeakRefSupport {
_pad : [ u8 ; 32 ] ,
}
2021-12-30 12:16:46 +00:00
#[ test ]
#[ cfg_attr(all(Py_LIMITED_API, not(Py_3_9)), ignore) ]
fn weakref_support ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
let inst = PyCell ::new (
py ,
WeakRefSupport {
_pad : * b " DEADBEEFDEADBEEFDEADBEEFDEADBEEF " ,
} ,
)
. unwrap ( ) ;
py_run! (
py ,
inst ,
" import weakref; assert weakref.ref(inst)() is inst "
) ;
} ) ;
2021-12-30 12:16:46 +00:00
}
// If the base class has weakref support, child class also has weakref.
#[ pyclass(extends=WeakRefSupport) ]
struct InheritWeakRef {
_value : usize ,
}
#[ test ]
#[ cfg_attr(all(Py_LIMITED_API, not(Py_3_9)), ignore) ]
fn inherited_weakref ( ) {
2022-07-19 17:34:23 +00:00
Python ::with_gil ( | py | {
let inst = PyCell ::new (
py ,
(
InheritWeakRef { _value : 0 } ,
WeakRefSupport {
_pad : * b " DEADBEEFDEADBEEFDEADBEEFDEADBEEF " ,
} ,
) ,
)
. unwrap ( ) ;
py_run! (
py ,
inst ,
" import weakref; assert weakref.ref(inst)() is inst "
) ;
} ) ;
2021-12-30 12:16:46 +00:00
}
2023-05-16 19:48:59 +00:00
#[ test ]
fn access_frozen_class_without_gil ( ) {
use std ::sync ::atomic ::{ AtomicUsize , Ordering } ;
#[ pyclass(frozen) ]
struct FrozenCounter {
value : AtomicUsize ,
}
let py_counter : Py < FrozenCounter > = Python ::with_gil ( | py | {
let counter = FrozenCounter {
value : AtomicUsize ::new ( 0 ) ,
} ;
let cell = PyCell ::new ( py , counter ) . unwrap ( ) ;
cell . get ( ) . value . fetch_add ( 1 , Ordering ::Relaxed ) ;
cell . into ( )
} ) ;
assert_eq! ( py_counter . get ( ) . value . load ( Ordering ::Relaxed ) , 1 ) ;
}
2023-05-22 07:26:07 +00:00
#[ test ]
2023-05-24 09:04:05 +00:00
#[ cfg(Py_3_8) ] // sys.unraisablehook not available until Python 3.8
#[ cfg_attr(target_arch = " wasm32 " , ignore) ]
2023-05-22 07:26:07 +00:00
fn drop_unsendable_elsewhere ( ) {
2023-05-24 09:04:05 +00:00
use common ::UnraisableCapture ;
2023-05-22 07:26:07 +00:00
use std ::sync ::{
atomic ::{ AtomicBool , Ordering } ,
Arc ,
} ;
use std ::thread ::spawn ;
#[ pyclass(unsendable) ]
struct Unsendable {
dropped : Arc < AtomicBool > ,
}
impl Drop for Unsendable {
fn drop ( & mut self ) {
self . dropped . store ( true , Ordering ::SeqCst ) ;
}
}
2023-05-24 09:04:05 +00:00
Python ::with_gil ( | py | {
let capture = UnraisableCapture ::install ( py ) ;
2023-05-22 07:26:07 +00:00
2023-05-24 09:04:05 +00:00
let dropped = Arc ::new ( AtomicBool ::new ( false ) ) ;
2023-05-22 07:26:07 +00:00
2023-05-24 09:04:05 +00:00
let unsendable = Py ::new (
py ,
Unsendable {
dropped : dropped . clone ( ) ,
} ,
)
. unwrap ( ) ;
2023-05-22 07:26:07 +00:00
2023-05-24 09:04:05 +00:00
py . allow_threads ( | | {
spawn ( move | | {
Python ::with_gil ( move | _py | {
drop ( unsendable ) ;
} ) ;
} )
. join ( )
. unwrap ( ) ;
2023-05-22 07:26:07 +00:00
} ) ;
2023-05-24 09:04:05 +00:00
assert! ( ! dropped . load ( Ordering ::SeqCst ) ) ;
let ( err , object ) = capture . borrow_mut ( py ) . capture . take ( ) . unwrap ( ) ;
2023-08-25 10:22:25 +00:00
assert_eq! ( err . to_string ( ) , " RuntimeError: test_class_basics::drop_unsendable_elsewhere::Unsendable is unsendable, but is being dropped on another thread " ) ;
2023-05-24 09:04:05 +00:00
assert! ( object . is_none ( py ) ) ;
capture . borrow_mut ( py ) . uninstall ( py ) ;
} ) ;
2023-05-22 07:26:07 +00:00
}