1use super::{
2 core::{Py, PyObject, PyObjectRef, PyRef},
3 payload::PyPayload,
4};
5use crate::common::{
6 atomic::{Ordering, PyAtomic, Radium},
7 lock::PyRwLockReadGuard,
8};
9use crate::{
10 VirtualMachine,
11 builtins::{PyBaseExceptionRef, PyStrInterned, PyType},
12 convert::{IntoPyException, ToPyObject, ToPyResult, TryFromObject},
13 vm::Context,
14};
15use alloc::fmt;
16
17use core::{
18 borrow::Borrow,
19 marker::PhantomData,
20 ops::Deref,
21 ptr::{NonNull, null_mut},
22};
23
24pub type PyResult<T = PyObjectRef> = Result<T, PyBaseExceptionRef>; impl<T: fmt::Display> fmt::Display for PyRef<T>
44where
45 T: PyPayload + fmt::Display,
46{
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 fmt::Display::fmt(&**self, f)
49 }
50}
51
52impl<T: fmt::Display> fmt::Display for Py<T>
53where
54 T: PyPayload + fmt::Display,
55{
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 fmt::Display::fmt(&**self, f)
58 }
59}
60
61#[repr(transparent)]
62pub struct PyExact<T> {
63 inner: Py<T>,
64}
65
66impl<T: PyPayload> PyExact<T> {
67 #[inline(always)]
70 pub const unsafe fn ref_unchecked(r: &Py<T>) -> &Self {
71 unsafe { &*(r as *const _ as *const Self) }
72 }
73}
74
75impl<T: PyPayload> Deref for PyExact<T> {
76 type Target = Py<T>;
77
78 #[inline(always)]
79 fn deref(&self) -> &Py<T> {
80 &self.inner
81 }
82}
83
84impl<T: PyPayload> Borrow<PyObject> for PyExact<T> {
85 #[inline(always)]
86 fn borrow(&self) -> &PyObject {
87 self.inner.borrow()
88 }
89}
90
91impl<T: PyPayload> AsRef<PyObject> for PyExact<T> {
92 #[inline(always)]
93 fn as_ref(&self) -> &PyObject {
94 self.inner.as_ref()
95 }
96}
97
98impl<T: PyPayload> Borrow<Py<T>> for PyExact<T> {
99 #[inline(always)]
100 fn borrow(&self) -> &Py<T> {
101 &self.inner
102 }
103}
104
105impl<T: PyPayload> AsRef<Py<T>> for PyExact<T> {
106 #[inline(always)]
107 fn as_ref(&self) -> &Py<T> {
108 &self.inner
109 }
110}
111
112impl<T: PyPayload> alloc::borrow::ToOwned for PyExact<T> {
113 type Owned = PyRefExact<T>;
114
115 fn to_owned(&self) -> Self::Owned {
116 let owned = self.inner.to_owned();
117 unsafe { PyRefExact::new_unchecked(owned) }
118 }
119}
120
121impl<T: PyPayload> PyRef<T> {
122 pub fn into_exact_or(
123 self,
124 ctx: &Context,
125 f: impl FnOnce(Self) -> PyRefExact<T>,
126 ) -> PyRefExact<T> {
127 if self.class().is(T::class(ctx)) {
128 unsafe { PyRefExact::new_unchecked(self) }
129 } else {
130 f(self)
131 }
132 }
133}
134
135#[derive(Debug)]
137#[repr(transparent)]
138pub struct PyRefExact<T: PyPayload> {
139 inner: PyRef<T>,
140}
141
142impl<T: PyPayload> PyRefExact<T> {
143 pub const unsafe fn new_unchecked(obj: PyRef<T>) -> Self {
146 Self { inner: obj }
147 }
148
149 pub fn into_pyref(self) -> PyRef<T> {
150 self.inner
151 }
152}
153
154impl<T: PyPayload> Clone for PyRefExact<T> {
155 fn clone(&self) -> Self {
156 let inner = self.inner.clone();
157 Self { inner }
158 }
159}
160
161impl<T: PyPayload> TryFromObject for PyRefExact<T> {
162 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
163 let target_cls = T::class(&vm.ctx);
164 let cls = obj.class();
165 if cls.is(target_cls) {
166 let obj = obj
167 .downcast()
168 .map_err(|obj| vm.new_downcast_runtime_error(target_cls, &obj))?;
169 Ok(Self { inner: obj })
170 } else if cls.fast_issubclass(target_cls) {
171 Err(vm.new_type_error(format!(
172 "Expected an exact instance of '{}', not a subclass '{}'",
173 target_cls.name(),
174 cls.name(),
175 )))
176 } else {
177 Err(vm.new_type_error(format!(
178 "Expected type '{}', not '{}'",
179 target_cls.name(),
180 cls.name(),
181 )))
182 }
183 }
184}
185
186impl<T: PyPayload> Deref for PyRefExact<T> {
187 type Target = PyExact<T>;
188
189 #[inline(always)]
190 fn deref(&self) -> &PyExact<T> {
191 unsafe { PyExact::ref_unchecked(self.inner.deref()) }
192 }
193}
194
195impl<T: PyPayload> Borrow<PyObject> for PyRefExact<T> {
196 #[inline(always)]
197 fn borrow(&self) -> &PyObject {
198 self.inner.borrow()
199 }
200}
201
202impl<T: PyPayload> AsRef<PyObject> for PyRefExact<T> {
203 #[inline(always)]
204 fn as_ref(&self) -> &PyObject {
205 self.inner.as_ref()
206 }
207}
208
209impl<T: PyPayload> Borrow<Py<T>> for PyRefExact<T> {
210 #[inline(always)]
211 fn borrow(&self) -> &Py<T> {
212 self.inner.borrow()
213 }
214}
215
216impl<T: PyPayload> AsRef<Py<T>> for PyRefExact<T> {
217 #[inline(always)]
218 fn as_ref(&self) -> &Py<T> {
219 self.inner.as_ref()
220 }
221}
222
223impl<T: PyPayload> Borrow<PyExact<T>> for PyRefExact<T> {
224 #[inline(always)]
225 fn borrow(&self) -> &PyExact<T> {
226 self
227 }
228}
229
230impl<T: PyPayload> AsRef<PyExact<T>> for PyRefExact<T> {
231 #[inline(always)]
232 fn as_ref(&self) -> &PyExact<T> {
233 self
234 }
235}
236
237impl<T: PyPayload> ToPyObject for PyRefExact<T> {
238 #[inline(always)]
239 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
240 self.inner.into()
241 }
242}
243
244pub struct PyAtomicRef<T> {
245 inner: PyAtomic<*mut u8>,
246 _phantom: PhantomData<T>,
247}
248
249impl<T> Drop for PyAtomicRef<T> {
250 fn drop(&mut self) {
251 unsafe {
254 let ptr = Radium::swap(&self.inner, null_mut(), Ordering::Relaxed);
255 if let Some(ptr) = NonNull::<PyObject>::new(ptr.cast()) {
256 let _: PyObjectRef = PyObjectRef::from_raw(ptr);
257 }
258 }
259 }
260}
261
262cfg_if::cfg_if! {
263 if #[cfg(feature = "threading")] {
264 unsafe impl<T: Send + PyPayload> Send for PyAtomicRef<T> {}
265 unsafe impl<T: Sync + PyPayload> Sync for PyAtomicRef<T> {}
266 unsafe impl<T: Send + PyPayload> Send for PyAtomicRef<Option<T>> {}
267 unsafe impl<T: Sync + PyPayload> Sync for PyAtomicRef<Option<T>> {}
268 unsafe impl Send for PyAtomicRef<PyObject> {}
269 unsafe impl Sync for PyAtomicRef<PyObject> {}
270 unsafe impl Send for PyAtomicRef<Option<PyObject>> {}
271 unsafe impl Sync for PyAtomicRef<Option<PyObject>> {}
272 }
273}
274
275impl<T: fmt::Debug> fmt::Debug for PyAtomicRef<T> {
276 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 write!(f, "PyAtomicRef(")?;
278 unsafe {
279 self.inner
280 .load(Ordering::Relaxed)
281 .cast::<T>()
282 .as_ref()
283 .fmt(f)
284 }?;
285 write!(f, ")")
286 }
287}
288
289impl<T: PyPayload> From<PyRef<T>> for PyAtomicRef<T> {
290 fn from(pyref: PyRef<T>) -> Self {
291 let py = PyRef::leak(pyref);
292 let ptr = py as *const _ as *mut u8;
293 ptr.expose_provenance();
296 Self {
297 inner: Radium::new(ptr),
298 _phantom: Default::default(),
299 }
300 }
301}
302
303impl<T: PyPayload> Deref for PyAtomicRef<T> {
304 type Target = Py<T>;
305
306 fn deref(&self) -> &Self::Target {
307 unsafe {
308 self.inner
309 .load(Ordering::Relaxed)
310 .cast::<Py<T>>()
311 .as_ref()
312 .unwrap_unchecked()
313 }
314 }
315}
316
317impl<T: PyPayload> PyAtomicRef<T> {
318 #[inline(always)]
322 pub(super) fn load_raw(&self) -> *const Py<T> {
323 self.inner.load(Ordering::Relaxed).cast::<Py<T>>()
324 }
325
326 #[must_use]
330 pub unsafe fn swap(&self, pyref: PyRef<T>) -> PyRef<T> {
331 let py = PyRef::leak(pyref) as *const Py<T> as *mut _;
332 let old = Radium::swap(&self.inner, py, Ordering::AcqRel);
333 unsafe { PyRef::from_raw(old.cast()) }
334 }
335
336 pub fn swap_to_temporary_refs(&self, pyref: PyRef<T>, vm: &VirtualMachine) {
337 let old = unsafe { self.swap(pyref) };
338 if let Some(frame) = vm.current_frame() {
339 frame.temporary_refs.lock().push(old.into());
340 }
341 }
342}
343
344impl<T: PyPayload> From<Option<PyRef<T>>> for PyAtomicRef<Option<T>> {
345 fn from(opt_ref: Option<PyRef<T>>) -> Self {
346 let val = opt_ref
347 .map(|x| PyRef::leak(x) as *const Py<T> as *mut _)
348 .unwrap_or(null_mut());
349 Self {
350 inner: Radium::new(val),
351 _phantom: Default::default(),
352 }
353 }
354}
355
356impl<T: PyPayload> PyAtomicRef<Option<T>> {
357 pub fn deref(&self) -> Option<&Py<T>> {
358 self.deref_ordering(Ordering::Relaxed)
359 }
360
361 pub fn deref_ordering(&self, ordering: Ordering) -> Option<&Py<T>> {
362 unsafe { self.inner.load(ordering).cast::<Py<T>>().as_ref() }
363 }
364
365 pub fn to_owned(&self) -> Option<PyRef<T>> {
366 self.to_owned_ordering(Ordering::Relaxed)
367 }
368
369 pub fn to_owned_ordering(&self, ordering: Ordering) -> Option<PyRef<T>> {
370 self.deref_ordering(ordering).map(|x| x.to_owned())
371 }
372
373 #[must_use]
377 pub unsafe fn swap(&self, opt_ref: Option<PyRef<T>>) -> Option<PyRef<T>> {
378 let val = opt_ref
379 .map(|x| PyRef::leak(x) as *const Py<T> as *mut _)
380 .unwrap_or(null_mut());
381 let old = Radium::swap(&self.inner, val, Ordering::AcqRel);
382 unsafe { old.cast::<Py<T>>().as_ref().map(|x| PyRef::from_raw(x)) }
383 }
384
385 pub fn swap_to_temporary_refs(&self, opt_ref: Option<PyRef<T>>, vm: &VirtualMachine) {
386 let Some(old) = (unsafe { self.swap(opt_ref) }) else {
387 return;
388 };
389 if let Some(frame) = vm.current_frame() {
390 frame.temporary_refs.lock().push(old.into());
391 }
392 }
393}
394
395impl From<PyObjectRef> for PyAtomicRef<PyObject> {
396 fn from(obj: PyObjectRef) -> Self {
397 let obj = obj.into_raw();
398 Self {
399 inner: Radium::new(obj.cast().as_ptr()),
400 _phantom: Default::default(),
401 }
402 }
403}
404
405impl Deref for PyAtomicRef<PyObject> {
406 type Target = PyObject;
407
408 fn deref(&self) -> &Self::Target {
409 unsafe {
410 self.inner
411 .load(Ordering::Relaxed)
412 .cast::<PyObject>()
413 .as_ref()
414 .unwrap_unchecked()
415 }
416 }
417}
418
419impl PyAtomicRef<PyObject> {
420 #[must_use]
424 pub unsafe fn swap(&self, obj: PyObjectRef) -> PyObjectRef {
425 let obj = obj.into_raw();
426 let old = Radium::swap(&self.inner, obj.cast().as_ptr(), Ordering::AcqRel);
427 unsafe { PyObjectRef::from_raw(NonNull::new_unchecked(old.cast())) }
428 }
429
430 pub fn swap_to_temporary_refs(&self, obj: PyObjectRef, vm: &VirtualMachine) {
431 let old = unsafe { self.swap(obj) };
432 if let Some(frame) = vm.current_frame() {
433 frame.temporary_refs.lock().push(old);
434 }
435 }
436}
437
438impl From<Option<PyObjectRef>> for PyAtomicRef<Option<PyObject>> {
439 fn from(obj: Option<PyObjectRef>) -> Self {
440 let val = obj
441 .map(|x| x.into_raw().as_ptr().cast())
442 .unwrap_or(null_mut());
443 Self {
444 inner: Radium::new(val),
445 _phantom: Default::default(),
446 }
447 }
448}
449
450impl PyAtomicRef<Option<PyObject>> {
451 pub fn deref(&self) -> Option<&PyObject> {
452 self.deref_ordering(Ordering::Relaxed)
453 }
454
455 pub fn deref_ordering(&self, ordering: Ordering) -> Option<&PyObject> {
456 unsafe { self.inner.load(ordering).cast::<PyObject>().as_ref() }
457 }
458
459 pub fn to_owned(&self) -> Option<PyObjectRef> {
460 self.to_owned_ordering(Ordering::Relaxed)
461 }
462
463 pub fn to_owned_ordering(&self, ordering: Ordering) -> Option<PyObjectRef> {
464 self.deref_ordering(ordering).map(|x| x.to_owned())
465 }
466
467 #[must_use]
471 pub unsafe fn swap(&self, obj: Option<PyObjectRef>) -> Option<PyObjectRef> {
472 let val = obj
473 .map(|x| x.into_raw().as_ptr().cast())
474 .unwrap_or(null_mut());
475 let old = Radium::swap(&self.inner, val, Ordering::AcqRel);
476 unsafe { NonNull::new(old.cast::<PyObject>()).map(|x| PyObjectRef::from_raw(x)) }
477 }
478
479 pub fn swap_to_temporary_refs(&self, obj: Option<PyObjectRef>, vm: &VirtualMachine) {
480 let Some(old) = (unsafe { self.swap(obj) }) else {
481 return;
482 };
483 if let Some(frame) = vm.current_frame() {
484 frame.temporary_refs.lock().push(old);
485 }
486 }
487}
488
489pub struct PyAtomicBorrow {
493 inner: PyAtomic<*mut u8>,
494}
495
496unsafe impl Send for PyAtomicBorrow {}
499unsafe impl Sync for PyAtomicBorrow {}
500
501impl PyAtomicBorrow {
502 pub fn new() -> Self {
503 Self {
504 inner: Radium::new(null_mut()),
505 }
506 }
507
508 pub fn store(&self, obj: &PyObject) {
509 let ptr = obj as *const PyObject as *mut u8;
510 Radium::store(&self.inner, ptr, Ordering::Relaxed);
511 }
512
513 pub fn load(&self) -> Option<&PyObject> {
514 let ptr = Radium::load(&self.inner, Ordering::Relaxed);
515 if ptr.is_null() {
516 None
517 } else {
518 Some(unsafe { &*(ptr as *const PyObject) })
519 }
520 }
521
522 pub fn clear(&self) {
523 Radium::store(&self.inner, null_mut(), Ordering::Relaxed);
524 }
525
526 pub fn to_owned(&self) -> Option<PyObjectRef> {
527 self.load().map(|obj| obj.to_owned())
528 }
529}
530
531impl Default for PyAtomicBorrow {
532 fn default() -> Self {
533 Self::new()
534 }
535}
536
537impl fmt::Debug for PyAtomicBorrow {
538 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
539 write!(
540 f,
541 "PyAtomicBorrow({:?})",
542 Radium::load(&self.inner, Ordering::Relaxed)
543 )
544 }
545}
546
547pub trait AsObject
548where
549 Self: Borrow<PyObject>,
550{
551 #[inline(always)]
552 fn as_object(&self) -> &PyObject {
553 self.borrow()
554 }
555
556 #[inline(always)]
557 fn get_id(&self) -> usize {
558 self.as_object().unique_id()
559 }
560
561 #[inline(always)]
562 fn is<T>(&self, other: &T) -> bool
563 where
564 T: AsObject,
565 {
566 self.get_id() == other.get_id()
567 }
568
569 #[inline(always)]
570 fn class(&self) -> &Py<PyType> {
571 self.as_object().class()
572 }
573
574 fn get_class_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
575 self.class().get_attr(attr_name)
576 }
577
578 #[inline]
581 fn fast_isinstance(&self, cls: &Py<PyType>) -> bool {
582 self.class().fast_issubclass(cls)
583 }
584}
585
586impl<T> AsObject for T where T: Borrow<PyObject> {}
587
588impl PyObject {
589 #[inline(always)]
590 fn unique_id(&self) -> usize {
591 self as *const Self as usize
592 }
593}
594
595#[allow(dead_code)]
606pub struct PyLease<'a, T: PyPayload> {
607 inner: PyRwLockReadGuard<'a, PyRef<T>>,
608}
609
610impl<T: PyPayload> PyLease<'_, T> {
611 #[inline(always)]
612 pub fn into_owned(self) -> PyRef<T> {
613 self.inner.clone()
614 }
615}
616
617impl<T: PyPayload> Borrow<PyObject> for PyLease<'_, T> {
618 #[inline(always)]
619 fn borrow(&self) -> &PyObject {
620 self.inner.as_ref()
621 }
622}
623
624impl<T: PyPayload> Deref for PyLease<'_, T> {
625 type Target = PyRef<T>;
626 #[inline(always)]
627 fn deref(&self) -> &Self::Target {
628 &self.inner
629 }
630}
631
632impl<T> fmt::Display for PyLease<'_, T>
633where
634 T: PyPayload + fmt::Display,
635{
636 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
637 fmt::Display::fmt(&**self, f)
638 }
639}
640
641impl<T: PyPayload> ToPyObject for PyRef<T> {
642 #[inline(always)]
643 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
644 self.into()
645 }
646}
647
648impl ToPyObject for PyObjectRef {
649 #[inline(always)]
650 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
651 self
652 }
653}
654
655impl ToPyObject for &PyObject {
656 #[inline(always)]
657 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
658 self.to_owned()
659 }
660}
661
662impl<T> ToPyObject for T
665where
666 T: PyPayload + core::fmt::Debug + Sized,
667{
668 #[inline(always)]
669 fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
670 PyPayload::into_pyobject(self, vm)
671 }
672}
673
674impl<T> ToPyResult for T
675where
676 T: ToPyObject,
677{
678 #[inline(always)]
679 fn to_pyresult(self, vm: &VirtualMachine) -> PyResult {
680 Ok(self.to_pyobject(vm))
681 }
682}
683
684impl<T, E> ToPyResult for Result<T, E>
685where
686 T: ToPyObject,
687 E: IntoPyException,
688{
689 #[inline(always)]
690 fn to_pyresult(self, vm: &VirtualMachine) -> PyResult {
691 self.map(|res| T::to_pyobject(res, vm))
692 .map_err(|e| E::into_pyexception(e, vm))
693 }
694}
695
696impl IntoPyException for PyBaseExceptionRef {
697 #[inline(always)]
698 fn into_pyexception(self, _vm: &VirtualMachine) -> PyBaseExceptionRef {
699 self
700 }
701}