1use super::{
2 Traverse, TraverseFn,
3 core::{Py, PyObject, PyObjectRef, PyRef},
4 payload::PyPayload,
5};
6use crate::common::atomic::{Ordering, PyAtomic, Radium};
7use crate::{
8 VirtualMachine,
9 builtins::{PyBaseExceptionRef, PyStrInterned, PyType},
10 convert::{IntoPyException, ToPyObject, ToPyResult, TryFromObject},
11 vm::Context,
12};
13use alloc::fmt;
14
15use core::{
16 borrow::Borrow,
17 marker::PhantomData,
18 ops::Deref,
19 ptr::{NonNull, null_mut},
20};
21
22pub type PyResult<T = PyObjectRef> = Result<T, PyBaseExceptionRef>; impl<T: PyPayload + fmt::Display> fmt::Display for PyRef<T> {
42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43 fmt::Display::fmt(&**self, f)
44 }
45}
46
47impl<T: PyPayload + fmt::Display> fmt::Display for Py<T> {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 fmt::Display::fmt(&**self, f)
50 }
51}
52
53#[repr(transparent)]
54pub struct PyExact<T> {
55 inner: Py<T>,
56}
57
58impl<T: PyPayload> PyExact<T> {
59 #[inline(always)]
62 pub const unsafe fn ref_unchecked(r: &Py<T>) -> &Self {
63 unsafe { &*(r as *const _ as *const Self) }
64 }
65}
66
67impl<T: PyPayload> Deref for PyExact<T> {
68 type Target = Py<T>;
69
70 #[inline(always)]
71 fn deref(&self) -> &Py<T> {
72 &self.inner
73 }
74}
75
76impl<T: PyPayload> Borrow<PyObject> for PyExact<T> {
77 #[inline(always)]
78 fn borrow(&self) -> &PyObject {
79 self.inner.borrow()
80 }
81}
82
83impl<T: PyPayload> AsRef<PyObject> for PyExact<T> {
84 #[inline(always)]
85 fn as_ref(&self) -> &PyObject {
86 self.inner.as_ref()
87 }
88}
89
90impl<T: PyPayload> Borrow<Py<T>> for PyExact<T> {
91 #[inline(always)]
92 fn borrow(&self) -> &Py<T> {
93 &self.inner
94 }
95}
96
97impl<T: PyPayload> AsRef<Py<T>> for PyExact<T> {
98 #[inline(always)]
99 fn as_ref(&self) -> &Py<T> {
100 &self.inner
101 }
102}
103
104impl<T: PyPayload> alloc::borrow::ToOwned for PyExact<T> {
105 type Owned = PyRefExact<T>;
106
107 fn to_owned(&self) -> Self::Owned {
108 let owned = self.inner.to_owned();
109 unsafe { PyRefExact::new_unchecked(owned) }
110 }
111}
112
113impl<T: PyPayload> PyRef<T> {
114 pub fn into_exact_or(
115 self,
116 ctx: &Context,
117 f: impl FnOnce(Self) -> PyRefExact<T>,
118 ) -> PyRefExact<T> {
119 if self.class().is(T::class(ctx)) {
120 unsafe { PyRefExact::new_unchecked(self) }
121 } else {
122 f(self)
123 }
124 }
125}
126
127#[derive(Debug)]
129#[repr(transparent)]
130pub struct PyRefExact<T: PyPayload> {
131 inner: PyRef<T>,
132}
133
134impl<T: PyPayload> PyRefExact<T> {
135 #[must_use]
138 pub const unsafe fn new_unchecked(obj: PyRef<T>) -> Self {
139 Self { inner: obj }
140 }
141
142 #[must_use]
143 pub fn into_pyref(self) -> PyRef<T> {
144 self.inner
145 }
146}
147
148impl<T: PyPayload> Clone for PyRefExact<T> {
149 fn clone(&self) -> Self {
150 let inner = self.inner.clone();
151 Self { inner }
152 }
153}
154
155impl<T: PyPayload> TryFromObject for PyRefExact<T> {
156 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
157 let target_cls = T::class(&vm.ctx);
158 let cls = obj.class();
159 if cls.is(target_cls) {
160 let obj = obj
161 .downcast()
162 .map_err(|obj| vm.new_downcast_runtime_error(target_cls, &obj))?;
163 Ok(Self { inner: obj })
164 } else if cls.fast_issubclass(target_cls) {
165 Err(vm.new_type_error(format!(
166 "Expected an exact instance of '{}', not a subclass '{}'",
167 target_cls.name(),
168 cls.name(),
169 )))
170 } else {
171 Err(vm.new_type_error(format!(
172 "Expected type '{}', not '{}'",
173 target_cls.name(),
174 cls.name(),
175 )))
176 }
177 }
178}
179
180impl<T: PyPayload> Deref for PyRefExact<T> {
181 type Target = PyExact<T>;
182
183 #[inline(always)]
184 fn deref(&self) -> &PyExact<T> {
185 unsafe { PyExact::ref_unchecked(self.inner.deref()) }
186 }
187}
188
189impl<T: PyPayload> Borrow<PyObject> for PyRefExact<T> {
190 #[inline(always)]
191 fn borrow(&self) -> &PyObject {
192 self.inner.borrow()
193 }
194}
195
196impl<T: PyPayload> AsRef<PyObject> for PyRefExact<T> {
197 #[inline(always)]
198 fn as_ref(&self) -> &PyObject {
199 self.inner.as_ref()
200 }
201}
202
203impl<T: PyPayload> Borrow<Py<T>> for PyRefExact<T> {
204 #[inline(always)]
205 fn borrow(&self) -> &Py<T> {
206 self.inner.borrow()
207 }
208}
209
210impl<T: PyPayload> AsRef<Py<T>> for PyRefExact<T> {
211 #[inline(always)]
212 fn as_ref(&self) -> &Py<T> {
213 self.inner.as_ref()
214 }
215}
216
217impl<T: PyPayload> Borrow<PyExact<T>> for PyRefExact<T> {
218 #[inline(always)]
219 fn borrow(&self) -> &PyExact<T> {
220 self
221 }
222}
223
224impl<T: PyPayload> AsRef<PyExact<T>> for PyRefExact<T> {
225 #[inline(always)]
226 fn as_ref(&self) -> &PyExact<T> {
227 self
228 }
229}
230
231impl<T: PyPayload> ToPyObject for PyRefExact<T> {
232 #[inline(always)]
233 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
234 self.inner.into()
235 }
236}
237
238pub struct PyAtomicRef<T> {
239 inner: PyAtomic<*mut u8>,
240 _phantom: PhantomData<T>,
241}
242
243impl<T> Unpin for PyAtomicRef<T> {}
246
247const _: () = assert!(
250 core::mem::size_of::<PyAtomicRef<Option<PyObject>>>()
251 == core::mem::size_of::<PyAtomicRef<()>>()
252 && core::mem::align_of::<PyAtomicRef<Option<PyObject>>>()
253 == core::mem::align_of::<PyAtomicRef<()>>()
254 && core::mem::offset_of!(PyAtomicRef<Option<PyObject>>, inner)
255 == core::mem::offset_of!(PyAtomicRef<()>, inner)
256 && core::mem::offset_of!(PyAtomicRef<Option<PyObject>>, inner) == 0
257);
258
259impl<T> Drop for PyAtomicRef<T> {
260 fn drop(&mut self) {
261 unsafe {
264 let ptr = Radium::swap(&self.inner, null_mut(), Ordering::Relaxed);
265 if let Some(ptr) = NonNull::<PyObject>::new(ptr.cast()) {
266 let _: PyObjectRef = PyObjectRef::from_raw(ptr);
267 }
268 }
269 }
270}
271
272cfg_select! {
273 feature = "threading" => {
274 unsafe impl<T: Send + PyPayload> Send for PyAtomicRef<T> {}
275 unsafe impl<T: Sync + PyPayload> Sync for PyAtomicRef<T> {}
276 unsafe impl<T: Send + PyPayload> Send for PyAtomicRef<Option<T>> {}
277 unsafe impl<T: Sync + PyPayload> Sync for PyAtomicRef<Option<T>> {}
278 unsafe impl Send for PyAtomicRef<PyObject> {}
279 unsafe impl Sync for PyAtomicRef<PyObject> {}
280 unsafe impl Send for PyAtomicRef<Option<PyObject>> {}
281 unsafe impl Sync for PyAtomicRef<Option<PyObject>> {}
282 }
283 _ => {}
284}
285
286impl<T> fmt::Debug for PyAtomicRef<T> {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 write!(f, "PyAtomicRef(")?;
289 unsafe {
293 self.inner
294 .load(Ordering::Relaxed)
295 .cast::<PyObject>()
296 .as_ref()
297 .fmt(f)
298 }?;
299 write!(f, ")")
300 }
301}
302
303impl<T: PyPayload> From<PyRef<T>> for PyAtomicRef<T> {
304 fn from(pyref: PyRef<T>) -> Self {
305 let py = PyRef::leak(pyref);
306 let ptr = py as *const _ as *mut u8;
307 ptr.expose_provenance();
310 Self {
311 inner: Radium::new(ptr),
312 _phantom: Default::default(),
313 }
314 }
315}
316
317impl<T: PyPayload> Deref for PyAtomicRef<T> {
318 type Target = Py<T>;
319
320 fn deref(&self) -> &Self::Target {
321 unsafe {
322 self.inner
323 .load(Ordering::Relaxed)
324 .cast::<Py<T>>()
325 .as_ref()
326 .unwrap_unchecked()
327 }
328 }
329}
330
331impl<T: PyPayload> PyAtomicRef<T> {
332 pub(super) unsafe fn from_ref_without_retag(pyref: PyRef<T>) -> Self {
340 let ptr = pyref.into_non_null().as_ptr().cast::<u8>();
341 ptr.expose_provenance();
342 Self {
343 inner: Radium::new(ptr),
344 _phantom: Default::default(),
345 }
346 }
347
348 #[inline(always)]
352 pub(super) fn load_raw(&self) -> *const Py<T> {
353 self.inner.load(Ordering::Relaxed).cast::<Py<T>>()
354 }
355
356 #[must_use]
360 pub unsafe fn swap(&self, pyref: PyRef<T>) -> PyRef<T> {
361 let py = PyRef::leak(pyref) as *const Py<T> as *mut _;
362 let old = Radium::swap(&self.inner, py, Ordering::AcqRel);
363 unsafe { PyRef::from_raw(old.cast()) }
364 }
365
366 pub fn swap_to_temporary_refs(&self, pyref: PyRef<T>, vm: &VirtualMachine) {
367 let old = unsafe { self.swap(pyref) };
368 if let Some(frame) = vm.current_frame() {
369 frame.iframe().cold().temporary_refs.lock().push(old.into());
370 }
371 }
372
373 pub(crate) fn load_owned(&self) -> PyRef<T> {
379 loop {
380 if let Some(obj) = cell_load_owned(&self.inner) {
381 return unsafe { obj.downcast_unchecked() };
383 }
384 core::hint::spin_loop();
385 }
386 }
387
388 pub(crate) fn store(&self, value: PyRef<T>) -> Option<PyRef<T>> {
393 cell_store(&self.inner, Some(value.into())).map(|obj| {
394 unsafe { obj.downcast_unchecked() }
396 })
397 }
398}
399
400impl<T: PyPayload> From<Option<PyRef<T>>> for PyAtomicRef<Option<T>> {
401 fn from(opt_ref: Option<PyRef<T>>) -> Self {
402 let val = opt_ref.map_or(null_mut(), |x| PyRef::leak(x) as *const Py<T> as *mut _);
403 Self {
404 inner: Radium::new(val),
405 _phantom: Default::default(),
406 }
407 }
408}
409
410impl<T: PyPayload> PyAtomicRef<Option<T>> {
411 pub(super) unsafe fn from_optional_ref_without_retag(opt_ref: Option<PyRef<T>>) -> Self {
417 let ptr = opt_ref.map_or(null_mut(), |pyref| {
418 pyref.into_non_null().as_ptr().cast::<u8>()
419 });
420 ptr.expose_provenance();
421 Self {
422 inner: Radium::new(ptr),
423 _phantom: Default::default(),
424 }
425 }
426
427 pub fn deref(&self) -> Option<&Py<T>> {
428 self.deref_ordering(Ordering::Relaxed)
429 }
430
431 pub fn deref_ordering(&self, ordering: Ordering) -> Option<&Py<T>> {
432 unsafe { self.inner.load(ordering).cast::<Py<T>>().as_ref() }
433 }
434
435 #[must_use]
439 pub unsafe fn swap(&self, opt_ref: Option<PyRef<T>>) -> Option<PyRef<T>> {
440 let val = opt_ref.map_or(null_mut(), |x| PyRef::leak(x) as *const Py<T> as *mut _);
441 let old = Radium::swap(&self.inner, val, Ordering::AcqRel);
442 unsafe { old.cast::<Py<T>>().as_ref().map(|x| PyRef::from_raw(x)) }
443 }
444
445 pub fn swap_to_temporary_refs(&self, opt_ref: Option<PyRef<T>>, vm: &VirtualMachine) {
446 let Some(old) = (unsafe { self.swap(opt_ref) }) else {
447 return;
448 };
449 if let Some(frame) = vm.current_frame() {
450 frame.iframe().cold().temporary_refs.lock().push(old.into());
451 }
452 }
453
454 pub fn load_owned(&self) -> Option<PyRef<T>> {
462 cell_load_owned(&self.inner).map(|obj| {
463 unsafe { obj.downcast_unchecked() }
465 })
466 }
467
468 pub(crate) fn store(&self, value: Option<PyRef<T>>) -> Option<PyRef<T>> {
470 cell_store(&self.inner, value.map(PyObjectRef::from)).map(|obj| {
471 unsafe { obj.downcast_unchecked() }
473 })
474 }
475
476 pub(crate) fn compare_exchange_empty(&self, value: PyRef<T>) -> Result<(), PyRef<T>> {
480 let cell = unsafe { &*core::ptr::from_ref(self).cast::<PyAtomicRef<Option<PyObject>>>() };
484 cell.compare_exchange_empty(value.into()).map_err(|obj| {
485 unsafe { obj.downcast_unchecked() }
487 })
488 }
489}
490
491fn cell_load_ptr(inner: &PyAtomic<*mut u8>) -> *mut PyObject {
492 inner.load(Ordering::Acquire).cast()
493}
494
495fn cell_load_owned(inner: &PyAtomic<*mut u8>) -> Option<PyObjectRef> {
501 let ptr = inner.load(Ordering::Acquire);
502 if ptr.is_null() {
503 return None;
504 }
505 #[cfg(not(feature = "threading"))]
509 {
510 unsafe { PyObject::try_to_owned_from_ptr(ptr.cast()) }
513 }
514 #[cfg(feature = "threading")]
515 {
516 let mut ptr = ptr;
517 loop {
518 if let Some(obj) = unsafe { PyObject::try_to_owned_from_ptr(ptr.cast()) }
521 && core::ptr::eq(inner.load(Ordering::Acquire), ptr)
522 {
523 return Some(obj);
524 }
525 ptr = inner.load(Ordering::Acquire);
526 if ptr.is_null() {
527 return None;
528 }
529 core::hint::spin_loop();
530 }
531 }
532}
533
534fn cell_store(inner: &PyAtomic<*mut u8>, value: Option<PyObjectRef>) -> Option<PyObjectRef> {
540 let new_ptr = match value {
541 Some(obj) => {
542 let ptr = obj.into_raw().as_ptr();
543 ptr.expose_provenance();
544 ptr.cast()
545 }
546 None => null_mut(),
547 };
548 let old = Radium::swap(inner, new_ptr, Ordering::AcqRel);
549 let old = NonNull::new(old.cast()).map(|ptr| unsafe { PyObjectRef::from_raw(ptr) });
551 #[cfg(feature = "threading")]
552 if let Some(old) = old.as_ref() {
553 old.mark_cache_published();
554 }
555 old
556}
557
558fn cell_compare_exchange_empty(
562 inner: &PyAtomic<*mut u8>,
563 value: PyObjectRef,
564) -> Result<(), PyObjectRef> {
565 let raw = value.into_raw();
566 let ptr = raw.as_ptr();
567 ptr.expose_provenance();
568 match inner.compare_exchange(
569 core::ptr::null_mut(),
570 ptr.cast(),
571 Ordering::AcqRel,
572 Ordering::Acquire,
573 ) {
574 Ok(_) => Ok(()),
575 Err(_) => {
576 Err(unsafe { PyObjectRef::from_raw(raw) })
579 }
580 }
581}
582
583impl From<PyObjectRef> for PyAtomicRef<PyObject> {
584 fn from(obj: PyObjectRef) -> Self {
585 let obj = obj.into_raw();
586 Self {
587 inner: Radium::new(obj.cast().as_ptr()),
588 _phantom: Default::default(),
589 }
590 }
591}
592
593impl Deref for PyAtomicRef<PyObject> {
594 type Target = PyObject;
595
596 fn deref(&self) -> &Self::Target {
597 unsafe {
598 self.inner
599 .load(Ordering::Relaxed)
600 .cast::<PyObject>()
601 .as_ref()
602 .unwrap_unchecked()
603 }
604 }
605}
606
607impl PyAtomicRef<PyObject> {
608 pub(crate) fn load_owned(&self) -> PyObjectRef {
613 cell_load_owned(&self.inner).expect("non-null atomic cell")
614 }
615
616 pub(crate) fn store(&self, value: PyObjectRef) -> PyObjectRef {
620 cell_store(&self.inner, Some(value)).expect("non-null atomic cell")
621 }
622
623 #[must_use]
627 pub unsafe fn swap(&self, obj: PyObjectRef) -> PyObjectRef {
628 let obj = obj.into_raw();
629 let old = Radium::swap(&self.inner, obj.cast().as_ptr(), Ordering::AcqRel);
630 unsafe { PyObjectRef::from_raw(NonNull::new_unchecked(old.cast())) }
631 }
632
633 pub fn swap_to_temporary_refs(&self, obj: PyObjectRef, vm: &VirtualMachine) {
634 let old = unsafe { self.swap(obj) };
635 if let Some(frame) = vm.current_frame() {
636 frame.iframe().cold().temporary_refs.lock().push(old);
637 }
638 }
639}
640
641impl From<Option<PyObjectRef>> for PyAtomicRef<Option<PyObject>> {
642 fn from(obj: Option<PyObjectRef>) -> Self {
643 let val = obj.map_or(null_mut(), |x| x.into_raw().as_ptr().cast());
644 Self {
645 inner: Radium::new(val),
646 _phantom: Default::default(),
647 }
648 }
649}
650
651impl PyAtomicRef<Option<PyObject>> {
652 pub(crate) const fn new_empty() -> Self {
654 Self {
655 inner: {
656 #[cfg(feature = "threading")]
657 {
658 core::sync::atomic::AtomicPtr::new(null_mut())
659 }
660 #[cfg(not(feature = "threading"))]
661 {
662 core::cell::Cell::new(null_mut())
663 }
664 },
665 _phantom: PhantomData,
666 }
667 }
668
669 pub(crate) fn load_ptr(&self) -> *mut PyObject {
674 cell_load_ptr(&self.inner)
675 }
676
677 pub fn load_owned(&self) -> Option<PyObjectRef> {
685 cell_load_owned(&self.inner)
686 }
687
688 pub(crate) fn store(&self, value: Option<PyObjectRef>) -> Option<PyObjectRef> {
690 cell_store(&self.inner, value)
691 }
692
693 pub(crate) fn compare_exchange_empty(&self, value: PyObjectRef) -> Result<(), PyObjectRef> {
697 cell_compare_exchange_empty(&self.inner, value)
698 }
699
700 pub fn deref(&self) -> Option<&PyObject> {
701 self.deref_ordering(Ordering::Relaxed)
702 }
703
704 pub fn deref_ordering(&self, ordering: Ordering) -> Option<&PyObject> {
705 unsafe { self.inner.load(ordering).cast::<PyObject>().as_ref() }
706 }
707
708 #[must_use]
712 pub unsafe fn swap(&self, obj: Option<PyObjectRef>) -> Option<PyObjectRef> {
713 let val = obj.map_or(null_mut(), |x| x.into_raw().as_ptr().cast());
714 let old = Radium::swap(&self.inner, val, Ordering::AcqRel);
715 unsafe { NonNull::new(old.cast::<PyObject>()).map(|x| PyObjectRef::from_raw(x)) }
716 }
717
718 pub fn swap_to_temporary_refs(&self, obj: Option<PyObjectRef>, vm: &VirtualMachine) {
719 let Some(old) = (unsafe { self.swap(obj) }) else {
720 return;
721 };
722 if let Some(frame) = vm.current_frame() {
723 frame.iframe().cold().temporary_refs.lock().push(old);
724 }
725 }
726}
727
728#[repr(transparent)]
734pub struct PyObjectCell(PyAtomicRef<Option<PyObject>>);
735
736impl From<Option<PyObjectRef>> for PyObjectCell {
737 fn from(value: Option<PyObjectRef>) -> Self {
738 Self(value.into())
739 }
740}
741
742impl PyObjectCell {
743 #[inline]
745 pub fn load_owned(&self) -> Option<PyObjectRef> {
746 self.0.load_owned()
747 }
748
749 #[inline]
751 pub fn store(&self, value: Option<PyObjectRef>) -> Option<PyObjectRef> {
752 self.0.store(value)
753 }
754}
755
756impl fmt::Debug for PyObjectCell {
757 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
758 f.debug_tuple("PyObjectCell")
759 .field(&self.load_owned())
760 .finish()
761 }
762}
763
764unsafe impl Traverse for PyObjectCell {
767 #[inline]
768 fn traverse(&self, traverse_fn: &mut TraverseFn<'_>) {
769 self.0.traverse(traverse_fn);
770 }
771}
772
773const _: () = assert!(
775 core::mem::size_of::<PyObjectCell>() == core::mem::size_of::<*mut PyObject>()
776 && core::mem::align_of::<PyObjectCell>() == core::mem::align_of::<*mut PyObject>()
777);
778
779pub struct PyAtomicBorrow {
783 inner: PyAtomic<*mut u8>,
784}
785
786unsafe impl Send for PyAtomicBorrow {}
789unsafe impl Sync for PyAtomicBorrow {}
790
791impl PyAtomicBorrow {
792 #[must_use]
793 pub fn new() -> Self {
794 Self {
795 inner: Radium::new(null_mut()),
796 }
797 }
798
799 pub fn store(&self, obj: &PyObject) {
800 let ptr = obj as *const PyObject as *mut u8;
801 Radium::store(&self.inner, ptr, Ordering::Relaxed);
802 }
803
804 pub fn load(&self) -> Option<&PyObject> {
805 let ptr = Radium::load(&self.inner, Ordering::Relaxed);
806 if ptr.is_null() {
807 None
808 } else {
809 Some(unsafe { &*(ptr as *const PyObject) })
810 }
811 }
812
813 pub fn clear(&self) {
814 Radium::store(&self.inner, null_mut(), Ordering::Relaxed);
815 }
816
817 pub fn to_owned(&self) -> Option<PyObjectRef> {
818 self.load().map(|obj| obj.to_owned())
819 }
820}
821
822impl Default for PyAtomicBorrow {
823 fn default() -> Self {
824 Self::new()
825 }
826}
827
828impl fmt::Debug for PyAtomicBorrow {
829 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
830 write!(
831 f,
832 "PyAtomicBorrow({:?})",
833 Radium::load(&self.inner, Ordering::Relaxed)
834 )
835 }
836}
837
838pub trait AsObject
839where
840 Self: Borrow<PyObject>,
841{
842 #[inline(always)]
843 fn as_object(&self) -> &PyObject {
844 self.borrow()
845 }
846
847 #[inline(always)]
848 fn get_id(&self) -> usize {
849 self.as_object().unique_id()
850 }
851
852 #[inline(always)]
853 fn is<T>(&self, other: &T) -> bool
854 where
855 T: AsObject,
856 {
857 self.get_id() == other.get_id()
858 }
859
860 #[inline(always)]
861 fn class(&self) -> &Py<PyType> {
862 self.as_object().class()
863 }
864
865 fn get_class_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
866 self.class().get_attr(attr_name)
867 }
868
869 #[inline]
872 fn fast_isinstance(&self, cls: &Py<PyType>) -> bool {
873 self.class().fast_issubclass(cls)
874 }
875}
876
877impl<T> AsObject for T where T: Borrow<PyObject> {}
878
879impl PyObject {
880 #[inline(always)]
881 fn unique_id(&self) -> usize {
882 self as *const Self as usize
883 }
884}
885
886impl<T: PyPayload> ToPyObject for PyRef<T> {
894 #[inline(always)]
895 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
896 self.into()
897 }
898}
899
900impl ToPyObject for PyObjectRef {
901 #[inline(always)]
902 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
903 self
904 }
905}
906
907impl ToPyObject for &PyObject {
908 #[inline(always)]
909 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
910 self.to_owned()
911 }
912}
913
914impl<T> ToPyObject for T
917where
918 T: PyPayload + core::fmt::Debug + Sized,
919{
920 #[inline(always)]
921 fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
922 PyPayload::into_pyobject(self, vm)
923 }
924}
925
926impl<T> ToPyResult for T
927where
928 T: ToPyObject,
929{
930 #[inline(always)]
931 fn to_pyresult(self, vm: &VirtualMachine) -> PyResult {
932 Ok(self.to_pyobject(vm))
933 }
934}
935
936impl<T, E> ToPyResult for Result<T, E>
937where
938 T: ToPyObject,
939 E: IntoPyException,
940{
941 #[inline(always)]
942 fn to_pyresult(self, vm: &VirtualMachine) -> PyResult {
943 self.map(|res| T::to_pyobject(res, vm))
944 .map_err(|e| E::into_pyexception(e, vm))
945 }
946}
947
948impl IntoPyException for PyBaseExceptionRef {
949 #[inline(always)]
950 fn into_pyexception(self, _vm: &VirtualMachine) -> PyBaseExceptionRef {
951 self
952 }
953}
954
955#[cfg(test)]
956mod tests {
957 use super::*;
958
959 #[test]
960 fn object_cell_snapshots_survive_replacement_and_clear() {
961 crate::Interpreter::without_stdlib(Default::default()).enter(|vm| {
962 let cell = PyObjectCell::from(Some(vm.ctx.new_bytes(vec![1, 2, 3]).into()));
963 let first = cell.load_owned().unwrap();
964 let previous = cell.store(Some(vm.ctx.new_bytes(vec![4, 5, 6]).into()));
965 assert!(previous.as_ref().unwrap().is(&first));
966 drop(previous);
967 assert_eq!(first.strong_count(), 1);
968 assert_eq!(
969 first
970 .downcast_ref::<crate::builtins::PyBytes>()
971 .unwrap()
972 .as_bytes(),
973 &[1, 2, 3]
974 );
975
976 let second = cell.load_owned().unwrap();
977 let previous = cell.store(None);
978 assert!(previous.as_ref().unwrap().is(&second));
979 drop(previous);
980 assert!(cell.load_owned().is_none());
981 assert!(cell.store(None).is_none());
982 assert_eq!(second.strong_count(), 1);
983 assert_eq!(
984 second
985 .downcast_ref::<crate::builtins::PyBytes>()
986 .unwrap()
987 .as_bytes(),
988 &[4, 5, 6]
989 );
990 });
991 }
992
993 #[test]
994 fn object_cell_traverses_current_reference_once_without_cloning() {
995 crate::Interpreter::without_stdlib(Default::default()).enter(|vm| {
996 let cell = PyObjectCell::from(None);
997 cell.traverse(&mut |_| panic!("empty cell owns no edge"));
998 let value: PyObjectRef = vm.ctx.new_bytes(vec![1, 2, 3]).into();
999 assert!(cell.store(Some(value.clone())).is_none());
1000 let references = value.strong_count();
1001 let mut edges = 0;
1002 cell.traverse(&mut |child| {
1003 assert!(child.is(&value));
1004 assert_eq!(value.strong_count(), references);
1005 edges += 1;
1006 });
1007 assert_eq!(edges, 1);
1008 drop(cell.store(None));
1009 cell.traverse(&mut |_| panic!("cleared cell owns no edge"));
1010 });
1011 }
1012}