1use super::{
2 core::{Py, PyObject, PyObjectRef, PyRef},
3 payload::{PyObjectPayload, PyPayload},
4};
5use crate::common::{
6 atomic::{Ordering, PyAtomic, Radium},
7 lock::PyRwLockReadGuard,
8};
9use crate::{
10 builtins::{PyBaseExceptionRef, PyStrInterned, PyType},
11 convert::{IntoPyException, ToPyObject, ToPyResult, TryFromObject},
12 vm::Context,
13 VirtualMachine,
14};
15use std::{borrow::Borrow, fmt, marker::PhantomData, ops::Deref, ptr::null_mut};
16
17pub type PyResult<T = PyObjectRef> = Result<T, PyBaseExceptionRef>; impl<T: fmt::Display> fmt::Display for PyRef<T>
37where
38 T: PyObjectPayload + fmt::Display,
39{
40 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
41 fmt::Display::fmt(&**self, f)
42 }
43}
44impl<T: fmt::Display> fmt::Display for Py<T>
45where
46 T: PyObjectPayload + fmt::Display,
47{
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: PyObjectPayload> {
55 inner: Py<T>,
56}
57
58impl<T: PyPayload> PyExact<T> {
59 #[inline(always)]
62 pub unsafe fn ref_unchecked(r: &Py<T>) -> &Self {
63 &*(r as *const _ as *const Self)
64 }
65}
66
67impl<T: PyPayload> Deref for PyExact<T> {
68 type Target = Py<T>;
69 #[inline(always)]
70 fn deref(&self) -> &Py<T> {
71 &self.inner
72 }
73}
74
75impl<T: PyObjectPayload> Borrow<PyObject> for PyExact<T> {
76 #[inline(always)]
77 fn borrow(&self) -> &PyObject {
78 self.inner.borrow()
79 }
80}
81
82impl<T: PyObjectPayload> AsRef<PyObject> for PyExact<T> {
83 #[inline(always)]
84 fn as_ref(&self) -> &PyObject {
85 self.inner.as_ref()
86 }
87}
88
89impl<T: PyObjectPayload> Borrow<Py<T>> for PyExact<T> {
90 #[inline(always)]
91 fn borrow(&self) -> &Py<T> {
92 &self.inner
93 }
94}
95
96impl<T: PyObjectPayload> AsRef<Py<T>> for PyExact<T> {
97 #[inline(always)]
98 fn as_ref(&self) -> &Py<T> {
99 &self.inner
100 }
101}
102
103impl<T: PyPayload> std::borrow::ToOwned for PyExact<T> {
104 type Owned = PyRefExact<T>;
105 fn to_owned(&self) -> Self::Owned {
106 let owned = self.inner.to_owned();
107 unsafe { PyRefExact::new_unchecked(owned) }
108 }
109}
110
111impl<T: PyPayload> PyRef<T> {
112 pub fn into_exact_or(
113 self,
114 ctx: &Context,
115 f: impl FnOnce(Self) -> PyRefExact<T>,
116 ) -> PyRefExact<T> {
117 if self.class().is(T::class(ctx)) {
118 unsafe { PyRefExact::new_unchecked(self) }
119 } else {
120 f(self)
121 }
122 }
123}
124
125#[derive(Debug)]
127#[repr(transparent)]
128pub struct PyRefExact<T: PyObjectPayload> {
129 inner: PyRef<T>,
130}
131
132impl<T: PyObjectPayload> PyRefExact<T> {
133 pub unsafe fn new_unchecked(obj: PyRef<T>) -> Self {
136 Self { inner: obj }
137 }
138
139 pub fn into_pyref(self) -> PyRef<T> {
140 self.inner
141 }
142}
143
144impl<T: PyObjectPayload> Clone for PyRefExact<T> {
145 fn clone(&self) -> Self {
146 let inner = self.inner.clone();
147 Self { inner }
148 }
149}
150
151impl<T: PyPayload> TryFromObject for PyRefExact<T> {
152 fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
153 let target_cls = T::class(&vm.ctx);
154 let cls = obj.class();
155 if cls.is(target_cls) {
156 let obj = obj
157 .downcast()
158 .map_err(|obj| vm.new_downcast_runtime_error(target_cls, &obj))?;
159 Ok(Self { inner: obj })
160 } else if cls.fast_issubclass(target_cls) {
161 Err(vm.new_type_error(format!(
162 "Expected an exact instance of '{}', not a subclass '{}'",
163 target_cls.name(),
164 cls.name(),
165 )))
166 } else {
167 Err(vm.new_type_error(format!(
168 "Expected type '{}', not '{}'",
169 target_cls.name(),
170 cls.name(),
171 )))
172 }
173 }
174}
175
176impl<T: PyPayload> Deref for PyRefExact<T> {
177 type Target = PyExact<T>;
178 #[inline(always)]
179 fn deref(&self) -> &PyExact<T> {
180 unsafe { PyExact::ref_unchecked(self.inner.deref()) }
181 }
182}
183
184impl<T: PyObjectPayload> Borrow<PyObject> for PyRefExact<T> {
185 #[inline(always)]
186 fn borrow(&self) -> &PyObject {
187 self.inner.borrow()
188 }
189}
190
191impl<T: PyObjectPayload> AsRef<PyObject> for PyRefExact<T> {
192 #[inline(always)]
193 fn as_ref(&self) -> &PyObject {
194 self.inner.as_ref()
195 }
196}
197
198impl<T: PyObjectPayload> Borrow<Py<T>> for PyRefExact<T> {
199 #[inline(always)]
200 fn borrow(&self) -> &Py<T> {
201 self.inner.borrow()
202 }
203}
204
205impl<T: PyObjectPayload> AsRef<Py<T>> for PyRefExact<T> {
206 #[inline(always)]
207 fn as_ref(&self) -> &Py<T> {
208 self.inner.as_ref()
209 }
210}
211
212impl<T: PyPayload> Borrow<PyExact<T>> for PyRefExact<T> {
213 #[inline(always)]
214 fn borrow(&self) -> &PyExact<T> {
215 self
216 }
217}
218
219impl<T: PyPayload> AsRef<PyExact<T>> for PyRefExact<T> {
220 #[inline(always)]
221 fn as_ref(&self) -> &PyExact<T> {
222 self
223 }
224}
225
226impl<T: PyPayload> ToPyObject for PyRefExact<T> {
227 #[inline(always)]
228 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
229 self.inner.into()
230 }
231}
232
233pub struct PyAtomicRef<T> {
234 inner: PyAtomic<*mut u8>,
235 _phantom: PhantomData<T>,
236}
237
238cfg_if::cfg_if! {
239 if #[cfg(feature = "threading")] {
240 unsafe impl<T: Send + PyObjectPayload> Send for PyAtomicRef<T> {}
241 unsafe impl<T: Sync + PyObjectPayload> Sync for PyAtomicRef<T> {}
242 unsafe impl<T: Send + PyObjectPayload> Send for PyAtomicRef<Option<T>> {}
243 unsafe impl<T: Sync + PyObjectPayload> Sync for PyAtomicRef<Option<T>> {}
244 unsafe impl Send for PyAtomicRef<PyObject> {}
245 unsafe impl Sync for PyAtomicRef<PyObject> {}
246 unsafe impl Send for PyAtomicRef<Option<PyObject>> {}
247 unsafe impl Sync for PyAtomicRef<Option<PyObject>> {}
248 }
249}
250
251impl<T: fmt::Debug> fmt::Debug for PyAtomicRef<T> {
252 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
253 write!(f, "PyAtomicRef(")?;
254 unsafe {
255 self.inner
256 .load(Ordering::Relaxed)
257 .cast::<T>()
258 .as_ref()
259 .fmt(f)
260 }?;
261 write!(f, ")")
262 }
263}
264
265impl<T: PyObjectPayload> From<PyRef<T>> for PyAtomicRef<T> {
266 fn from(pyref: PyRef<T>) -> Self {
267 let py = PyRef::leak(pyref);
268 Self {
269 inner: Radium::new(py as *const _ as *mut _),
270 _phantom: Default::default(),
271 }
272 }
273}
274
275impl<T: PyObjectPayload> Deref for PyAtomicRef<T> {
276 type Target = Py<T>;
277
278 fn deref(&self) -> &Self::Target {
279 unsafe {
280 self.inner
281 .load(Ordering::Relaxed)
282 .cast::<Py<T>>()
283 .as_ref()
284 .unwrap_unchecked()
285 }
286 }
287}
288
289impl<T: PyObjectPayload> PyAtomicRef<T> {
290 #[must_use]
294 pub unsafe fn swap(&self, pyref: PyRef<T>) -> PyRef<T> {
295 let py = PyRef::leak(pyref) as *const Py<T> as *mut _;
296 let old = Radium::swap(&self.inner, py, Ordering::AcqRel);
297 PyRef::from_raw(old.cast())
298 }
299
300 pub fn swap_to_temporary_refs(&self, pyref: PyRef<T>, vm: &VirtualMachine) {
301 let old = unsafe { self.swap(pyref) };
302 if let Some(frame) = vm.current_frame() {
303 frame.temporary_refs.lock().push(old.into());
304 }
305 }
306}
307
308impl<T: PyObjectPayload> From<Option<PyRef<T>>> for PyAtomicRef<Option<T>> {
309 fn from(opt_ref: Option<PyRef<T>>) -> Self {
310 let val = opt_ref
311 .map(|x| PyRef::leak(x) as *const Py<T> as *mut _)
312 .unwrap_or(null_mut());
313 Self {
314 inner: Radium::new(val),
315 _phantom: Default::default(),
316 }
317 }
318}
319
320impl<T: PyObjectPayload> PyAtomicRef<Option<T>> {
321 pub fn deref(&self) -> Option<&Py<T>> {
322 unsafe { self.inner.load(Ordering::Relaxed).cast::<Py<T>>().as_ref() }
323 }
324
325 pub fn to_owned(&self) -> Option<PyRef<T>> {
326 self.deref().map(|x| x.to_owned())
327 }
328
329 #[must_use]
333 pub unsafe fn swap(&self, opt_ref: Option<PyRef<T>>) -> Option<PyRef<T>> {
334 let val = opt_ref
335 .map(|x| PyRef::leak(x) as *const Py<T> as *mut _)
336 .unwrap_or(null_mut());
337 let old = Radium::swap(&self.inner, val, Ordering::AcqRel);
338 unsafe { old.cast::<Py<T>>().as_ref().map(|x| PyRef::from_raw(x)) }
339 }
340
341 pub fn swap_to_temporary_refs(&self, opt_ref: Option<PyRef<T>>, vm: &VirtualMachine) {
342 let Some(old) = (unsafe { self.swap(opt_ref) }) else {
343 return;
344 };
345 if let Some(frame) = vm.current_frame() {
346 frame.temporary_refs.lock().push(old.into());
347 }
348 }
349}
350
351impl From<PyObjectRef> for PyAtomicRef<PyObject> {
352 fn from(obj: PyObjectRef) -> Self {
353 let obj = obj.into_raw();
354 Self {
355 inner: Radium::new(obj as *mut _),
356 _phantom: Default::default(),
357 }
358 }
359}
360
361impl Deref for PyAtomicRef<PyObject> {
362 type Target = PyObject;
363
364 fn deref(&self) -> &Self::Target {
365 unsafe {
366 self.inner
367 .load(Ordering::Relaxed)
368 .cast::<PyObject>()
369 .as_ref()
370 .unwrap_unchecked()
371 }
372 }
373}
374
375impl PyAtomicRef<PyObject> {
376 #[must_use]
380 pub unsafe fn swap(&self, obj: PyObjectRef) -> PyObjectRef {
381 let obj = obj.into_raw();
382 let old = Radium::swap(&self.inner, obj as *mut _, Ordering::AcqRel);
383 PyObjectRef::from_raw(old as _)
384 }
385
386 pub fn swap_to_temporary_refs(&self, obj: PyObjectRef, vm: &VirtualMachine) {
387 let old = unsafe { self.swap(obj) };
388 if let Some(frame) = vm.current_frame() {
389 frame.temporary_refs.lock().push(old);
390 }
391 }
392}
393
394impl From<Option<PyObjectRef>> for PyAtomicRef<Option<PyObject>> {
395 fn from(obj: Option<PyObjectRef>) -> Self {
396 let val = obj.map(|x| x.into_raw() as *mut _).unwrap_or(null_mut());
397 Self {
398 inner: Radium::new(val),
399 _phantom: Default::default(),
400 }
401 }
402}
403
404impl PyAtomicRef<Option<PyObject>> {
405 pub fn deref(&self) -> Option<&PyObject> {
406 unsafe {
407 self.inner
408 .load(Ordering::Relaxed)
409 .cast::<PyObject>()
410 .as_ref()
411 }
412 }
413
414 pub fn to_owned(&self) -> Option<PyObjectRef> {
415 self.deref().map(|x| x.to_owned())
416 }
417
418 #[must_use]
422 pub unsafe fn swap(&self, obj: Option<PyObjectRef>) -> Option<PyObjectRef> {
423 let val = obj.map(|x| x.into_raw() as *mut _).unwrap_or(null_mut());
424 let old = Radium::swap(&self.inner, val, Ordering::AcqRel);
425 old.cast::<PyObject>()
426 .as_ref()
427 .map(|x| PyObjectRef::from_raw(x))
428 }
429
430 pub fn swap_to_temporary_refs(&self, obj: Option<PyObjectRef>, vm: &VirtualMachine) {
431 let Some(old) = (unsafe { self.swap(obj) }) else {
432 return;
433 };
434 if let Some(frame) = vm.current_frame() {
435 frame.temporary_refs.lock().push(old);
436 }
437 }
438}
439
440pub trait AsObject
441where
442 Self: Borrow<PyObject>,
443{
444 #[inline(always)]
445 fn as_object(&self) -> &PyObject {
446 self.borrow()
447 }
448
449 #[inline(always)]
450 fn get_id(&self) -> usize {
451 self.as_object().unique_id()
452 }
453
454 #[inline(always)]
455 fn is<T>(&self, other: &T) -> bool
456 where
457 T: AsObject,
458 {
459 self.get_id() == other.get_id()
460 }
461
462 #[inline(always)]
463 fn class(&self) -> &Py<PyType> {
464 self.as_object().class()
465 }
466
467 fn get_class_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
468 self.class().get_attr(attr_name)
469 }
470
471 #[inline]
474 fn fast_isinstance(&self, cls: &Py<PyType>) -> bool {
475 self.class().fast_issubclass(cls)
476 }
477}
478
479impl<T> AsObject for T where T: Borrow<PyObject> {}
480
481impl PyObject {
482 #[inline(always)]
483 fn unique_id(&self) -> usize {
484 self as *const PyObject as usize
485 }
486}
487
488#[allow(dead_code)]
499pub struct PyLease<'a, T: PyObjectPayload> {
500 inner: PyRwLockReadGuard<'a, PyRef<T>>,
501}
502
503impl<'a, T: PyObjectPayload + PyPayload> PyLease<'a, T> {
504 #[inline(always)]
505 pub fn into_owned(self) -> PyRef<T> {
506 self.inner.clone()
507 }
508}
509
510impl<'a, T: PyObjectPayload + PyPayload> Borrow<PyObject> for PyLease<'a, T> {
511 #[inline(always)]
512 fn borrow(&self) -> &PyObject {
513 self.inner.as_ref()
514 }
515}
516
517impl<'a, T: PyObjectPayload + PyPayload> Deref for PyLease<'a, T> {
518 type Target = PyRef<T>;
519 #[inline(always)]
520 fn deref(&self) -> &Self::Target {
521 &self.inner
522 }
523}
524
525impl<'a, T> fmt::Display for PyLease<'a, T>
526where
527 T: PyPayload + fmt::Display,
528{
529 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
530 fmt::Display::fmt(&**self, f)
531 }
532}
533
534impl<T: PyObjectPayload> ToPyObject for PyRef<T> {
535 #[inline(always)]
536 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
537 self.into()
538 }
539}
540
541impl ToPyObject for PyObjectRef {
542 #[inline(always)]
543 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
544 self
545 }
546}
547
548impl ToPyObject for &PyObject {
549 #[inline(always)]
550 fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
551 self.to_owned()
552 }
553}
554
555impl<T> ToPyObject for T
558where
559 T: PyPayload + Sized,
560{
561 #[inline(always)]
562 fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef {
563 PyPayload::into_pyobject(self, vm)
564 }
565}
566
567impl<T> ToPyResult for T
568where
569 T: ToPyObject,
570{
571 #[inline(always)]
572 fn to_pyresult(self, vm: &VirtualMachine) -> PyResult {
573 Ok(self.to_pyobject(vm))
574 }
575}
576
577impl<T, E> ToPyResult for Result<T, E>
578where
579 T: ToPyObject,
580 E: IntoPyException,
581{
582 #[inline(always)]
583 fn to_pyresult(self, vm: &VirtualMachine) -> PyResult {
584 self.map(|res| T::to_pyobject(res, vm))
585 .map_err(|e| E::into_pyexception(e, vm))
586 }
587}
588
589impl IntoPyException for PyBaseExceptionRef {
590 #[inline(always)]
591 fn into_pyexception(self, _vm: &VirtualMachine) -> PyBaseExceptionRef {
592 self
593 }
594}