1use super::{PyStr, PyStrInterned, PyTuple, PyType};
2use crate::{
3 AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
4 builtins::{PyTypeRef, builtin_func::PyNativeMethod, type_},
5 class::PyClassImpl,
6 common::hash::PyHash,
7 convert::{ToPyObject, ToPyResult},
8 function::{
9 Callee, FuncArgs, ItemDoc, PyMethodDef, PyMethodFlags, PySetterValue, PySsize, plain_doc,
10 },
11 protocol::{PyNumberBinaryFunc, PyNumberTernaryFunc, PyNumberUnaryFunc},
12 types::{
13 Callable, Comparable, DelFunc, DescrGetFunc, DescrSetFunc, GenericMethod, GetDescriptor,
14 GetattroFunc, HashFunc, Hashable, InitFunc, IterFunc, IterNextFunc, MapAssSubscriptFunc,
15 MapLenFunc, MapSubscriptFunc, PyComparisonOp, Representable, RichCompareFunc,
16 SeqAssItemFunc, SeqConcatFunc, SeqContainsFunc, SeqItemFunc, SeqLenFunc, SeqRepeatFunc,
17 SetattroFunc, StringifyFunc,
18 },
19};
20use core::mem::{align_of, size_of};
21use rustpython_common::lock::PyRwLock;
22
23#[derive(Debug)]
24pub struct PyDescriptor {
25 pub typ: &'static Py<PyType>,
26 pub name: &'static PyStrInterned,
27 pub qualname: PyRwLock<Option<String>>,
28}
29
30#[derive(Debug)]
31pub struct PyDescriptorOwned {
32 pub typ: PyRef<PyType>,
33 pub name: &'static PyStrInterned,
34 pub qualname: PyRwLock<Option<String>>,
35}
36
37#[pyclass(name = "method_descriptor", module = false)]
38pub struct PyMethodDescriptor {
39 #[pymember(name = "__objclass__", path = "typ")]
40 #[pymember(name = "__name__", path = "name")]
41 pub common: PyDescriptor,
42 pub method: &'static PyMethodDef,
43 pub(crate) _method_def_owner: Option<PyObjectRef>,
46}
47
48impl PyMethodDescriptor {
49 pub fn new(method: &'static PyMethodDef, typ: &'static Py<PyType>, ctx: &Context) -> Self {
50 Self {
51 common: PyDescriptor {
52 typ,
53 name: ctx.intern_str(method.name),
54 qualname: PyRwLock::new(None),
55 },
56 method,
57 _method_def_owner: None,
58 }
59 }
60}
61
62impl PyPayload for PyMethodDescriptor {
63 fn class(ctx: &Context) -> &'static Py<PyType> {
64 ctx.types.method_descriptor_type
65 }
66}
67
68impl core::fmt::Debug for PyMethodDescriptor {
69 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
70 write!(f, "method descriptor for '{}'", self.common.name)
71 }
72}
73
74impl GetDescriptor for PyMethodDescriptor {
75 fn descr_get(
76 zelf: &PyObject,
77 obj: Option<&PyObject>,
78 cls: Option<&PyObject>,
79 vm: &VirtualMachine,
80 ) -> PyResult {
81 let descr = Self::_as_pyref(zelf, vm).unwrap();
82 let bound = match obj {
83 Some(obj) => {
84 method_descr_typecheck(descr, obj, vm)?;
85 if descr.method.flags.contains(PyMethodFlags::METHOD) {
86 if cls
87 .as_ref()
88 .is_none_or(|c| c.fast_isinstance(vm.ctx.types.type_type))
89 {
90 obj.to_owned()
91 } else {
92 return Err(vm.new_type_error(format!(
93 "descriptor '{}' needs a type, not '{}', as arg 2",
94 descr.common.name.as_str(),
95 obj.class().name()
96 )));
97 }
98 } else if descr.method.flags.contains(PyMethodFlags::CLASS) {
99 obj.class().to_owned().into()
100 } else {
101 obj.to_owned()
102 }
103 }
104 None if descr.method.flags.contains(PyMethodFlags::CLASS) => cls.unwrap().to_owned(),
105 None => return Ok(zelf.to_owned()),
106 };
107 Ok(descr.bind(bound, &vm.ctx).into())
108 }
109}
110
111impl Callable for PyMethodDescriptor {
112 type Args = FuncArgs;
113 #[inline]
114 fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
115 if let Some(obj) = args.args.first() {
116 method_descr_typecheck(zelf, obj, vm)?;
117 }
118 (zelf.method.func)(
119 vm,
120 args,
121 Callee::named(zelf.method.name).with_instance_arg(true),
122 )
123 }
124}
125
126impl PyMethodDescriptor {
127 pub fn bind(&self, obj: PyObjectRef, ctx: &Context) -> PyRef<PyNativeMethod> {
128 self.method.build_bound_method(ctx, obj, self.common.typ)
129 }
130}
131
132#[pyclass(
133 with(GetDescriptor, Callable, Representable),
134 flags(METHOD_DESCRIPTOR, DISALLOW_INSTANTIATION)
135)]
136impl Py<PyMethodDescriptor> {
137 #[pygetset]
138 fn __qualname__(&self) -> String {
139 format!("{}.{}", self.common.typ.name(), self.common.name)
140 }
141
142 #[pygetset]
143 fn __doc__(&self) -> Option<&'static str> {
144 type_::rendered_item_doc(self.method.name, self.method.item_doc())
145 }
146
147 #[pygetset]
148 fn __text_signature__(&self) -> Option<&'static str> {
149 let doc = self.method.doc?;
150 type_::get_text_signature_from_internal_doc(self.method.name, doc)
151 }
152
153 #[pymethod]
154 fn __reduce__(
155 &self,
156 vm: &VirtualMachine,
157 ) -> (Option<PyObjectRef>, (Option<PyObjectRef>, &'static str)) {
158 let builtins_getattr = vm.builtins.get_attr("getattr", vm).ok();
159 let classname = vm.builtins.get_attr(&self.common.typ.__name__(vm), vm).ok();
160 (builtins_getattr, (classname, self.method.name))
161 }
162}
163
164impl Representable for PyMethodDescriptor {
165 #[inline]
166 fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
167 Ok(format!(
168 "<method '{}' of '{}' objects>",
169 zelf.method.name,
170 zelf.common.typ.name()
171 ))
172 }
173}
174
175#[pyclass(name = "classmethod_descriptor", module = false)]
177pub struct PyClassMethodDescriptor {
178 #[pymember(name = "__objclass__", path = "typ")]
179 #[pymember(name = "__name__", path = "name")]
180 pub common: PyDescriptor,
181 pub method: &'static PyMethodDef,
182 pub(crate) _method_def_owner: Option<PyObjectRef>,
183}
184
185impl PyClassMethodDescriptor {
186 pub fn new(method: &'static PyMethodDef, typ: &'static Py<PyType>, ctx: &Context) -> Self {
187 Self {
188 common: PyDescriptor {
189 typ,
190 name: ctx.intern_str(method.name),
191 qualname: PyRwLock::new(None),
192 },
193 method,
194 _method_def_owner: None,
195 }
196 }
197
198 pub fn bind(&self, obj: PyObjectRef, ctx: &Context) -> PyRef<PyNativeMethod> {
199 self.method.build_bound_method(ctx, obj, self.common.typ)
200 }
201}
202
203impl PyPayload for PyClassMethodDescriptor {
204 fn class(ctx: &Context) -> &'static Py<PyType> {
205 ctx.types.classmethod_descriptor_type
206 }
207}
208
209impl core::fmt::Debug for PyClassMethodDescriptor {
210 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
211 write!(f, "classmethod descriptor for '{}'", self.common.name)
212 }
213}
214
215impl GetDescriptor for PyClassMethodDescriptor {
216 fn descr_get(
217 zelf: &PyObject,
218 obj: Option<&PyObject>,
219 cls: Option<&PyObject>,
220 vm: &VirtualMachine,
221 ) -> PyResult {
222 let descr = Self::_as_pyref(zelf, vm).unwrap();
223 let type_obj = match cls {
224 Some(typ) => typ.to_owned(),
225 None => match obj {
226 Some(o) => o.class().to_owned().into(),
227 None => {
228 return Err(vm.new_type_error(format!(
229 "descriptor '{}' for type '{}' needs either an object or a type",
230 descr.common.name,
231 descr.common.typ.name()
232 )));
233 }
234 },
235 };
236 if !type_obj.fast_isinstance(vm.ctx.types.type_type) {
237 return Err(vm.new_type_error(format!(
238 "descriptor '{}' for type '{}' needs a type, not '{}' as arg 2",
239 descr.common.name,
240 descr.common.typ.name(),
241 type_obj.class().name()
242 )));
243 }
244 let typ = type_obj.downcast::<PyType>().map_err(|obj| {
245 vm.new_type_error(format!(
246 "descriptor '{}' for type '{}' needs a type, not '{}' as arg 2",
247 descr.common.name,
248 descr.common.typ.name(),
249 obj.class().name()
250 ))
251 })?;
252 if !typ.fast_issubclass(descr.common.typ) {
253 return Err(vm.new_type_error(format!(
254 "descriptor '{}' requires a subtype of '{}' but received '{}'",
255 descr.common.name,
256 descr.common.typ.name(),
257 typ.name()
258 )));
259 }
260 Ok(descr.bind(typ.into(), &vm.ctx).into())
261 }
262}
263
264impl Callable for PyClassMethodDescriptor {
265 type Args = FuncArgs;
266 fn call(zelf: &Py<Self>, mut args: FuncArgs, vm: &VirtualMachine) -> PyResult {
267 let Some(owner) = args.args.first().cloned() else {
268 return Err(vm.new_type_error(format!(
269 "descriptor '{}' of '{}' object needs an argument",
270 zelf.method.name,
271 zelf.common.typ.name()
272 )));
273 };
274 let bound = Self::descr_get(zelf.as_object(), None, Some(&owner), vm)?;
275 args.args.remove(0);
276 bound.call(args, vm)
277 }
278}
279
280#[pyclass(
281 with(GetDescriptor, Callable, Representable),
282 flags(DISALLOW_INSTANTIATION)
283)]
284impl Py<PyClassMethodDescriptor> {
285 #[pygetset]
286 fn __qualname__(&self) -> String {
287 format!("{}.{}", self.common.typ.name(), self.common.name)
288 }
289
290 #[pygetset]
291 fn __doc__(&self) -> Option<&'static str> {
292 type_::rendered_item_doc(self.method.name, self.method.item_doc())
293 }
294
295 #[pygetset]
296 fn __text_signature__(&self) -> Option<&'static str> {
297 let doc = self.method.doc?;
298 type_::get_text_signature_from_internal_doc(self.method.name, doc)
299 }
300}
301
302impl Representable for PyClassMethodDescriptor {
303 #[inline]
304 fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
305 Ok(format!(
306 "<method '{}' of '{}' objects>",
307 zelf.method.name,
308 zelf.common.typ.name()
309 ))
310 }
311}
312
313#[derive(Copy, Clone, Debug, Eq, PartialEq)]
315#[repr(i32)]
316pub enum MemberKind {
317 Short = 0,
319 Int = 1,
320 Long = 2,
322 Double = 4,
323 String = 5,
326 Object = 6,
327 UByte = 9,
329 UShort = 10,
331 Uint = 11,
332 ULong = 12,
334 Bool = 14,
335 ObjectEx = 16,
336 LongLong = 17,
338 ULongLong = 18,
340 PySsizeT = 19,
342}
343
344impl MemberKind {
345 const fn from_i64() -> Self {
346 if size_of::<core::ffi::c_long>() == 8 {
347 Self::Long
348 } else {
349 Self::LongLong
350 }
351 }
352
353 const fn from_u64() -> Self {
354 if size_of::<core::ffi::c_ulong>() == 8 {
355 Self::ULong
356 } else {
357 Self::ULongLong
358 }
359 }
360
361 #[must_use]
362 pub fn from_i32(value: i32) -> Option<Self> {
363 match value {
364 0 => Some(Self::Short),
365 1 => Some(Self::Int),
366 2 => Some(Self::Long),
367 4 => Some(Self::Double),
368 5 => Some(Self::String),
369 6 => Some(Self::Object),
370 9 => Some(Self::UByte),
371 10 => Some(Self::UShort),
372 11 => Some(Self::Uint),
373 12 => Some(Self::ULong),
374 14 => Some(Self::Bool),
375 16 => Some(Self::ObjectEx),
376 17 => Some(Self::LongLong),
377 18 => Some(Self::ULongLong),
378 19 => Some(Self::PySsizeT),
379 _ => None,
380 }
381 }
382}
383
384bitflags::bitflags! {
385 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
386 #[repr(transparent)]
387 pub struct PyMemberFlags: i32 {
388 const READONLY = 1;
389 const AUDIT_READ = 2;
390 const RELATIVE_OFFSET = 8;
392 const ATOMIC = 0x10;
400 }
401}
402
403#[doc(hidden)]
405pub trait MemberLayout {
406 const KIND: MemberKind;
407 const ATOMIC: bool = false;
409}
410
411#[doc(hidden)]
413pub trait MemberCell: MemberLayout {}
414
415#[doc(hidden)]
417#[must_use]
418pub const fn member_kind_of<T: MemberLayout, Owner>(
419 _: for<'a> fn(&'a Owner) -> &'a T,
420) -> MemberKind {
421 <T as MemberLayout>::KIND
422}
423
424#[doc(hidden)]
426#[must_use]
427pub const fn member_atomic_of<T: MemberLayout, Owner>(_: for<'a> fn(&'a Owner) -> &'a T) -> bool {
428 <T as MemberLayout>::ATOMIC
429}
430
431impl MemberLayout for i16 {
432 const KIND: MemberKind = MemberKind::Short;
433}
434impl MemberLayout for core::sync::atomic::AtomicI16 {
435 const KIND: MemberKind = MemberKind::Short;
436 const ATOMIC: bool = true;
437}
438impl MemberCell for core::sync::atomic::AtomicI16 {}
439
440impl MemberLayout for u16 {
441 const KIND: MemberKind = MemberKind::UShort;
442}
443impl MemberLayout for core::sync::atomic::AtomicU16 {
444 const KIND: MemberKind = MemberKind::UShort;
445 const ATOMIC: bool = true;
446}
447impl MemberCell for core::sync::atomic::AtomicU16 {}
448
449impl MemberLayout for u8 {
450 const KIND: MemberKind = MemberKind::UByte;
451}
452impl MemberLayout for core::sync::atomic::AtomicU8 {
453 const KIND: MemberKind = MemberKind::UByte;
454 const ATOMIC: bool = true;
455}
456impl MemberCell for core::sync::atomic::AtomicU8 {}
457
458impl MemberLayout for i64 {
460 const KIND: MemberKind = MemberKind::from_i64();
461}
462impl MemberLayout for core::sync::atomic::AtomicI64 {
463 const KIND: MemberKind = MemberKind::from_i64();
464 const ATOMIC: bool = true;
465}
466impl MemberCell for core::sync::atomic::AtomicI64 {}
467
468impl MemberLayout for u64 {
469 const KIND: MemberKind = MemberKind::from_u64();
470}
471impl MemberLayout for core::sync::atomic::AtomicU64 {
472 const KIND: MemberKind = MemberKind::from_u64();
473 const ATOMIC: bool = true;
474}
475impl MemberCell for core::sync::atomic::AtomicU64 {}
476
477#[repr(transparent)]
479#[derive(Copy, Clone)]
480pub struct CStrMember(*const core::ffi::c_char);
481
482impl core::fmt::Debug for CStrMember {
483 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
484 f.debug_struct("CStrMember").field("ptr", &self.0).finish()
485 }
486}
487
488impl CStrMember {
489 #[must_use]
490 pub const fn new(ptr: *const core::ffi::c_char) -> Self {
491 Self(ptr)
492 }
493
494 #[must_use]
495 pub const fn as_ptr(self) -> *const core::ffi::c_char {
496 self.0
497 }
498}
499
500unsafe impl Send for CStrMember {}
502unsafe impl Sync for CStrMember {}
503
504const _: () = assert!(
505 size_of::<CStrMember>() == size_of::<*const core::ffi::c_char>()
506 && align_of::<CStrMember>() == align_of::<*const core::ffi::c_char>()
507);
508
509impl MemberLayout for CStrMember {
510 const KIND: MemberKind = MemberKind::String;
511}
512
513impl MemberLayout for bool {
514 const KIND: MemberKind = MemberKind::Bool;
515}
516impl MemberLayout for core::sync::atomic::AtomicBool {
517 const KIND: MemberKind = MemberKind::Bool;
518 const ATOMIC: bool = true;
519}
520impl MemberCell for core::sync::atomic::AtomicBool {}
521
522impl MemberLayout for i32 {
523 const KIND: MemberKind = MemberKind::Int;
524}
525impl MemberLayout for core::sync::atomic::AtomicI32 {
526 const KIND: MemberKind = MemberKind::Int;
527 const ATOMIC: bool = true;
528}
529impl MemberCell for core::sync::atomic::AtomicI32 {}
530
531impl MemberLayout for u32 {
532 const KIND: MemberKind = MemberKind::Uint;
533}
534impl MemberLayout for core::sync::atomic::AtomicU32 {
535 const KIND: MemberKind = MemberKind::Uint;
536 const ATOMIC: bool = true;
537}
538impl MemberCell for core::sync::atomic::AtomicU32 {}
539
540impl MemberLayout for PyObjectRef {
544 const KIND: MemberKind = MemberKind::Object;
545}
546impl MemberLayout for Option<PyObjectRef> {
547 const KIND: MemberKind = MemberKind::Object;
548}
549impl MemberLayout for crate::object::PyObjectCell {
550 const KIND: MemberKind = MemberKind::Object;
551 const ATOMIC: bool = true;
552}
553impl<T> MemberLayout for PyRef<T> {
554 const KIND: MemberKind = MemberKind::Object;
555}
556impl<T> MemberLayout for Option<PyRef<T>> {
557 const KIND: MemberKind = MemberKind::Object;
558}
559impl MemberLayout for crate::object::PyAtomicRef<PyObject> {
560 const KIND: MemberKind = MemberKind::Object;
561 const ATOMIC: bool = true;
562}
563impl<T: PyPayload> MemberLayout for crate::object::PyAtomicRef<T> {
568 const KIND: MemberKind = MemberKind::Object;
569 const ATOMIC: bool = true;
570}
571impl MemberLayout for crate::object::PyAtomicRef<Option<PyObject>> {
572 const KIND: MemberKind = MemberKind::Object;
573 const ATOMIC: bool = true;
574}
575impl<T: PyPayload> MemberLayout for crate::object::PyAtomicRef<Option<T>> {
576 const KIND: MemberKind = MemberKind::Object;
577 const ATOMIC: bool = true;
578}
579impl MemberLayout for &'static crate::builtins::PyStrInterned {
580 const KIND: MemberKind = MemberKind::Object;
581}
582impl<T: PyPayload> MemberLayout for &'static Py<T> {
583 const KIND: MemberKind = MemberKind::Object;
584}
585impl MemberCell for crate::object::PyAtomicRef<PyObject> {}
586impl MemberCell for crate::object::PyAtomicRef<Option<PyObject>> {}
587
588impl MemberLayout for f64 {
589 const KIND: MemberKind = MemberKind::Double;
590}
591impl MemberLayout for crate::common::atomic::AtomicF64 {
592 const KIND: MemberKind = MemberKind::Double;
593 const ATOMIC: bool = true;
594}
595impl MemberCell for crate::common::atomic::AtomicF64 {}
596
597impl MemberLayout for isize {
598 const KIND: MemberKind = MemberKind::PySsizeT;
599}
600impl MemberLayout for core::sync::atomic::AtomicIsize {
601 const KIND: MemberKind = MemberKind::PySsizeT;
602 const ATOMIC: bool = true;
603}
604impl MemberCell for core::sync::atomic::AtomicIsize {}
605
606const _: () =
610 assert!(size_of::<usize>() == size_of::<isize>() && align_of::<usize>() == align_of::<isize>());
611impl MemberLayout for usize {
612 const KIND: MemberKind = MemberKind::PySsizeT;
613}
614
615const _: () = assert!(
625 size_of::<core::sync::atomic::AtomicPtr<PyStrInterned>>()
626 == size_of::<crate::object::PyAtomicRef<Option<PyObject>>>()
627 && align_of::<core::sync::atomic::AtomicPtr<PyStrInterned>>()
628 == align_of::<crate::object::PyAtomicRef<Option<PyObject>>>()
629);
630impl MemberLayout for core::sync::atomic::AtomicPtr<PyStrInterned> {
631 const KIND: MemberKind = MemberKind::Object;
632 const ATOMIC: bool = true;
633}
634
635#[derive(Clone, Copy, Debug)]
641pub enum MemberAccess {
642 Offset,
643 TupleItem,
644}
645
646#[repr(C)]
649#[allow(dead_code)]
650pub struct PyMemberDefLayout {
651 _name: *const core::ffi::c_char,
652 _type: core::ffi::c_int,
653 _offset: isize,
654 _flags: PyMemberFlags,
655 _doc: *const core::ffi::c_char,
656}
657
658const _: () = assert!(
659 size_of::<PyMemberFlags>() == size_of::<core::ffi::c_int>()
660 && align_of::<PyMemberFlags>() == align_of::<core::ffi::c_int>()
661);
662
663pub struct PyMemberDef {
665 pub name: String,
666 pub kind: MemberKind,
667 pub offset: isize,
668 pub flags: PyMemberFlags,
669 pub doc: ItemDoc,
670}
671
672impl PyMemberDef {
673 pub(crate) fn readonly(&self) -> bool {
674 self.flags.contains(PyMemberFlags::READONLY)
675 }
676
677 pub(crate) fn atomic_storage(&self) -> bool {
680 self.flags.contains(PyMemberFlags::ATOMIC) || !self.readonly()
681 }
682
683 pub(crate) fn audit_read(&self) -> bool {
684 self.flags.contains(PyMemberFlags::AUDIT_READ)
685 }
686}
687
688impl core::fmt::Debug for PyMemberDef {
689 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
690 f.debug_struct("PyMemberDef")
691 .field("name", &self.name)
692 .field("kind", &self.kind)
693 .field("offset", &self.offset)
694 .field("flags", &self.flags)
695 .field("doc", &self.doc)
696 .finish()
697 }
698}
699
700#[derive(Clone, Copy)]
702pub struct PyMemberSpec {
703 pub name: &'static str,
704 pub kind: MemberKind,
705 pub offset: isize,
706 pub flags: PyMemberFlags,
707 pub doc: ItemDoc,
708}
709
710impl PyMemberSpec {
711 #[must_use]
713 pub const fn concat<const N: usize>(parts: &[&[Self]]) -> [Self; N] {
714 const EMPTY: PyMemberSpec = PyMemberSpec {
715 name: "",
716 kind: MemberKind::Object,
717 offset: 0,
718 flags: PyMemberFlags::empty(),
719 doc: ItemDoc::NONE,
720 };
721 let mut out = [EMPTY; N];
722 let mut index = 0;
723 let mut part_index = 0;
724 while part_index < parts.len() {
725 let part = parts[part_index];
726 let mut item_index = 0;
727 while item_index < part.len() {
728 out[index] = part[item_index];
729 index += 1;
730 item_index += 1;
731 }
732 part_index += 1;
733 }
734 out
735 }
736}
737
738#[pyclass(name = "member_descriptor", module = false)]
740#[derive(Debug)]
741pub struct PyMemberDescriptor {
742 #[pymember(name = "__objclass__", path = "typ")]
743 #[pymember(name = "__name__", path = "name")]
744 pub common: PyDescriptorOwned,
745 pub member: PyMemberDef,
746 pub access: MemberAccess,
747}
748
749impl PyMemberDescriptor {
750 pub(crate) fn slot_offset(&self) -> Option<isize> {
753 if matches!(self.access, MemberAccess::TupleItem) {
754 return None;
755 }
756 match self.member.kind {
757 MemberKind::Object | MemberKind::ObjectEx => Some(self.member.offset),
758 MemberKind::Bool
759 | MemberKind::Double
760 | MemberKind::Int
761 | MemberKind::Long
762 | MemberKind::LongLong
763 | MemberKind::Short
764 | MemberKind::String
765 | MemberKind::UByte
766 | MemberKind::Uint
767 | MemberKind::ULong
768 | MemberKind::ULongLong
769 | MemberKind::UShort
770 | MemberKind::PySsizeT => None,
771 }
772 }
773
774 fn get(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
775 if self.member.audit_read() {
776 vm.audit("object.__getattr__", || {
777 (obj.clone(), vm.ctx.new_str(self.member.name.as_str()))
778 })?;
779 }
780 match self.access {
781 MemberAccess::Offset => member_get_one(&obj, self.member.offset, &self.member, vm),
782 MemberAccess::TupleItem => {
783 let index = self.member.offset as usize;
784 let tuple = obj.downcast_ref::<PyTuple>().ok_or_else(|| {
785 vm.new_type_error("unexpected payload for struct sequence member")
786 })?;
787 tuple
788 .as_slice()
789 .get(index)
790 .cloned()
791 .ok_or_else(|| vm.new_index_error(format!("tuple index {index} out of range")))
792 }
793 }
794 }
795
796 fn set(
797 &self,
798 obj: PyObjectRef,
799 value: PySetterValue<PyObjectRef>,
800 vm: &VirtualMachine,
801 ) -> PyResult<()> {
802 if self.member.readonly() {
803 return Err(vm.new_attribute_error("readonly attribute"));
804 }
805 match self.access {
806 MemberAccess::Offset => {
807 member_set_one(&obj, self.member.offset, &self.member, value, vm)
808 }
809 MemberAccess::TupleItem => Err(vm.new_attribute_error("readonly attribute")),
810 }
811 }
812}
813
814impl PyPayload for PyMemberDescriptor {
815 fn class(ctx: &Context) -> &'static Py<PyType> {
816 ctx.types.member_descriptor_type
817 }
818}
819
820fn calculate_qualname(descr: &PyDescriptorOwned, vm: &VirtualMachine) -> PyResult<Option<String>> {
821 if let Some(qualname) = vm.get_attribute_opt(descr.typ.as_object(), "__qualname__")? {
822 let str = qualname.downcast::<PyStr>().map_err(|_| {
823 vm.new_type_error("<descriptor>.__objclass__.__qualname__ is not a unicode object")
824 })?;
825 Ok(Some(format!("{}.{}", str, descr.name)))
826 } else {
827 Ok(None)
828 }
829}
830
831#[pyclass(with(GetDescriptor, Representable), flags(DISALLOW_INSTANTIATION))]
832impl Py<PyMemberDescriptor> {
833 #[pygetset]
834 fn __doc__(&self) -> Option<&'static str> {
835 plain_doc(self.member.doc)
836 }
837
838 #[pygetset]
839 fn __qualname__(&self, vm: &VirtualMachine) -> PyResult<Option<String>> {
840 let qualname = self.common.qualname.read();
841 Ok(if qualname.is_none() {
842 drop(qualname);
843 let calculated = calculate_qualname(&self.common, vm)?;
844 calculated.clone_into(&mut self.common.qualname.write());
845 calculated
846 } else {
847 qualname.to_owned()
848 })
849 }
850
851 #[pymethod]
852 fn __reduce__(&self, vm: &VirtualMachine) -> PyResult {
853 let builtins_getattr = vm.builtins.get_attr("getattr", vm)?;
854 Ok(vm
855 .ctx
856 .new_tuple(vec![
857 builtins_getattr,
858 vm.ctx
859 .new_tuple(vec![
860 self.common.typ.clone().into(),
861 vm.ctx.new_str(self.common.name.as_str()).into(),
862 ])
863 .into(),
864 ])
865 .into())
866 }
867
868 #[pyslot]
869 fn descr_set(
870 zelf: &PyObject,
871 obj: PyObjectRef,
872 value: PySetterValue<PyObjectRef>,
873 vm: &VirtualMachine,
874 ) -> PyResult<()> {
875 let zelf = PyMemberDescriptor::_as_pyref(zelf, vm)?;
876
877 if !obj.class().fast_issubclass(&zelf.common.typ) {
878 return Err(vm.new_type_error(format!(
879 "descriptor '{}' for '{}' objects doesn't apply to a '{}' object",
880 zelf.common.name,
881 zelf.common.typ.name(),
882 obj.class().name()
883 )));
884 }
885
886 zelf.set(obj, value, vm)
887 }
888}
889
890fn member_addr(obj: &PyObject, offset: isize) -> *mut u8 {
891 (obj as *const PyObject as *const u8).wrapping_add(offset as usize) as *mut u8
892}
893
894fn warn_member(vm: &VirtualMachine, message: &str) -> PyResult<()> {
895 crate::warn::warn(
896 vm.ctx.new_str(message).into(),
897 Some(vm.ctx.exceptions.runtime_warning.to_owned()),
898 1,
899 None,
900 vm,
901 )
902}
903
904fn load_i32(obj: &PyObject, offset: isize, atomic: bool) -> i32 {
905 let addr = member_addr(obj, offset);
906 if atomic {
907 unsafe {
909 (*addr.cast::<core::sync::atomic::AtomicI32>())
910 .load(core::sync::atomic::Ordering::Relaxed)
911 }
912 } else {
913 unsafe { addr.cast::<i32>().read() }
916 }
917}
918
919fn load_i16(obj: &PyObject, offset: isize, atomic: bool) -> i16 {
920 let addr = member_addr(obj, offset);
921 if atomic {
922 unsafe {
924 (*addr.cast::<core::sync::atomic::AtomicI16>())
925 .load(core::sync::atomic::Ordering::Relaxed)
926 }
927 } else {
928 unsafe { addr.cast::<i16>().read() }
931 }
932}
933
934fn load_u16(obj: &PyObject, offset: isize, atomic: bool) -> u16 {
935 let addr = member_addr(obj, offset);
936 if atomic {
937 unsafe {
939 (*addr.cast::<core::sync::atomic::AtomicU16>())
940 .load(core::sync::atomic::Ordering::Relaxed)
941 }
942 } else {
943 unsafe { addr.cast::<u16>().read() }
946 }
947}
948
949fn load_u8(obj: &PyObject, offset: isize, atomic: bool) -> u8 {
950 let addr = member_addr(obj, offset);
951 if atomic {
952 unsafe {
954 (*addr.cast::<core::sync::atomic::AtomicU8>())
955 .load(core::sync::atomic::Ordering::Relaxed)
956 }
957 } else {
958 unsafe { addr.cast::<u8>().read() }
961 }
962}
963
964fn load_i64(obj: &PyObject, offset: isize, atomic: bool) -> i64 {
965 let addr = member_addr(obj, offset);
966 if atomic {
967 unsafe {
969 (*addr.cast::<core::sync::atomic::AtomicI64>())
970 .load(core::sync::atomic::Ordering::Relaxed)
971 }
972 } else {
973 unsafe { addr.cast::<i64>().read() }
976 }
977}
978
979fn load_u64(obj: &PyObject, offset: isize, atomic: bool) -> u64 {
980 let addr = member_addr(obj, offset);
981 if atomic {
982 unsafe {
984 (*addr.cast::<core::sync::atomic::AtomicU64>())
985 .load(core::sync::atomic::Ordering::Relaxed)
986 }
987 } else {
988 unsafe { addr.cast::<u64>().read() }
992 }
993}
994
995#[allow(clippy::unnecessary_cast)] fn load_c_long(obj: &PyObject, offset: isize, atomic: bool) -> core::ffi::c_long {
997 let addr = member_addr(obj, offset);
998 if !atomic {
999 unsafe { addr.cast::<core::ffi::c_long>().read() }
1002 } else if size_of::<core::ffi::c_long>() == 8 {
1003 unsafe {
1006 (*addr.cast::<core::sync::atomic::AtomicI64>())
1007 .load(core::sync::atomic::Ordering::Relaxed) as core::ffi::c_long
1008 }
1009 } else {
1010 unsafe {
1013 (*addr.cast::<core::sync::atomic::AtomicI32>())
1014 .load(core::sync::atomic::Ordering::Relaxed) as core::ffi::c_long
1015 }
1016 }
1017}
1018
1019#[allow(clippy::unnecessary_cast)] fn store_c_long(obj: &PyObject, offset: isize, value: core::ffi::c_long) {
1021 let addr = member_addr(obj, offset);
1022 if size_of::<core::ffi::c_long>() == 8 {
1023 unsafe {
1026 (*addr.cast::<core::sync::atomic::AtomicI64>())
1027 .store(value as i64, core::sync::atomic::Ordering::Relaxed);
1028 }
1029 } else {
1030 unsafe {
1033 (*addr.cast::<core::sync::atomic::AtomicI32>())
1034 .store(value as i32, core::sync::atomic::Ordering::Relaxed);
1035 }
1036 }
1037}
1038
1039#[allow(clippy::unnecessary_cast)] fn load_c_ulong(obj: &PyObject, offset: isize, atomic: bool) -> core::ffi::c_ulong {
1041 let addr = member_addr(obj, offset);
1042 if !atomic {
1043 unsafe { addr.cast::<core::ffi::c_ulong>().read() }
1047 } else if size_of::<core::ffi::c_ulong>() == 8 {
1048 unsafe {
1051 (*addr.cast::<core::sync::atomic::AtomicU64>())
1052 .load(core::sync::atomic::Ordering::Relaxed) as core::ffi::c_ulong
1053 }
1054 } else {
1055 unsafe {
1058 (*addr.cast::<core::sync::atomic::AtomicU32>())
1059 .load(core::sync::atomic::Ordering::Relaxed) as core::ffi::c_ulong
1060 }
1061 }
1062}
1063
1064#[allow(clippy::unnecessary_cast)] fn store_c_ulong(obj: &PyObject, offset: isize, value: core::ffi::c_ulong) {
1066 let addr = member_addr(obj, offset);
1067 if size_of::<core::ffi::c_ulong>() == 8 {
1068 unsafe {
1071 (*addr.cast::<core::sync::atomic::AtomicU64>())
1072 .store(value as u64, core::sync::atomic::Ordering::Relaxed);
1073 }
1074 } else {
1075 unsafe {
1078 (*addr.cast::<core::sync::atomic::AtomicU32>())
1079 .store(value as u32, core::sync::atomic::Ordering::Relaxed);
1080 }
1081 }
1082}
1083
1084fn load_u32(obj: &PyObject, offset: isize, atomic: bool) -> u32 {
1085 let addr = member_addr(obj, offset);
1086 if atomic {
1087 unsafe {
1089 (*addr.cast::<core::sync::atomic::AtomicU32>())
1090 .load(core::sync::atomic::Ordering::Relaxed)
1091 }
1092 } else {
1093 unsafe { addr.cast::<u32>().read() }
1096 }
1097}
1098
1099fn member_as_c_long(value: &PyObject, vm: &VirtualMachine) -> PyResult<core::ffi::c_long> {
1100 let int_obj = value.try_index(vm)?;
1101 core::ffi::c_long::try_from(int_obj.as_bigint())
1102 .map_err(|_| vm.new_overflow_error("Python int too large to convert to C long"))
1103}
1104
1105fn member_uint_value(
1106 value: &PyObject,
1107 vm: &VirtualMachine,
1108) -> PyResult<(u32, Option<&'static str>)> {
1109 let int_obj = value.try_index(vm)?;
1110 let big = int_obj.as_bigint();
1111 if big.sign() == malachite_bigint::Sign::Minus {
1112 let long_val = core::ffi::c_long::try_from(big)
1113 .map_err(|_| vm.new_overflow_error("Python int too large to convert to C long"))?;
1114 let stored = long_val as u32;
1116 return Ok((stored, Some("Writing negative value into unsigned field")));
1117 }
1118 core::ffi::c_ulong::try_from(big)
1119 .map_err(|_| vm.new_overflow_error("Python int too large to convert to C unsigned long"))?;
1120 let wide = u64::try_from(big).expect("a value that fits c_ulong fits u64");
1121 let stored = wide as u32;
1122 let warning = (wide > u64::from(u32::MAX)).then_some("Truncation of value to unsigned int");
1123 Ok((stored, warning))
1124}
1125
1126fn member_get_one(
1128 obj: &PyObject,
1129 offset: isize,
1130 member: &PyMemberDef,
1131 vm: &VirtualMachine,
1132) -> PyResult {
1133 let value = match member.kind {
1134 MemberKind::Object => obj.get_slot(offset).unwrap_or_else(|| vm.ctx.none()),
1135 MemberKind::ObjectEx => match obj.get_slot(offset) {
1136 Some(value) => value,
1137 None => {
1138 return Err(vm.new_attribute_error(format!(
1139 "'{}' object has no attribute '{}'",
1140 obj.class().fully_qualified_name(vm)?,
1141 member.name
1142 )));
1143 }
1144 },
1145 MemberKind::Bool => {
1146 let raw = if member.atomic_storage() {
1147 unsafe {
1150 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicBool>())
1151 .load(core::sync::atomic::Ordering::Relaxed)
1152 }
1153 } else {
1154 unsafe { member_addr(obj, offset).cast::<bool>().read() }
1157 };
1158 vm.ctx.new_bool(raw).into()
1159 }
1160 MemberKind::Int => vm
1161 .ctx
1162 .new_int(load_i32(obj, offset, member.atomic_storage()))
1163 .into(),
1164 MemberKind::Uint => vm
1165 .ctx
1166 .new_int(load_u32(obj, offset, member.atomic_storage()))
1167 .into(),
1168 MemberKind::Double => {
1169 let raw = if member.atomic_storage() {
1170 unsafe {
1173 (*member_addr(obj, offset).cast::<crate::common::atomic::AtomicF64>())
1174 .load(core::sync::atomic::Ordering::Relaxed)
1175 }
1176 } else {
1177 unsafe { member_addr(obj, offset).cast::<f64>().read() }
1181 };
1182 vm.ctx.new_float(raw).into()
1183 }
1184 MemberKind::PySsizeT => {
1185 let raw = if member.atomic_storage() {
1186 unsafe {
1189 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicIsize>())
1190 .load(core::sync::atomic::Ordering::Relaxed)
1191 }
1192 } else {
1193 unsafe { member_addr(obj, offset).cast::<isize>().read() }
1196 };
1197 vm.ctx.new_int(raw).into()
1198 }
1199 MemberKind::Short => vm
1200 .ctx
1201 .new_int(load_i16(obj, offset, member.atomic_storage()))
1202 .into(),
1203 MemberKind::UShort => vm
1204 .ctx
1205 .new_int(load_u16(obj, offset, member.atomic_storage()))
1206 .into(),
1207 MemberKind::UByte => vm
1208 .ctx
1209 .new_int(load_u8(obj, offset, member.atomic_storage()))
1210 .into(),
1211 MemberKind::Long => vm
1212 .ctx
1213 .new_int(load_c_long(obj, offset, member.atomic_storage()))
1214 .into(),
1215 MemberKind::LongLong => vm
1216 .ctx
1217 .new_int(load_i64(obj, offset, member.atomic_storage()))
1218 .into(),
1219 MemberKind::ULong => vm
1220 .ctx
1221 .new_int(load_c_ulong(obj, offset, member.atomic_storage()))
1222 .into(),
1223 MemberKind::ULongLong => vm
1224 .ctx
1225 .new_int(load_u64(obj, offset, member.atomic_storage()))
1226 .into(),
1227 MemberKind::String => {
1228 let ptr = unsafe {
1231 member_addr(obj, offset)
1232 .cast::<*const core::ffi::c_char>()
1233 .read()
1234 };
1235 if ptr.is_null() {
1236 vm.ctx.none()
1237 } else {
1238 let bytes = unsafe { core::ffi::CStr::from_ptr(ptr) }.to_bytes();
1241 let Ok(text) = core::str::from_utf8(bytes) else {
1242 return Err(vm.new_unicode_decode_error(
1243 vm.ctx.new_str("utf-8"),
1244 vm.ctx.new_bytes(bytes.to_vec()),
1245 0,
1246 bytes.len(),
1247 vm.ctx.new_str("invalid UTF-8"),
1248 ));
1249 };
1250 vm.ctx.new_str(text).into()
1251 }
1252 }
1253 };
1254 Ok(value)
1255}
1256
1257fn member_set_one(
1259 obj: &PyObject,
1260 offset: isize,
1261 member: &PyMemberDef,
1262 value: PySetterValue,
1263 vm: &VirtualMachine,
1264) -> PyResult<()> {
1265 if matches!(value, PySetterValue::Delete)
1266 && !matches!(member.kind, MemberKind::Object | MemberKind::ObjectEx)
1267 {
1268 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1269 }
1270 match member.kind {
1271 MemberKind::Object => match value {
1272 PySetterValue::Assign(v) => obj.set_slot(offset, Some(v)),
1273 PySetterValue::Delete => obj.set_slot(offset, None),
1274 },
1275 MemberKind::ObjectEx => match value {
1276 PySetterValue::Assign(v) => obj.set_slot(offset, Some(v)),
1277 PySetterValue::Delete => {
1278 if obj.get_slot(offset).is_none() {
1279 return Err(vm.new_attribute_error(member.name.clone()));
1280 }
1281 obj.set_slot(offset, None);
1282 }
1283 },
1284 MemberKind::Bool => {
1285 let PySetterValue::Assign(value) = value else {
1286 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1287 };
1288 if !value.class().is(vm.ctx.types.bool_type) {
1289 return Err(vm.new_type_error("attribute value type must be bool"));
1290 }
1291 let stored = value.is(&vm.ctx.true_value);
1292 unsafe {
1294 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicBool>())
1295 .store(stored, core::sync::atomic::Ordering::Relaxed);
1296 }
1297 }
1298 MemberKind::Int => {
1299 let PySetterValue::Assign(value) = value else {
1300 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1301 };
1302 let long_val = member_as_c_long(&value, vm)?;
1303 let stored = long_val as i32;
1304 unsafe {
1307 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicI32>())
1308 .store(stored, core::sync::atomic::Ordering::Relaxed);
1309 }
1310 let truncated = long_val > i32::MAX as core::ffi::c_long
1311 || long_val < i32::MIN as core::ffi::c_long;
1312 if truncated {
1313 warn_member(vm, "Truncation of value to int")?;
1314 }
1315 }
1316 MemberKind::Uint => {
1317 let PySetterValue::Assign(value) = value else {
1318 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1319 };
1320 let (stored, warning) = member_uint_value(&value, vm)?;
1321 unsafe {
1324 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicU32>())
1325 .store(stored, core::sync::atomic::Ordering::Relaxed);
1326 }
1327 if let Some(warning) = warning {
1328 warn_member(vm, warning)?;
1329 }
1330 }
1331 MemberKind::Double => {
1332 let PySetterValue::Assign(value) = value else {
1333 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1334 };
1335 let number = value.try_float(vm)?.to_f64();
1336 unsafe {
1339 (*member_addr(obj, offset).cast::<crate::common::atomic::AtomicF64>())
1340 .store(number, core::sync::atomic::Ordering::Relaxed);
1341 }
1342 }
1343 MemberKind::PySsizeT => {
1344 let PySetterValue::Assign(value) = value else {
1345 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1346 };
1347 if !value.fast_isinstance(vm.ctx.types.int_type) {
1349 return Err(vm.new_type_error("an integer is required"));
1350 }
1351 let Some(int_obj) = value.downcast_ref::<crate::builtins::PyInt>() else {
1352 return Err(vm.new_type_error("an integer is required"));
1353 };
1354 let stored = isize::try_from(int_obj.as_bigint()).map_err(|_| {
1355 vm.new_overflow_error("Python int too large to convert to C ssize_t")
1356 })?;
1357 unsafe {
1360 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicIsize>())
1361 .store(stored, core::sync::atomic::Ordering::Relaxed);
1362 }
1363 }
1364 MemberKind::Short => {
1365 let PySetterValue::Assign(value) = value else {
1366 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1367 };
1368 let long_val = member_as_c_long(&value, vm)?;
1369 let stored = long_val as i16;
1370 unsafe {
1372 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicI16>())
1373 .store(stored, core::sync::atomic::Ordering::Relaxed);
1374 }
1375 if long_val > i16::MAX as core::ffi::c_long || long_val < i16::MIN as core::ffi::c_long
1376 {
1377 warn_member(vm, "Truncation of value to short")?;
1378 }
1379 }
1380 MemberKind::UShort => {
1381 let PySetterValue::Assign(value) = value else {
1382 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1383 };
1384 let long_val = member_as_c_long(&value, vm)?;
1385 let stored = long_val as u16;
1386 unsafe {
1388 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicU16>())
1389 .store(stored, core::sync::atomic::Ordering::Relaxed);
1390 }
1391 if long_val > u16::MAX as core::ffi::c_long || long_val < 0 {
1392 warn_member(vm, "Truncation of value to unsigned short")?;
1393 }
1394 }
1395 MemberKind::UByte => {
1396 let PySetterValue::Assign(value) = value else {
1397 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1398 };
1399 let long_val = member_as_c_long(&value, vm)?;
1400 let stored = long_val as u8;
1401 unsafe {
1403 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicU8>())
1404 .store(stored, core::sync::atomic::Ordering::Relaxed);
1405 }
1406 if long_val > u8::MAX as core::ffi::c_long || long_val < 0 {
1407 warn_member(vm, "Truncation of value to unsigned char")?;
1408 }
1409 }
1410 MemberKind::Long => {
1411 let PySetterValue::Assign(value) = value else {
1412 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1413 };
1414 let stored = member_as_c_long(&value, vm)?;
1415 store_c_long(obj, offset, stored);
1416 }
1417 MemberKind::LongLong => {
1418 let PySetterValue::Assign(value) = value else {
1419 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1420 };
1421 let stored = member_as_c_longlong(&value, vm)?;
1422 unsafe {
1424 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicI64>())
1425 .store(stored, core::sync::atomic::Ordering::Relaxed);
1426 }
1427 }
1428 MemberKind::ULong => {
1429 let PySetterValue::Assign(value) = value else {
1430 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1431 };
1432 let (stored, warning) = member_ulong_value(&value, vm)?;
1433 store_c_ulong(obj, offset, stored);
1434 if let Some(warning) = warning {
1435 warn_member(vm, warning)?;
1436 }
1437 }
1438 MemberKind::ULongLong => {
1439 let PySetterValue::Assign(value) = value else {
1440 return Err(vm.new_type_error("can't delete numeric/char attribute"));
1441 };
1442 let (stored, warning) = member_ulonglong_value(&value, vm)?;
1443 unsafe {
1445 (*member_addr(obj, offset).cast::<core::sync::atomic::AtomicU64>())
1446 .store(stored, core::sync::atomic::Ordering::Relaxed);
1447 }
1448 if let Some(warning) = warning {
1449 warn_member(vm, warning)?;
1450 }
1451 }
1452 MemberKind::String => {
1453 return Err(vm.new_type_error("readonly attribute"));
1454 }
1455 }
1456 Ok(())
1457}
1458
1459fn member_as_c_longlong(value: &PyObject, vm: &VirtualMachine) -> PyResult<i64> {
1460 let int_obj = value.try_index(vm)?;
1461 i64::try_from(int_obj.as_bigint())
1462 .map_err(|_| vm.new_overflow_error("Python int too large to convert to C long long"))
1463}
1464
1465fn member_ulong_value(
1466 value: &PyObject,
1467 vm: &VirtualMachine,
1468) -> PyResult<(core::ffi::c_ulong, Option<&'static str>)> {
1469 let int_obj = value.try_index(vm)?;
1470 let big = int_obj.as_bigint();
1471 if big.sign() == malachite_bigint::Sign::Minus {
1472 let long_val = core::ffi::c_long::try_from(big)
1473 .map_err(|_| vm.new_overflow_error("Python int too large to convert to C long"))?;
1474 return Ok((
1475 long_val as core::ffi::c_ulong,
1476 Some("Writing negative value into unsigned field"),
1477 ));
1478 }
1479 let stored = core::ffi::c_ulong::try_from(big)
1480 .map_err(|_| vm.new_overflow_error("Python int too large to convert to C unsigned long"))?;
1481 Ok((stored, None))
1482}
1483
1484fn member_ulonglong_value(
1485 value: &PyObject,
1486 vm: &VirtualMachine,
1487) -> PyResult<(u64, Option<&'static str>)> {
1488 let int_obj = value.try_index(vm)?;
1489 let big = int_obj.as_bigint();
1490 if big.sign() == malachite_bigint::Sign::Minus {
1491 let long_val = core::ffi::c_long::try_from(big)
1492 .map_err(|_| vm.new_overflow_error("Python int too large to convert to C long"))?;
1493 return Ok((
1494 long_val as u64,
1495 Some("Writing negative value into unsigned field"),
1496 ));
1497 }
1498 let stored = u64::try_from(big).map_err(|_| {
1499 vm.new_overflow_error("Python int too large to convert to C unsigned long long")
1500 })?;
1501 Ok((stored, None))
1502}
1503
1504impl Representable for PyMemberDescriptor {
1505 #[inline]
1506 fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
1507 Ok(format!(
1508 "<member '{}' of '{}' objects>",
1509 zelf.common.name,
1510 zelf.common.typ.slot_name(),
1511 ))
1512 }
1513}
1514
1515impl GetDescriptor for PyMemberDescriptor {
1516 fn descr_get(
1517 zelf: &PyObject,
1518 obj: Option<&PyObject>,
1519 _cls: Option<&PyObject>,
1520 vm: &VirtualMachine,
1521 ) -> PyResult {
1522 let descr = Self::_as_pyref(zelf, vm)?;
1523 match obj {
1524 Some(x) => {
1525 if !x.class().fast_issubclass(&descr.common.typ) {
1526 return Err(vm.new_type_error(format!(
1527 "descriptor '{}' for '{}' objects doesn't apply to a '{}' object",
1528 descr.common.name,
1529 descr.common.typ.name(),
1530 x.class().name()
1531 )));
1532 }
1533 descr.get(x.to_owned(), vm)
1534 }
1535 None => Ok(zelf.to_owned()),
1536 }
1537 }
1538}
1539
1540fn method_descr_typecheck(
1541 descr: &PyMethodDescriptor,
1542 obj: &PyObject,
1543 vm: &VirtualMachine,
1544) -> PyResult<()> {
1545 if descr.method.flags.contains(PyMethodFlags::STATIC)
1546 || descr.method.flags.contains(PyMethodFlags::CLASS)
1547 || obj.fast_isinstance(descr.common.typ)
1548 {
1549 return Ok(());
1550 }
1551 Err(vm.new_type_error(format!(
1552 "descriptor '{}' for '{}' objects doesn't apply to a '{}' object",
1553 descr.common.name.as_str(),
1554 descr.common.typ.name(),
1555 obj.class().name()
1556 )))
1557}
1558
1559fn vectorcall_method_descriptor(
1561 zelf_obj: &PyObject,
1562 args: Vec<PyObjectRef>,
1563 nargs: usize,
1564 kwnames: Option<&[PyObjectRef]>,
1565 vm: &VirtualMachine,
1566) -> PyResult {
1567 let zelf: &Py<PyMethodDescriptor> = zelf_obj.downcast_ref().unwrap();
1568 if nargs > 0
1569 && let Some(obj) = args.first()
1570 {
1571 method_descr_typecheck(zelf, obj, vm)?;
1572 }
1573 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
1574 (zelf.method.func)(
1575 vm,
1576 func_args,
1577 Callee::named(zelf.method.name).with_instance_arg(true),
1578 )
1579}
1580
1581fn vectorcall_wrapper(
1583 zelf_obj: &PyObject,
1584 mut args: Vec<PyObjectRef>,
1585 nargs: usize,
1586 kwnames: Option<&[PyObjectRef]>,
1587 vm: &VirtualMachine,
1588) -> PyResult {
1589 let zelf: &Py<PyWrapper> = zelf_obj.downcast_ref().unwrap();
1590 if nargs == 0 {
1592 return Err(vm.new_type_error(format!(
1593 "descriptor '{}' of '{}' object needs an argument",
1594 zelf.name.as_str(),
1595 zelf.typ.name()
1596 )));
1597 }
1598 let obj = args.remove(0);
1599 if !obj.fast_isinstance(zelf.typ) {
1600 return Err(vm.new_type_error(format!(
1601 "descriptor '{}' requires a '{}' object but received a '{}'",
1602 zelf.name.as_str(),
1603 zelf.typ.name(),
1604 obj.class().name()
1605 )));
1606 }
1607 let rest = FuncArgs::from_vectorcall_owned(args, nargs - 1, kwnames);
1608 zelf.wrapped.call(obj, rest, vm)
1609}
1610
1611pub(crate) fn init(ctx: &'static Context) {
1612 PyMemberDescriptor::extend_class(ctx, ctx.types.member_descriptor_type);
1613 PyMethodDescriptor::extend_class(ctx, ctx.types.method_descriptor_type);
1614 ctx.types
1615 .method_descriptor_type
1616 .slots
1617 .vectorcall
1618 .store(Some(vectorcall_method_descriptor));
1619 PyClassMethodDescriptor::extend_class(ctx, ctx.types.classmethod_descriptor_type);
1620 PyWrapper::extend_class(ctx, ctx.types.wrapper_descriptor_type);
1621 ctx.types
1622 .wrapper_descriptor_type
1623 .slots
1624 .vectorcall
1625 .store(Some(vectorcall_wrapper));
1626 PyMethodWrapper::extend_class(ctx, ctx.types.method_wrapper_type);
1627}
1628
1629#[derive(Clone, Copy)]
1633pub enum SlotFunc {
1634 Init(InitFunc),
1636 Hash(HashFunc),
1637 Str(StringifyFunc),
1638 Repr(StringifyFunc),
1639 Iter(IterFunc),
1640 IterNext(IterNextFunc),
1641 Call(GenericMethod),
1642 Del(DelFunc),
1643
1644 GetAttro(GetattroFunc),
1646 SetAttro(SetattroFunc), DelAttro(SetattroFunc), RichCompare(RichCompareFunc, PyComparisonOp),
1651
1652 DescrGet(DescrGetFunc),
1654 DescrSet(DescrSetFunc), DescrDel(DescrSetFunc), SeqLength(SeqLenFunc),
1659 SeqConcat(SeqConcatFunc),
1660 SeqRepeat(SeqRepeatFunc),
1661 SeqItem(SeqItemFunc),
1662 SeqSetItem(SeqAssItemFunc), SeqDelItem(SeqAssItemFunc), SeqContains(SeqContainsFunc),
1665
1666 MapLength(MapLenFunc),
1668 MapSubscript(MapSubscriptFunc),
1669 MapSetSubscript(MapAssSubscriptFunc), MapDelSubscript(MapAssSubscriptFunc), NumBoolean(PyNumberUnaryFunc<bool>), NumUnary(PyNumberUnaryFunc), NumBinary(PyNumberBinaryFunc), NumBinaryRight(PyNumberBinaryFunc), NumTernary(PyNumberTernaryFunc), NumTernaryRight(PyNumberTernaryFunc), GetBuffer(crate::types::AsBufferFunc), ReleaseBuffer, }
1684
1685impl core::fmt::Debug for SlotFunc {
1686 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
1687 match self {
1688 Self::Init(_) => write!(f, "SlotFunc::Init(...)"),
1689 Self::Hash(_) => write!(f, "SlotFunc::Hash(...)"),
1690 Self::Str(_) => write!(f, "SlotFunc::Str(...)"),
1691 Self::Repr(_) => write!(f, "SlotFunc::Repr(...)"),
1692 Self::Iter(_) => write!(f, "SlotFunc::Iter(...)"),
1693 Self::IterNext(_) => write!(f, "SlotFunc::IterNext(...)"),
1694 Self::Call(_) => write!(f, "SlotFunc::Call(...)"),
1695 Self::Del(_) => write!(f, "SlotFunc::Del(...)"),
1696 Self::GetAttro(_) => write!(f, "SlotFunc::GetAttro(...)"),
1697 Self::SetAttro(_) => write!(f, "SlotFunc::SetAttro(...)"),
1698 Self::DelAttro(_) => write!(f, "SlotFunc::DelAttro(...)"),
1699 Self::RichCompare(_, op) => write!(f, "SlotFunc::RichCompare(..., {op:?})"),
1700 Self::DescrGet(_) => write!(f, "SlotFunc::DescrGet(...)"),
1701 Self::DescrSet(_) => write!(f, "SlotFunc::DescrSet(...)"),
1702 Self::DescrDel(_) => write!(f, "SlotFunc::DescrDel(...)"),
1703 Self::SeqLength(_) => write!(f, "SlotFunc::SeqLength(...)"),
1705 Self::SeqConcat(_) => write!(f, "SlotFunc::SeqConcat(...)"),
1706 Self::SeqRepeat(_) => write!(f, "SlotFunc::SeqRepeat(...)"),
1707 Self::SeqItem(_) => write!(f, "SlotFunc::SeqItem(...)"),
1708 Self::SeqSetItem(_) => write!(f, "SlotFunc::SeqSetItem(...)"),
1709 Self::SeqDelItem(_) => write!(f, "SlotFunc::SeqDelItem(...)"),
1710 Self::SeqContains(_) => write!(f, "SlotFunc::SeqContains(...)"),
1711 Self::MapLength(_) => write!(f, "SlotFunc::MapLength(...)"),
1713 Self::MapSubscript(_) => write!(f, "SlotFunc::MapSubscript(...)"),
1714 Self::MapSetSubscript(_) => write!(f, "SlotFunc::MapSetSubscript(...)"),
1715 Self::MapDelSubscript(_) => write!(f, "SlotFunc::MapDelSubscript(...)"),
1716 Self::NumBoolean(_) => write!(f, "SlotFunc::NumBoolean(...)"),
1718 Self::NumUnary(_) => write!(f, "SlotFunc::NumUnary(...)"),
1719 Self::NumBinary(_) => write!(f, "SlotFunc::NumBinary(...)"),
1720 Self::NumBinaryRight(_) => write!(f, "SlotFunc::NumBinaryRight(...)"),
1721 Self::NumTernary(_) => write!(f, "SlotFunc::NumTernary(...)"),
1722 Self::NumTernaryRight(_) => write!(f, "SlotFunc::NumTernaryRight(...)"),
1723 Self::GetBuffer(_) => write!(f, "SlotFunc::GetBuffer(...)"),
1724 Self::ReleaseBuffer => write!(f, "SlotFunc::ReleaseBuffer"),
1725 }
1726 }
1727}
1728
1729impl SlotFunc {
1730 pub fn call(&self, obj: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1732 match self {
1733 Self::Init(func) => {
1734 func(&obj, args, vm)?;
1735 Ok(vm.ctx.none())
1736 }
1737 Self::Hash(func) => {
1738 if !args.args.is_empty() || !args.kwargs.is_empty() {
1739 return Err(vm.new_type_error("__hash__() takes no arguments (1 given)"));
1740 }
1741 let hash = func(&obj, vm)?;
1742 Ok(vm.ctx.new_int(hash).into())
1743 }
1744 Self::Repr(func) | Self::Str(func) => {
1745 if !args.args.is_empty() || !args.kwargs.is_empty() {
1746 let name = match self {
1747 Self::Repr(_) => "__repr__",
1748 Self::Str(_) => "__str__",
1749 _ => unreachable!(),
1750 };
1751 return Err(vm.new_type_error(format!("{name}() takes no arguments (1 given)")));
1752 }
1753 let s = func(&obj, vm)?;
1754 Ok(s.into())
1755 }
1756 Self::Iter(func) => {
1757 if !args.args.is_empty() || !args.kwargs.is_empty() {
1758 return Err(vm.new_type_error("__iter__() takes no arguments (1 given)"));
1759 }
1760 func(obj, vm)
1761 }
1762 Self::IterNext(func) => {
1763 if !args.args.is_empty() || !args.kwargs.is_empty() {
1764 return Err(vm.new_type_error("__next__() takes no arguments (1 given)"));
1765 }
1766 func(&obj, vm).to_pyresult(vm)
1767 }
1768 Self::Call(func) => func(&obj, args, vm),
1769 Self::Del(func) => {
1770 if !args.args.is_empty() || !args.kwargs.is_empty() {
1771 return Err(vm.new_type_error("__del__() takes no arguments (1 given)"));
1772 }
1773 func(&obj, vm)?;
1774 Ok(vm.ctx.none())
1775 }
1776 Self::GetAttro(func) => {
1777 let (name,): (PyRef<PyStr>,) = args.bind(vm)?;
1778 func(&obj, &name, vm)
1779 }
1780 Self::SetAttro(func) => {
1781 let (name, value): (PyRef<PyStr>, PyObjectRef) = args.bind(vm)?;
1782 crate::types::hackcheck_setattro(&obj, *func, "__setattr__", vm)?;
1783 func(&obj, &name, PySetterValue::Assign(value), vm)?;
1784 Ok(vm.ctx.none())
1785 }
1786 Self::DelAttro(func) => {
1787 let (name,): (PyRef<PyStr>,) = args.bind(vm)?;
1788 crate::types::hackcheck_setattro(&obj, *func, "__delattr__", vm)?;
1789 func(&obj, &name, PySetterValue::Delete, vm)?;
1790 Ok(vm.ctx.none())
1791 }
1792 Self::RichCompare(func, op) => {
1793 let (other,): (PyObjectRef,) = args.bind(vm)?;
1794 func(&obj, &other, *op, vm).map(|r| match r {
1795 crate::function::Either::A(obj) => obj,
1796 crate::function::Either::B(cmp_val) => cmp_val.to_pyobject(vm),
1797 })
1798 }
1799 Self::DescrGet(func) => {
1800 let (instance, owner): (PyObjectRef, crate::function::OptionalArg<PyObjectRef>) =
1801 args.bind(vm)?;
1802 let owner = owner.into_option();
1803 let instance_opt = if vm.is_none(&instance) {
1804 None
1805 } else {
1806 Some(instance)
1807 };
1808 func(&obj, instance_opt.as_deref(), owner.as_deref(), vm)
1809 }
1810 Self::DescrSet(func) => {
1811 let (instance, value): (PyObjectRef, PyObjectRef) = args.bind(vm)?;
1812 func(&obj, instance, PySetterValue::Assign(value), vm)?;
1813 Ok(vm.ctx.none())
1814 }
1815 Self::DescrDel(func) => {
1816 let (instance,): (PyObjectRef,) = args.bind(vm)?;
1817 func(&obj, instance, PySetterValue::Delete, vm)?;
1818 Ok(vm.ctx.none())
1819 }
1820 Self::SeqLength(func) => {
1822 args.bind::<()>(vm)?;
1823 let len = func(obj.sequence_unchecked(), vm)?;
1824 Ok(vm.ctx.new_int(len).into())
1825 }
1826 Self::SeqConcat(func) => {
1827 let (other,): (PyObjectRef,) = args.bind(vm)?;
1828 func(obj.sequence_unchecked(), &other, vm)
1829 }
1830 Self::SeqRepeat(func) => {
1831 let (n,): (PySsize,) = args.bind(vm)?;
1832 func(obj.sequence_unchecked(), n, vm)
1833 }
1834 Self::SeqItem(func) => {
1835 let (index,): (isize,) = args.bind(vm)?;
1836 func(obj.sequence_unchecked(), index, vm)
1837 }
1838 Self::SeqSetItem(func) => {
1839 let (index, value): (isize, PyObjectRef) = args.bind(vm)?;
1840 func(obj.sequence_unchecked(), index, Some(value), vm)?;
1841 Ok(vm.ctx.none())
1842 }
1843 Self::SeqDelItem(func) => {
1844 let (index,): (isize,) = args.bind(vm)?;
1845 func(obj.sequence_unchecked(), index, None, vm)?;
1846 Ok(vm.ctx.none())
1847 }
1848 Self::SeqContains(func) => {
1849 let (item,): (PyObjectRef,) = args.bind(vm)?;
1850 let result = func(obj.sequence_unchecked(), &item, vm)?;
1851 Ok(vm.ctx.new_bool(result).into())
1852 }
1853 Self::MapLength(func) => {
1855 args.bind::<()>(vm)?;
1856 let len = func(obj.mapping_unchecked(), vm)?;
1857 Ok(vm.ctx.new_int(len).into())
1858 }
1859 Self::MapSubscript(func) => {
1860 let (key,): (PyObjectRef,) = args.bind(vm)?;
1861 func(obj.mapping_unchecked(), &key, vm)
1862 }
1863 Self::MapSetSubscript(func) => {
1864 let (key, value): (PyObjectRef, PyObjectRef) = args.bind(vm)?;
1865 func(obj.mapping_unchecked(), &key, Some(value), vm)?;
1866 Ok(vm.ctx.none())
1867 }
1868 Self::MapDelSubscript(func) => {
1869 let (key,): (PyObjectRef,) = args.bind(vm)?;
1870 func(obj.mapping_unchecked(), &key, None, vm)?;
1871 Ok(vm.ctx.none())
1872 }
1873 Self::NumBoolean(func) => {
1875 args.bind::<()>(vm)?;
1876 let result = func(obj.number(), vm)?;
1877 Ok(vm.ctx.new_bool(result).into())
1878 }
1879 Self::NumUnary(func) => {
1880 args.bind::<()>(vm)?;
1881 func(obj.number(), vm)
1882 }
1883 Self::NumBinary(func) => {
1884 let (other,): (PyObjectRef,) = args.bind(vm)?;
1885 func(&obj, &other, vm)
1886 }
1887 Self::NumBinaryRight(func) => {
1888 let (other,): (PyObjectRef,) = args.bind(vm)?;
1889 func(&other, &obj, vm) }
1891 Self::NumTernary(func) => {
1892 let (y, z) = pow_args(args, vm)?;
1893 func(&obj, &y, &z, vm)
1894 }
1895 Self::NumTernaryRight(func) => {
1896 let (y, z) = pow_args(args, vm)?;
1897 func(&y, &obj, &z, vm)
1898 }
1899 Self::GetBuffer(func) => {
1901 let (flags_obj,): (PyObjectRef,) = args.bind(vm)?;
1902 let buffer = func(&obj, parse_buffer_flags(&flags_obj, vm)?, vm)?;
1903 crate::builtins::PyMemoryView::from_buffer(buffer, vm)
1904 .map(|mv| mv.into_pyobject(vm))
1905 }
1906 Self::ReleaseBuffer => {
1907 let (mv_obj,): (PyObjectRef,) = args.bind(vm)?;
1908 let mv = mv_obj
1909 .downcast::<crate::builtins::PyMemoryView>()
1910 .map_err(|_| vm.new_type_error("expected a memoryview object"))?;
1911 crate::builtins::memory::release_buffer_from_python(&obj, &mv, vm)?;
1912 Ok(vm.ctx.none())
1913 }
1914 }
1915 }
1916}
1917
1918fn pow_args(args: FuncArgs, vm: &VirtualMachine) -> PyResult<(PyObjectRef, PyObjectRef)> {
1920 if let Some(err) = args.check_kwargs_empty(vm) {
1921 return Err(err);
1922 }
1923 let size = args.args.len();
1924 if !(1..=2).contains(&size) {
1925 return Err(vm.new_type_error(format!("expected 1 or 2 arguments, got {size}")));
1926 }
1927 let y = args.args[0].clone();
1928 let z = if size == 2 {
1929 args.args[1].clone()
1930 } else {
1931 vm.ctx.none()
1932 };
1933 Ok((y, z))
1934}
1935
1936fn parse_buffer_flags(
1938 arg: &PyObject,
1939 vm: &VirtualMachine,
1940) -> PyResult<crate::protocol::BufferFlags> {
1941 use num_traits::ToPrimitive;
1942 let idx = arg.try_index(vm)?;
1943 let flags = idx
1944 .as_bigint()
1945 .to_isize()
1946 .ok_or_else(|| vm.new_overflow_error("cannot fit 'int' into an index-sized integer"))?;
1947 let flags =
1948 i32::try_from(flags).map_err(|_| vm.new_overflow_error("buffer flags out of range"))?;
1949 Ok(crate::protocol::BufferFlags::from_bits_retain(flags as u32))
1950}
1951
1952#[pyclass(name = "wrapper_descriptor", module = false)]
1955#[derive(Debug)]
1956pub(crate) struct PyWrapper {
1957 #[pymember(name = "__objclass__")]
1958 pub typ: &'static Py<PyType>,
1959 #[pymember(name = "__name__")]
1960 pub name: &'static PyStrInterned,
1961 pub wrapped: SlotFunc,
1962 pub doc: Option<&'static str>,
1964 pub plain_off: u32,
1966 pub plain_len: u32,
1967}
1968
1969impl PyPayload for PyWrapper {
1970 fn class(ctx: &Context) -> &'static Py<PyType> {
1971 ctx.types.wrapper_descriptor_type
1972 }
1973}
1974
1975impl GetDescriptor for PyWrapper {
1976 fn descr_get(
1977 zelf: &PyObject,
1978 obj: Option<&PyObject>,
1979 _cls: Option<&PyObject>,
1980 vm: &VirtualMachine,
1981 ) -> PyResult {
1982 match obj {
1983 None => Ok(zelf.to_owned()),
1984 Some(obj) => {
1985 let zelf = zelf.to_owned().downcast::<Self>().unwrap();
1986 Ok(PyMethodWrapper {
1987 wrapper: zelf,
1988 obj: obj.to_owned(),
1989 }
1990 .into_pyobject(vm))
1991 }
1992 }
1993 }
1994}
1995
1996impl Callable for PyWrapper {
1997 type Args = FuncArgs;
1998
1999 fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
2000 let (obj, rest): (PyObjectRef, FuncArgs) = args.bind(vm)?;
2002
2003 if !obj.fast_isinstance(zelf.typ) {
2004 return Err(vm.new_type_error(format!(
2005 "descriptor '{}' requires a '{}' object but received a '{}'",
2006 zelf.name.as_str(),
2007 zelf.typ.name(),
2008 obj.class().name()
2009 )));
2010 }
2011
2012 zelf.wrapped.call(obj, rest, vm)
2013 }
2014}
2015
2016#[pyclass(
2017 with(GetDescriptor, Callable, Representable),
2018 flags(DISALLOW_INSTANTIATION)
2019)]
2020impl Py<PyWrapper> {
2021 #[pygetset]
2022 fn __qualname__(&self) -> String {
2023 format!("{}.{}", self.typ.name(), self.name)
2024 }
2025
2026 #[pygetset]
2027 fn __doc__(&self) -> Option<&'static str> {
2028 if self.plain_len != 0 {
2029 return crate::function::db_doc(self.plain_off, self.plain_len);
2030 }
2031 let doc = self.doc?;
2032 type_::get_doc_from_internal_doc(self.name.as_str(), doc)
2033 }
2034
2035 #[pygetset]
2036 fn __text_signature__(&self) -> Option<String> {
2037 self.doc.and_then(|doc| {
2038 type_::get_text_signature_from_internal_doc(self.name.as_str(), doc)
2039 .map(|signature| signature.to_string())
2040 })
2041 }
2042}
2043
2044impl Representable for PyWrapper {
2045 #[inline]
2046 fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
2047 Ok(format!(
2048 "<slot wrapper '{}' of '{}' objects>",
2049 zelf.name.as_str(),
2050 zelf.typ.name()
2051 ))
2052 }
2053}
2054
2055#[pyclass(name = "method-wrapper", module = false, traverse)]
2060#[derive(Debug)]
2061pub(crate) struct PyMethodWrapper {
2062 pub wrapper: PyRef<PyWrapper>,
2063 #[pymember(name = "__self__")]
2064 #[pytraverse(skip)]
2065 pub obj: PyObjectRef,
2066}
2067
2068impl PyPayload for PyMethodWrapper {
2069 fn class(ctx: &Context) -> &'static Py<PyType> {
2070 ctx.types.method_wrapper_type
2071 }
2072}
2073
2074impl Callable for PyMethodWrapper {
2075 type Args = FuncArgs;
2076
2077 fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
2078 if !zelf.obj.fast_isinstance(zelf.wrapper.typ) {
2080 return Err(vm.new_type_error(format!(
2081 "descriptor '{}' requires a '{}' object but received a '{}'",
2082 zelf.wrapper.name.as_str(),
2083 zelf.wrapper.typ.name(),
2084 zelf.obj.class().name()
2085 )));
2086 }
2087 zelf.wrapper.wrapped.call(zelf.obj.clone(), args, vm)
2088 }
2089}
2090
2091#[pyclass(
2092 with(Callable, Representable, Hashable, Comparable),
2093 flags(DISALLOW_INSTANTIATION)
2094)]
2095impl Py<PyMethodWrapper> {
2096 #[pygetset]
2097 fn __name__(&self) -> &'static PyStrInterned {
2098 self.wrapper.name
2099 }
2100
2101 #[pygetset]
2102 fn __objclass__(&self) -> PyTypeRef {
2103 self.wrapper.typ.to_owned()
2104 }
2105
2106 #[pygetset]
2107 fn __qualname__(&self) -> String {
2108 format!("{}.{}", self.wrapper.typ.name(), self.wrapper.name)
2109 }
2110
2111 #[pygetset]
2112 fn __doc__(&self) -> Option<&'static str> {
2113 if self.wrapper.plain_len != 0 {
2114 return crate::function::db_doc(self.wrapper.plain_off, self.wrapper.plain_len);
2115 }
2116 let doc = self.wrapper.doc?;
2117 type_::get_doc_from_internal_doc(self.wrapper.name.as_str(), doc)
2118 }
2119
2120 #[pygetset]
2121 fn __text_signature__(&self) -> Option<String> {
2122 self.wrapper.doc.and_then(|doc| {
2123 type_::get_text_signature_from_internal_doc(self.wrapper.name.as_str(), doc)
2124 .map(|signature| signature.to_string())
2125 })
2126 }
2127
2128 #[pymethod]
2129 fn __reduce__(zelf: PyRef<PyMethodWrapper>, vm: &VirtualMachine) -> PyResult {
2130 let builtins_getattr = vm.builtins.get_attr("getattr", vm)?;
2131 Ok(vm
2132 .ctx
2133 .new_tuple(vec![
2134 builtins_getattr,
2135 vm.ctx
2136 .new_tuple(vec![
2137 zelf.obj.clone(),
2138 vm.ctx.new_str(zelf.wrapper.name.as_str()).into(),
2139 ])
2140 .into(),
2141 ])
2142 .into())
2143 }
2144}
2145
2146impl Representable for PyMethodWrapper {
2147 #[inline]
2148 fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
2149 Ok(format!(
2150 "<method-wrapper '{}' of {} object at {:#x}>",
2151 zelf.wrapper.name.as_str(),
2152 zelf.obj.class().name(),
2153 zelf.obj.get_id()
2154 ))
2155 }
2156}
2157
2158impl Hashable for PyMethodWrapper {
2159 fn hash(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyHash> {
2160 let mut hash =
2162 (zelf.wrapper.as_object().get_id() as PyHash) ^ (zelf.obj.get_id() as PyHash);
2163 if hash == -1 {
2164 hash = -2;
2165 }
2166 Ok(hash)
2167 }
2168}
2169
2170impl Comparable for PyMethodWrapper {
2171 fn cmp(
2172 zelf: &Py<Self>,
2173 other: &PyObject,
2174 op: PyComparisonOp,
2175 _vm: &VirtualMachine,
2176 ) -> PyResult<crate::function::PyComparisonValue> {
2177 op.eq_only(|| {
2178 let other = class_or_notimplemented!(Self, other);
2179 let eq = zelf.wrapper.is(&other.wrapper) && zelf.obj.is(&other.obj);
2180 Ok(eq.into())
2181 })
2182 }
2183}
2184
2185#[cfg(test)]
2186mod tests {
2187 use super::*;
2188 use crate::{protocol::PyNumberMethods, types::AsNumber};
2189 use core::sync::atomic::{AtomicU8, Ordering};
2190
2191 #[pyclass(name = "UByteMembers", module = false)]
2192 #[derive(Debug, PyPayload)]
2193 #[repr(C)]
2194 struct UByteMembers {
2195 prefix: u8,
2196 #[pymember]
2197 readonly: u8,
2198 #[pymember(writable)]
2199 writable: AtomicU8,
2200 suffix: u8,
2201 }
2202
2203 #[pyclass(with(AsNumber))]
2204 impl UByteMembers {}
2205
2206 impl AsNumber for UByteMembers {
2207 fn as_number() -> &'static PyNumberMethods {
2208 static METHODS: PyNumberMethods = PyNumberMethods {
2209 index: Some(|_, vm| Ok(vm.ctx.new_int(42).into())),
2210 ..PyNumberMethods::NOT_IMPLEMENTED
2211 };
2212 &METHODS
2213 }
2214 }
2215
2216 #[pyclass(name = "ReadonlyAtomicMembers", module = false)]
2217 #[derive(Debug, PyPayload)]
2218 struct ReadonlyAtomicMembers {
2219 #[pymember]
2220 byte: AtomicU8,
2221 #[pymember]
2222 object: crate::object::PyObjectCell,
2223 }
2224
2225 #[pyclass]
2226 impl ReadonlyAtomicMembers {}
2227
2228 #[test]
2229 fn readonly_atomic_members_use_atomic_storage() {
2230 crate::Interpreter::without_stdlib(Default::default()).enter(|vm| {
2231 let _class = ReadonlyAtomicMembers::make_static_type();
2232 let obj = ReadonlyAtomicMembers {
2233 byte: AtomicU8::new(128),
2234 object: None.into(),
2235 }
2236 .into_ref(&vm.ctx);
2237
2238 for name in ["byte", "object"] {
2239 let descriptor = obj.class().as_object().get_attr(name, vm).unwrap();
2240 let descriptor = descriptor.downcast_ref::<PyMemberDescriptor>().unwrap();
2241 assert!(descriptor.member.readonly());
2242 assert!(descriptor.member.atomic_storage());
2243 let err = obj
2244 .as_object()
2245 .set_attr(name, vm.ctx.none(), vm)
2246 .unwrap_err();
2247 assert!(err.fast_isinstance(vm.ctx.exceptions.attribute_error));
2248 }
2249
2250 assert!(vm.is_none(&obj.as_object().get_attr("object", vm).unwrap()));
2251 obj.byte.store(255, Ordering::Relaxed);
2252 let value: PyObjectRef = vm.ctx.new_int(42).into();
2253 obj.object.store(Some(value.clone()));
2254 assert!(obj.as_object().get_attr("object", vm).unwrap().is(&value));
2255 let byte = obj.as_object().get_attr("byte", vm).unwrap();
2256 assert_eq!(
2257 u8::try_from(
2258 byte.downcast_ref::<crate::builtins::PyInt>()
2259 .unwrap()
2260 .as_bigint()
2261 )
2262 .unwrap(),
2263 255
2264 );
2265 obj.object.store(None);
2266 assert!(vm.is_none(&obj.as_object().get_attr("object", vm).unwrap()));
2267 });
2268 }
2269
2270 #[test]
2271 fn unsigned_byte_members_preserve_adjacent_fields() {
2272 crate::Interpreter::without_stdlib(Default::default()).enter(|vm| {
2273 let _class = UByteMembers::make_static_type();
2274 let obj = UByteMembers {
2275 prefix: 17,
2276 readonly: 255,
2277 writable: AtomicU8::new(128),
2278 suffix: 29,
2279 }
2280 .into_ref(&vm.ctx);
2281
2282 assert_eq!(MemberKind::from_i32(9), Some(MemberKind::UByte));
2283 for (name, expected) in [("readonly", 255), ("writable", 128)] {
2284 let value = obj.as_object().get_attr(name, vm).unwrap();
2285 let value = value.downcast_ref::<crate::builtins::PyInt>().unwrap();
2286 assert_eq!(i32::try_from(value.as_bigint()).unwrap(), expected);
2287 }
2288 obj.as_object()
2289 .set_attr("writable", vm.ctx.new_int(255), vm)
2290 .unwrap();
2291 assert_eq!(obj.writable.load(Ordering::Relaxed), 255);
2292 assert_eq!((obj.prefix, obj.readonly, obj.suffix), (17, 255, 29));
2293
2294 let err = obj
2295 .as_object()
2296 .set_attr("readonly", vm.ctx.new_int(0), vm)
2297 .unwrap_err();
2298 assert!(err.fast_isinstance(vm.ctx.exceptions.attribute_error));
2299 let err = obj.as_object().del_attr("writable", vm).unwrap_err();
2300 assert!(err.fast_isinstance(vm.ctx.exceptions.type_error));
2301 assert_eq!(obj.writable.load(Ordering::Relaxed), 255);
2302
2303 let err = obj
2304 .as_object()
2305 .set_attr("writable", vm.ctx.new_int(i128::MAX), vm)
2306 .unwrap_err();
2307 assert!(err.fast_isinstance(vm.ctx.exceptions.overflow_error));
2308 assert_eq!(obj.writable.load(Ordering::Relaxed), 255);
2309
2310 obj.as_object()
2311 .set_attr("writable", obj.to_owned(), vm)
2312 .unwrap();
2313 assert_eq!(obj.writable.load(Ordering::Relaxed), 42);
2314
2315 let filters = vm.state.warnings.filters.to_owned();
2316 filters.borrow_vec_mut().insert(
2317 0,
2318 vm.ctx
2319 .new_tuple(vec![
2320 vm.ctx.new_str("error").into(),
2321 vm.ctx.none(),
2322 vm.ctx.exceptions.runtime_warning.to_owned().into(),
2323 vm.ctx.none(),
2324 vm.ctx.new_int(0).into(),
2325 ])
2326 .into(),
2327 );
2328 vm.state.warnings.filters_mutated();
2329 for (input, expected) in [(256, 0), (-1, 255)] {
2330 let err = obj
2331 .as_object()
2332 .set_attr("writable", vm.ctx.new_int(input), vm)
2333 .unwrap_err();
2334 assert!(err.fast_isinstance(vm.ctx.exceptions.runtime_warning));
2335 assert_eq!(obj.writable.load(Ordering::Relaxed), expected);
2337 assert_eq!((obj.prefix, obj.readonly, obj.suffix), (17, 255, 29));
2338 }
2339 });
2340 }
2341}