1use super::{
2 PyClassMethod, PyDictRef, PyList, PyStaticMethod, PyStr, PyStrInterned, PyStrRef, PyTupleRef,
3 PyUtf8StrRef, PyWeak, mappingproxy::PyMappingProxy, object, union_,
4};
5use crate::{
6 AsObject, Context, Py, PyAtomicRef, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
7 TryFromObject, VirtualMachine,
8 builtins::{
9 PyBaseExceptionRef,
10 descriptor::{
11 MemberGetter, MemberKind, MemberSetter, PyDescriptorOwned, PyMemberDef,
12 PyMemberDescriptor,
13 },
14 function::{PyCellRef, PyFunction},
15 tuple::{IntoPyTuple, PyTuple},
16 },
17 class::{PyClassImpl, StaticType},
18 common::{
19 ascii,
20 borrow::BorrowedValue,
21 lock::{PyMutex, PyRwLock, PyRwLockReadGuard},
22 },
23 function::{FuncArgs, KwArgs, OptionalArg, PyMethodDef, PySetterValue},
24 object::{Traverse, TraverseFn},
25 protocol::{PyIterReturn, PyNumberMethods},
26 types::{
27 AsNumber, Callable, Constructor, GetAttr, Initializer, PyTypeFlags, PyTypeSlots,
28 Representable, SLOT_DEFS, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot,
29 },
30};
31use core::{
32 any::Any,
33 borrow::Borrow,
34 ops::Deref,
35 pin::Pin,
36 ptr::NonNull,
37 sync::atomic::{AtomicBool, AtomicPtr, AtomicU32, Ordering},
38};
39use indexmap::{IndexMap, map::Entry};
40use itertools::Itertools;
41use num_traits::ToPrimitive;
42use rustpython_common::wtf8::Wtf8;
43use std::collections::HashSet;
44
45#[pyclass(module = false, name = "type", traverse = "manual")]
46pub struct PyType {
47 pub base: Option<PyTypeRef>,
48 pub bases: PyRwLock<Vec<PyTypeRef>>,
49 pub mro: PyRwLock<Vec<PyTypeRef>>,
50 pub subclasses: PyRwLock<Vec<PyRef<PyWeak>>>,
51 pub attributes: PyRwLock<PyAttributes>,
52 pub slots: PyTypeSlots,
53 pub heaptype_ext: Option<Pin<Box<HeapTypeExt>>>,
54 pub tp_version_tag: AtomicU32,
56}
57
58static NEXT_TYPE_VERSION: AtomicU32 = AtomicU32::new(1);
63
64const TYPE_CACHE_SIZE_EXP: u32 = 12;
73const TYPE_CACHE_SIZE: usize = 1 << TYPE_CACHE_SIZE_EXP;
74const TYPE_CACHE_MASK: usize = TYPE_CACHE_SIZE - 1;
75
76struct TypeCacheEntry {
77 sequence: AtomicU32,
79 version: AtomicU32,
81 name: AtomicPtr<PyStrInterned>,
83 value: AtomicPtr<PyObject>,
91}
92
93unsafe impl Send for TypeCacheEntry {}
98unsafe impl Sync for TypeCacheEntry {}
99
100impl TypeCacheEntry {
101 fn new() -> Self {
102 Self {
103 sequence: AtomicU32::new(0),
104 version: AtomicU32::new(0),
105 name: AtomicPtr::new(core::ptr::null_mut()),
106 value: AtomicPtr::new(core::ptr::null_mut()),
107 }
108 }
109
110 #[inline]
111 fn begin_write(&self) {
112 let mut seq = self.sequence.load(Ordering::Acquire);
113 loop {
114 while (seq & 1) != 0 {
115 core::hint::spin_loop();
116 seq = self.sequence.load(Ordering::Acquire);
117 }
118 match self.sequence.compare_exchange_weak(
119 seq,
120 seq.wrapping_add(1),
121 Ordering::AcqRel,
122 Ordering::Acquire,
123 ) {
124 Ok(_) => {
125 core::sync::atomic::fence(Ordering::Release);
126 break;
127 }
128 Err(observed) => {
129 core::hint::spin_loop();
130 seq = observed;
131 }
132 }
133 }
134 }
135
136 #[inline]
137 fn end_write(&self) {
138 self.sequence.fetch_add(1, Ordering::Release);
139 }
140
141 #[inline]
142 fn begin_read(&self) -> u32 {
143 let mut sequence = self.sequence.load(Ordering::Acquire);
144 while (sequence & 1) != 0 {
145 core::hint::spin_loop();
146 sequence = self.sequence.load(Ordering::Acquire);
147 }
148 sequence
149 }
150
151 #[inline]
152 fn end_read(&self, previous: u32) -> bool {
153 core::sync::atomic::fence(Ordering::Acquire);
154 self.sequence.load(Ordering::Relaxed) == previous
155 }
156
157 fn clear_value(&self) {
161 self.value.store(core::ptr::null_mut(), Ordering::Relaxed);
162 }
163}
164
165static TYPE_CACHE: std::sync::LazyLock<Box<[TypeCacheEntry]>> = std::sync::LazyLock::new(|| {
169 (0..TYPE_CACHE_SIZE)
170 .map(|_| TypeCacheEntry::new())
171 .collect::<Vec<_>>()
172 .into_boxed_slice()
173});
174
175static TYPE_CACHE_CLEARING: AtomicBool = AtomicBool::new(false);
178
179#[inline]
181fn type_cache_hash(version: u32, name: &'static PyStrInterned) -> usize {
182 let name_hash = (name as *const PyStrInterned as usize >> 3) as u32;
183 ((version ^ name_hash) as usize) & TYPE_CACHE_MASK
184}
185
186fn type_cache_clear_version(version: u32) {
189 for entry in TYPE_CACHE.iter() {
190 if entry.version.load(Ordering::Relaxed) == version {
191 entry.begin_write();
192 if entry.version.load(Ordering::Relaxed) == version {
193 entry.version.store(0, Ordering::Release);
194 entry.clear_value();
195 }
196 entry.end_write();
197 }
198 }
199}
200
201pub fn type_cache_clear() {
209 TYPE_CACHE_CLEARING.store(true, Ordering::Release);
210 for entry in TYPE_CACHE.iter() {
211 entry.begin_write();
212 entry.version.store(0, Ordering::Release);
213 entry.clear_value();
214 entry.end_write();
215 }
216 TYPE_CACHE_CLEARING.store(false, Ordering::Release);
217}
218
219unsafe impl crate::object::Traverse for PyType {
220 fn traverse(&self, tracer_fn: &mut crate::object::TraverseFn<'_>) {
221 self.base.traverse(tracer_fn);
222 self.bases.traverse(tracer_fn);
223 self.mro.traverse(tracer_fn);
224 self.subclasses.traverse(tracer_fn);
225 self.attributes
226 .read_recursive()
227 .iter()
228 .map(|(_, v)| v.traverse(tracer_fn))
229 .count();
230 if let Some(ext) = self.heaptype_ext.as_ref() {
231 ext.specialization_cache.traverse(tracer_fn);
232 }
233 }
234
235 fn clear(&mut self, out: &mut Vec<crate::PyObjectRef>) {
237 if let Some(base) = self.base.take() {
238 out.push(base.into());
239 }
240 if let Some(mut guard) = self.bases.try_write() {
241 for base in guard.drain(..) {
242 out.push(base.into());
243 }
244 }
245 if let Some(mut guard) = self.mro.try_write() {
246 for typ in guard.drain(..) {
247 out.push(typ.into());
248 }
249 }
250 if let Some(mut guard) = self.subclasses.try_write() {
251 for weak in guard.drain(..) {
252 out.push(weak.into());
253 }
254 }
255 if let Some(mut guard) = self.attributes.try_write() {
256 for (_, val) in guard.drain(..) {
257 out.push(val);
258 }
259 }
260 if let Some(ext) = self.heaptype_ext.as_ref() {
261 ext.specialization_cache.clear_into(out);
262 }
263 }
264}
265
266pub struct HeapTypeExt {
268 pub name: PyRwLock<PyUtf8StrRef>,
269 pub qualname: PyRwLock<PyStrRef>,
270 pub slots: Option<PyRef<PyTuple<PyStrRef>>>,
271 pub type_data: PyRwLock<Option<TypeDataSlot>>,
272 pub specialization_cache: TypeSpecializationCache,
273}
274
275pub struct TypeSpecializationCache {
276 pub init: PyAtomicRef<Option<PyFunction>>,
277 pub getitem: PyAtomicRef<Option<PyFunction>>,
278 pub getitem_version: AtomicU32,
279 write_lock: PyMutex<()>,
281 retired: PyRwLock<Vec<PyObjectRef>>,
282}
283
284impl TypeSpecializationCache {
285 fn new() -> Self {
286 Self {
287 init: PyAtomicRef::from(None::<PyRef<PyFunction>>),
288 getitem: PyAtomicRef::from(None::<PyRef<PyFunction>>),
289 getitem_version: AtomicU32::new(0),
290 write_lock: PyMutex::new(()),
291 retired: PyRwLock::new(Vec::new()),
292 }
293 }
294
295 #[inline]
296 fn retire_old_function(&self, old: Option<PyRef<PyFunction>>) {
297 if let Some(old) = old {
298 self.retired.write().push(old.into());
299 }
300 }
301
302 #[inline]
303 fn swap_init(&self, new_init: Option<PyRef<PyFunction>>, vm: Option<&VirtualMachine>) {
304 if let Some(vm) = vm {
305 self.init.swap_to_temporary_refs(new_init, vm);
309 return;
310 }
311 let old = unsafe { self.init.swap(new_init) };
314 self.retire_old_function(old);
315 }
316
317 #[inline]
318 fn swap_getitem(&self, new_getitem: Option<PyRef<PyFunction>>, vm: Option<&VirtualMachine>) {
319 if let Some(vm) = vm {
320 self.getitem.swap_to_temporary_refs(new_getitem, vm);
321 return;
322 }
323 let old = unsafe { self.getitem.swap(new_getitem) };
326 self.retire_old_function(old);
327 }
328
329 #[inline]
330 fn invalidate_for_type_modified(&self) {
331 let _guard = self.write_lock.lock();
332 self.swap_init(None, None);
335 self.swap_getitem(None, None);
336 }
337
338 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
339 if let Some(init) = self.init.deref() {
340 tracer_fn(init.as_object());
341 }
342 if let Some(getitem) = self.getitem.deref() {
343 tracer_fn(getitem.as_object());
344 }
345 self.retired
346 .read()
347 .iter()
348 .map(|obj| obj.traverse(tracer_fn))
349 .count();
350 }
351
352 fn clear_into(&self, out: &mut Vec<PyObjectRef>) {
353 let _guard = self.write_lock.lock();
354 let old_init = unsafe { self.init.swap(None) };
355 if let Some(old_init) = old_init {
356 out.push(old_init.into());
357 }
358 let old_getitem = unsafe { self.getitem.swap(None) };
359 if let Some(old_getitem) = old_getitem {
360 out.push(old_getitem.into());
361 }
362 self.getitem_version.store(0, Ordering::Release);
363 out.extend(self.retired.write().drain(..));
364 }
365}
366
367pub struct PointerSlot<T>(NonNull<T>);
368
369unsafe impl<T> Sync for PointerSlot<T> {}
370unsafe impl<T> Send for PointerSlot<T> {}
371
372impl<T> PointerSlot<T> {
373 pub const unsafe fn borrow_static(&self) -> &'static T {
374 unsafe { self.0.as_ref() }
375 }
376}
377
378impl<T> Clone for PointerSlot<T> {
379 fn clone(&self) -> Self {
380 *self
381 }
382}
383
384impl<T> Copy for PointerSlot<T> {}
385
386impl<T> From<&'static T> for PointerSlot<T> {
387 fn from(x: &'static T) -> Self {
388 Self(NonNull::from(x))
389 }
390}
391
392impl<T> AsRef<T> for PointerSlot<T> {
393 fn as_ref(&self) -> &T {
394 unsafe { self.0.as_ref() }
395 }
396}
397
398pub type PyTypeRef = PyRef<PyType>;
399
400cfg_if::cfg_if! {
401 if #[cfg(feature = "threading")] {
402 unsafe impl Send for PyType {}
403 unsafe impl Sync for PyType {}
404 }
405}
406
407pub type PyAttributes = IndexMap<&'static PyStrInterned, PyObjectRef, ahash::RandomState>;
411
412unsafe impl Traverse for PyAttributes {
413 fn traverse(&self, tracer_fn: &mut TraverseFn<'_>) {
414 self.values().for_each(|v| v.traverse(tracer_fn));
415 }
416}
417
418impl core::fmt::Display for PyType {
419 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
420 core::fmt::Display::fmt(&self.name(), f)
421 }
422}
423
424impl core::fmt::Debug for PyType {
425 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
426 write!(f, "[PyType {}]", &self.name())
427 }
428}
429
430impl PyPayload for PyType {
431 #[inline]
432 fn class(ctx: &Context) -> &'static Py<PyType> {
433 ctx.types.type_type
434 }
435}
436
437fn downcast_qualname(value: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
438 match value.downcast::<PyStr>() {
439 Ok(value) => Ok(value),
440 Err(value) => Err(vm.new_type_error(format!(
441 "can only assign string to __qualname__, not '{}'",
442 value.class().name()
443 ))),
444 }
445}
446
447fn is_subtype_with_mro(a_mro: &[PyTypeRef], a: &Py<PyType>, b: &Py<PyType>) -> bool {
448 if a.is(b) {
449 return true;
450 }
451 for item in a_mro {
452 if item.is(b) {
453 return true;
454 }
455 }
456 false
457}
458
459impl PyType {
460 pub fn assign_version_tag(&self) -> u32 {
463 let v = self.tp_version_tag.load(Ordering::Acquire);
464 if v != 0 {
465 return v;
466 }
467
468 for base in self.bases.read().iter() {
470 if base.assign_version_tag() == 0 {
471 return 0;
472 }
473 }
474
475 loop {
476 let current = NEXT_TYPE_VERSION.load(Ordering::Relaxed);
477 let Some(next) = current.checked_add(1) else {
478 return 0; };
480 if NEXT_TYPE_VERSION
481 .compare_exchange_weak(current, next, Ordering::Relaxed, Ordering::Relaxed)
482 .is_ok()
483 {
484 self.tp_version_tag.store(current, Ordering::Release);
485 return current;
486 }
487 }
488 }
489
490 pub fn modified(&self) {
492 if let Some(ext) = self.heaptype_ext.as_ref() {
493 ext.specialization_cache.invalidate_for_type_modified();
494 }
495 let old_version = self.tp_version_tag.load(Ordering::Acquire);
498 if old_version == 0 {
499 return;
500 }
501 self.tp_version_tag.store(0, Ordering::SeqCst);
502 type_cache_clear_version(old_version);
505 let subclasses = self.subclasses.read();
506 for weak_ref in subclasses.iter() {
507 if let Some(sub) = weak_ref.upgrade() {
508 sub.downcast_ref::<PyType>().unwrap().modified();
509 }
510 }
511 }
512
513 pub fn new_simple_heap(
514 name: &str,
515 base: &Py<PyType>,
516 ctx: &Context,
517 ) -> Result<PyRef<Self>, String> {
518 Self::new_heap(
519 name,
520 vec![base.to_owned()],
521 Default::default(),
522 Default::default(),
523 Self::static_type().to_owned(),
524 ctx,
525 )
526 }
527 pub fn new_heap(
528 name: &str,
529 bases: Vec<PyRef<Self>>,
530 attrs: PyAttributes,
531 mut slots: PyTypeSlots,
532 metaclass: PyRef<Self>,
533 ctx: &Context,
534 ) -> Result<PyRef<Self>, String> {
535 slots.flags |= PyTypeFlags::HEAPTYPE;
540
541 let name_utf8 = ctx.new_utf8_str(name);
542 let name = name_utf8.clone().into_wtf8();
543 let heaptype_ext = HeapTypeExt {
544 name: PyRwLock::new(name_utf8),
545 qualname: PyRwLock::new(name),
546 slots: None,
547 type_data: PyRwLock::new(None),
548 specialization_cache: TypeSpecializationCache::new(),
549 };
550 let base = bases[0].clone();
551
552 Self::new_heap_inner(base, bases, attrs, slots, heaptype_ext, metaclass, ctx)
553 }
554
555 pub(crate) fn check(obj: &PyObject) -> Option<&Py<Self>> {
558 obj.downcast_ref::<Self>()
559 }
560
561 fn resolve_mro(bases: &[PyRef<Self>]) -> Result<Vec<PyTypeRef>, String> {
562 let mut unique_bases = HashSet::new();
564 for base in bases {
565 if !unique_bases.insert(base.get_id()) {
566 return Err(format!("duplicate base class {}", base.name()));
567 }
568 }
569
570 let mros = bases
571 .iter()
572 .map(|base| base.mro_map_collect(|t| t.to_owned()))
573 .collect();
574 linearise_mro(mros)
575 }
576
577 fn inherit_patma_flags(slots: &mut PyTypeSlots, bases: &[PyRef<Self>]) {
580 const COLLECTION_FLAGS: PyTypeFlags = PyTypeFlags::from_bits_truncate(
581 PyTypeFlags::SEQUENCE.bits() | PyTypeFlags::MAPPING.bits(),
582 );
583
584 if slots.flags.intersects(COLLECTION_FLAGS) {
586 return;
587 }
588
589 for base in bases {
591 let base_flags = base.slots.flags & COLLECTION_FLAGS;
592 if !base_flags.is_empty() {
593 slots.flags |= base_flags;
594 return;
595 }
596 }
597 }
598
599 fn check_abc_tpflags(
602 slots: &mut PyTypeSlots,
603 attrs: &PyAttributes,
604 bases: &[PyRef<Self>],
605 ctx: &Context,
606 ) {
607 const COLLECTION_FLAGS: PyTypeFlags = PyTypeFlags::from_bits_truncate(
608 PyTypeFlags::SEQUENCE.bits() | PyTypeFlags::MAPPING.bits(),
609 );
610
611 if slots.flags.intersects(COLLECTION_FLAGS) {
613 return;
614 }
615
616 let abc_tpflags_name = ctx.intern_str("__abc_tpflags__");
618 if let Some(abc_tpflags_obj) = attrs.get(abc_tpflags_name)
619 && let Some(int_obj) = abc_tpflags_obj.downcast_ref::<crate::builtins::int::PyInt>()
620 {
621 let flags_val = int_obj.as_bigint().to_i64().unwrap_or(0);
622 let abc_flags = PyTypeFlags::from_bits_truncate(flags_val as u64);
623 slots.flags |= abc_flags & COLLECTION_FLAGS;
624 return;
625 }
626
627 for base in bases {
629 if let Some(abc_tpflags_obj) = base.find_name_in_mro(abc_tpflags_name)
630 && let Some(int_obj) = abc_tpflags_obj.downcast_ref::<crate::builtins::int::PyInt>()
631 {
632 let flags_val = int_obj.as_bigint().to_i64().unwrap_or(0);
633 let abc_flags = PyTypeFlags::from_bits_truncate(flags_val as u64);
634 slots.flags |= abc_flags & COLLECTION_FLAGS;
635 return;
636 }
637 }
638 }
639
640 #[allow(clippy::too_many_arguments)]
641 fn new_heap_inner(
642 base: PyRef<Self>,
643 bases: Vec<PyRef<Self>>,
644 attrs: PyAttributes,
645 mut slots: PyTypeSlots,
646 heaptype_ext: HeapTypeExt,
647 metaclass: PyRef<Self>,
648 ctx: &Context,
649 ) -> Result<PyRef<Self>, String> {
650 let mro = Self::resolve_mro(&bases)?;
651
652 if mro
655 .iter()
656 .any(|b| b.slots.flags.has_feature(PyTypeFlags::HAS_DICT))
657 {
658 slots.flags |= PyTypeFlags::HAS_DICT
659 }
660
661 if mro
663 .iter()
664 .any(|b| b.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF))
665 {
666 slots.flags |= PyTypeFlags::HAS_WEAKREF | PyTypeFlags::MANAGED_WEAKREF
667 }
668
669 Self::inherit_patma_flags(&mut slots, &bases);
671
672 Self::check_abc_tpflags(&mut slots, &attrs, &bases, ctx);
674
675 if slots.basicsize == 0 {
676 slots.basicsize = base.slots.basicsize;
677 }
678
679 Self::inherit_readonly_slots(&mut slots, &base);
680
681 if slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF) {
683 slots.flags |= PyTypeFlags::MANAGED_WEAKREF;
684 }
685
686 if let Some(qualname) = attrs.get(identifier!(ctx, __qualname__))
687 && !qualname.fast_isinstance(ctx.types.str_type)
688 {
689 return Err(format!(
690 "type __qualname__ must be a str, not {}",
691 qualname.class().name()
692 ));
693 }
694
695 let new_type = PyRef::new_ref(
696 Self {
697 base: Some(base),
698 bases: PyRwLock::new(bases),
699 mro: PyRwLock::new(mro),
700 subclasses: PyRwLock::default(),
701 attributes: PyRwLock::new(attrs),
702 slots,
703 heaptype_ext: Some(Pin::new(Box::new(heaptype_ext))),
704 tp_version_tag: AtomicU32::new(0),
705 },
706 metaclass,
707 None,
708 );
709 new_type.mro.write().insert(0, new_type.clone());
710
711 new_type.init_slots(ctx);
712
713 let weakref_type = super::PyWeak::static_type();
714 for base in new_type.bases.read().iter() {
715 base.subclasses.write().push(
716 new_type
717 .as_object()
718 .downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
719 .unwrap(),
720 );
721 }
722
723 Ok(new_type)
724 }
725
726 pub fn new_static(
727 base: PyRef<Self>,
728 attrs: PyAttributes,
729 mut slots: PyTypeSlots,
730 metaclass: PyRef<Self>,
731 ) -> Result<PyRef<Self>, String> {
732 if base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
733 slots.flags |= PyTypeFlags::HAS_DICT
734 }
735 if base.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF) {
736 slots.flags |= PyTypeFlags::HAS_WEAKREF | PyTypeFlags::MANAGED_WEAKREF
737 }
738
739 Self::inherit_patma_flags(&mut slots, core::slice::from_ref(&base));
742
743 if slots.basicsize == 0 {
744 slots.basicsize = base.slots.basicsize;
745 }
746
747 Self::inherit_readonly_slots(&mut slots, &base);
748
749 if slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF) {
751 slots.flags |= PyTypeFlags::MANAGED_WEAKREF;
752 }
753
754 let bases = PyRwLock::new(vec![base.clone()]);
755 let mro = base.mro_map_collect(|x| x.to_owned());
756
757 let new_type = PyRef::new_ref(
758 Self {
759 base: Some(base),
760 bases,
761 mro: PyRwLock::new(mro),
762 subclasses: PyRwLock::default(),
763 attributes: PyRwLock::new(attrs),
764 slots,
765 heaptype_ext: None,
766 tp_version_tag: AtomicU32::new(0),
767 },
768 metaclass,
769 None,
770 );
771
772 unsafe {
775 crate::gc_state::gc_state()
776 .untrack_object(core::ptr::NonNull::from(new_type.as_object()));
777 }
778 new_type.as_object().clear_gc_tracked();
779
780 new_type.mro.write().insert(0, new_type.clone());
781
782 Self::set_new(&new_type.slots, &new_type.base);
786 Self::set_alloc(&new_type.slots, &new_type.base);
787
788 let weakref_type = super::PyWeak::static_type();
789 for base in new_type.bases.read().iter() {
790 base.subclasses.write().push(
791 new_type
792 .as_object()
793 .downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
794 .unwrap(),
795 );
796 }
797
798 Ok(new_type)
799 }
800
801 pub(crate) fn init_slots(&self, ctx: &Context) {
802 let mro: Vec<_> = self.mro.read()[1..].to_vec();
804 for base in mro.iter() {
805 self.inherit_slots(base);
806 }
807
808 #[allow(clippy::mutable_key_type)]
810 let mut slot_name_set = std::collections::HashSet::new();
811
812 for cls in self.mro.read()[1..].iter() {
814 for &name in cls.attributes.read().keys() {
815 if name.as_bytes().starts_with(b"__") && name.as_bytes().ends_with(b"__") {
816 slot_name_set.insert(name);
817 }
818 }
819 }
820 for &name in self.attributes.read().keys() {
821 if name.as_bytes().starts_with(b"__") && name.as_bytes().ends_with(b"__") {
822 slot_name_set.insert(name);
823 }
824 }
825 let mut slot_names: Vec<_> = slot_name_set.into_iter().collect();
827 slot_names.sort_by_key(|name| name.as_str());
828 for attr_name in slot_names {
829 self.update_slot::<true>(attr_name, ctx);
830 }
831
832 Self::set_new(&self.slots, &self.base);
833 Self::set_alloc(&self.slots, &self.base);
834 }
835
836 fn set_new(slots: &PyTypeSlots, base: &Option<PyTypeRef>) {
837 if slots.flags.contains(PyTypeFlags::DISALLOW_INSTANTIATION) {
838 slots.new.store(None)
839 } else if slots.new.load().is_none() {
840 slots.new.store(
841 base.as_ref()
842 .map(|base| base.slots.new.load())
843 .unwrap_or(None),
844 )
845 }
846 }
847
848 fn set_alloc(slots: &PyTypeSlots, base: &Option<PyTypeRef>) {
849 if slots.alloc.load().is_none() {
850 slots.alloc.store(
851 base.as_ref()
852 .map(|base| base.slots.alloc.load())
853 .unwrap_or(None),
854 );
855 }
856 }
857
858 fn inherit_readonly_slots(slots: &mut PyTypeSlots, base: &Self) {
861 if slots.as_buffer.is_none() {
862 slots.as_buffer = base.slots.as_buffer;
863 }
864 }
865
866 pub(crate) fn inherit_slots(&self, base: &Self) {
868 for def in SLOT_DEFS {
871 def.accessor.copyslot_if_none(self, base);
872 }
873 }
874
875 pub fn set_str_attr<V: Into<PyObjectRef>>(
877 &self,
878 attr_name: &str,
879 value: V,
880 ctx: impl AsRef<Context>,
881 ) {
882 let ctx = ctx.as_ref();
883 let attr_name = ctx.intern_str(attr_name);
884 self.set_attr(attr_name, value.into())
885 }
886
887 pub fn set_attr(&self, attr_name: &'static PyStrInterned, value: PyObjectRef) {
888 self.modified();
892 self.attributes.write().insert(attr_name, value);
893 }
894
895 pub fn get_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
898 self.find_name_in_mro(attr_name)
899 }
900
901 pub(crate) fn cache_init_for_specialization(
904 &self,
905 init: PyRef<PyFunction>,
906 tp_version: u32,
907 vm: &VirtualMachine,
908 ) -> bool {
909 let Some(ext) = self.heaptype_ext.as_ref() else {
910 return false;
911 };
912 if tp_version == 0 {
913 return false;
914 }
915 if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
916 return false;
917 }
918 let _guard = ext.specialization_cache.write_lock.lock();
919 if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
920 return false;
921 }
922 ext.specialization_cache.swap_init(Some(init), Some(vm));
923 true
924 }
925
926 pub(crate) fn get_cached_init_for_specialization(
928 &self,
929 tp_version: u32,
930 ) -> Option<PyRef<PyFunction>> {
931 let ext = self.heaptype_ext.as_ref()?;
932 if tp_version == 0 {
933 return None;
934 }
935 if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
936 return None;
937 }
938 ext.specialization_cache
939 .init
940 .to_owned_ordering(Ordering::Acquire)
941 }
942
943 pub(crate) fn cache_getitem_for_specialization(
946 &self,
947 getitem: PyRef<PyFunction>,
948 tp_version: u32,
949 vm: &VirtualMachine,
950 ) -> bool {
951 let Some(ext) = self.heaptype_ext.as_ref() else {
952 return false;
953 };
954 if tp_version == 0 {
955 return false;
956 }
957 let _guard = ext.specialization_cache.write_lock.lock();
958 if self.tp_version_tag.load(Ordering::Acquire) != tp_version {
959 return false;
960 }
961 let func_version = getitem.get_version_for_current_state();
962 if func_version == 0 {
963 return false;
964 }
965 ext.specialization_cache
966 .swap_getitem(Some(getitem), Some(vm));
967 ext.specialization_cache
968 .getitem_version
969 .store(func_version, Ordering::Relaxed);
970 true
971 }
972
973 pub(crate) fn get_cached_getitem_for_specialization(&self) -> Option<(PyRef<PyFunction>, u32)> {
975 let ext = self.heaptype_ext.as_ref()?;
976 let getitem = ext
978 .specialization_cache
979 .getitem
980 .to_owned_ordering(Ordering::Acquire)?;
981 let cached_version = ext
982 .specialization_cache
983 .getitem_version
984 .load(Ordering::Relaxed);
985 if cached_version == 0 {
986 return None;
987 }
988 Some((getitem, cached_version))
989 }
990
991 pub fn get_direct_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
992 self.attributes.read().get(attr_name).cloned()
993 }
994
995 fn find_name_in_mro(&self, name: &'static PyStrInterned) -> Option<PyObjectRef> {
1003 let version = self.tp_version_tag.load(Ordering::Acquire);
1004 if version != 0 {
1005 let idx = type_cache_hash(version, name);
1006 let entry = &TYPE_CACHE[idx];
1007 let name_ptr = name as *const _ as *mut _;
1008 loop {
1009 let seq1 = entry.begin_read();
1010 let v1 = entry.version.load(Ordering::Acquire);
1011 let type_version = self.tp_version_tag.load(Ordering::Acquire);
1012 if v1 != type_version
1013 || !core::ptr::eq(entry.name.load(Ordering::Relaxed), name_ptr)
1014 {
1015 break;
1016 }
1017 let ptr = entry.value.load(Ordering::Acquire);
1018 if ptr.is_null() {
1019 if entry.end_read(seq1) {
1020 break;
1021 }
1022 continue;
1023 }
1024 if let Some(cloned) = unsafe { PyObject::try_to_owned_from_ptr(ptr) } {
1027 let same_ptr = core::ptr::eq(entry.value.load(Ordering::Relaxed), ptr);
1028 if same_ptr && entry.end_read(seq1) {
1029 return Some(cloned);
1030 }
1031 drop(cloned);
1032 continue;
1033 }
1034 break;
1035 }
1036 }
1037
1038 let assigned = if version == 0 {
1041 self.assign_version_tag()
1042 } else {
1043 version
1044 };
1045
1046 let result = self.find_name_in_mro_uncached(name);
1048
1049 if let Some(ref found) = result
1053 && assigned != 0
1054 && !TYPE_CACHE_CLEARING.load(Ordering::Acquire)
1055 && self.tp_version_tag.load(Ordering::Acquire) == assigned
1056 {
1057 let idx = type_cache_hash(assigned, name);
1058 let entry = &TYPE_CACHE[idx];
1059 let name_ptr = name as *const _ as *mut _;
1060 entry.begin_write();
1061 entry.version.store(0, Ordering::Release);
1063 let new_ptr = &**found as *const PyObject as *mut PyObject;
1065 entry.value.store(new_ptr, Ordering::Relaxed);
1066 entry.name.store(name_ptr, Ordering::Relaxed);
1067 entry.version.store(assigned, Ordering::Release);
1069 entry.end_write();
1070 }
1071
1072 result
1073 }
1074
1075 fn find_name_in_mro_uncached(&self, name: &'static PyStrInterned) -> Option<PyObjectRef> {
1077 for cls in self.mro.read().iter() {
1078 if let Some(value) = cls.attributes.read().get(name) {
1079 return Some(value.clone());
1080 }
1081 }
1082 None
1083 }
1084
1085 pub fn lookup_ref(&self, name: &Py<PyStr>, vm: &VirtualMachine) -> Option<PyObjectRef> {
1087 let interned_name = vm.ctx.interned_str(name)?;
1088 self.find_name_in_mro(interned_name)
1089 }
1090
1091 pub fn get_super_attr(&self, attr_name: &'static PyStrInterned) -> Option<PyObjectRef> {
1092 self.mro.read()[1..]
1093 .iter()
1094 .find_map(|class| class.attributes.read().get(attr_name).cloned())
1095 }
1096
1097 pub fn has_attr(&self, attr_name: &'static PyStrInterned) -> bool {
1099 self.has_name_in_mro(attr_name)
1100 }
1101
1102 fn has_name_in_mro(&self, name: &'static PyStrInterned) -> bool {
1105 let version = self.tp_version_tag.load(Ordering::Acquire);
1106 if version != 0 {
1107 let idx = type_cache_hash(version, name);
1108 let entry = &TYPE_CACHE[idx];
1109 let name_ptr = name as *const _ as *mut _;
1110 loop {
1111 let seq1 = entry.begin_read();
1112 let v1 = entry.version.load(Ordering::Acquire);
1113 let type_version = self.tp_version_tag.load(Ordering::Acquire);
1114 if v1 != type_version
1115 || !core::ptr::eq(entry.name.load(Ordering::Relaxed), name_ptr)
1116 {
1117 break;
1118 }
1119 let ptr = entry.value.load(Ordering::Acquire);
1120 if entry.end_read(seq1) {
1121 if !ptr.is_null() {
1122 return true;
1123 }
1124 break;
1125 }
1126 continue;
1127 }
1128 }
1129
1130 self.find_name_in_mro(name).is_some()
1132 }
1133
1134 pub fn get_attributes(&self) -> PyAttributes {
1135 let mut attributes = PyAttributes::default();
1137
1138 for bc in self.mro.read().iter().map(|cls| -> &Self { cls }).rev() {
1140 for (name, value) in bc.attributes.read().iter() {
1141 attributes.insert(name.to_owned(), value.clone());
1142 }
1143 }
1144
1145 attributes
1146 }
1147
1148 pub(crate) fn __new__(zelf: PyRef<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1150 let (subtype, args): (PyRef<Self>, FuncArgs) = args.bind(vm)?;
1151 if !subtype.fast_issubclass(&zelf) {
1152 return Err(vm.new_type_error(format!(
1153 "{zelf}.__new__({subtype}): {subtype} is not a subtype of {zelf}",
1154 zelf = zelf.name(),
1155 subtype = subtype.name(),
1156 )));
1157 }
1158 call_slot_new(zelf, subtype, args, vm)
1159 }
1160
1161 fn name_inner<'a, R: 'a>(
1162 &'a self,
1163 static_f: impl FnOnce(&'static str) -> R,
1164 heap_f: impl FnOnce(&'a HeapTypeExt) -> R,
1165 ) -> R {
1166 if let Some(ref ext) = self.heaptype_ext {
1167 heap_f(ext)
1168 } else {
1169 static_f(self.slots.name)
1170 }
1171 }
1172
1173 pub fn slot_name(&self) -> BorrowedValue<'_, str> {
1174 self.name_inner(
1175 |name| name.into(),
1176 |ext| {
1177 PyRwLockReadGuard::map(ext.name.read(), |name: &PyUtf8StrRef| -> &str {
1178 name.as_str()
1179 })
1180 .into()
1181 },
1182 )
1183 }
1184
1185 pub fn name(&self) -> BorrowedValue<'_, str> {
1186 self.name_inner(
1187 |name| name.rsplit_once('.').map_or(name, |(_, name)| name).into(),
1188 |ext| {
1189 PyRwLockReadGuard::map(ext.name.read(), |name: &PyUtf8StrRef| -> &str {
1190 name.as_str()
1191 })
1192 .into()
1193 },
1194 )
1195 }
1196
1197 pub fn init_type_data<T: Any + Send + Sync + 'static>(&self, data: T) -> Result<(), String> {
1202 let ext = self
1203 .heaptype_ext
1204 .as_ref()
1205 .ok_or_else(|| "Cannot set type data on non-heap types".to_string())?;
1206
1207 let mut type_data = ext.type_data.write();
1208 if type_data.is_some() {
1209 return Err("Type data already initialized".to_string());
1210 }
1211 *type_data = Some(TypeDataSlot::new(data));
1212 Ok(())
1213 }
1214
1215 pub fn get_type_data<T: Any + 'static>(&self) -> Option<TypeDataRef<'_, T>> {
1218 self.heaptype_ext
1219 .as_ref()
1220 .and_then(|ext| TypeDataRef::try_new(ext.type_data.read()))
1221 }
1222
1223 pub fn get_type_data_mut<T: Any + 'static>(&self) -> Option<TypeDataRefMut<'_, T>> {
1226 self.heaptype_ext
1227 .as_ref()
1228 .and_then(|ext| TypeDataRefMut::try_new(ext.type_data.write()))
1229 }
1230
1231 pub fn has_type_data<T: Any + 'static>(&self) -> bool {
1233 self.heaptype_ext.as_ref().is_some_and(|ext| {
1234 ext.type_data
1235 .read()
1236 .as_ref()
1237 .is_some_and(|slot| slot.get::<T>().is_some())
1238 })
1239 }
1240}
1241
1242impl Py<PyType> {
1243 pub(crate) fn is_subtype(&self, other: &Self) -> bool {
1244 is_subtype_with_mro(&self.mro.read(), self, other)
1245 }
1246
1247 pub fn check_exact<'a>(obj: &'a PyObject, vm: &VirtualMachine) -> Option<&'a Self> {
1250 obj.downcast_ref_if_exact::<PyType>(vm)
1251 }
1252
1253 pub fn fast_issubclass(&self, cls: &impl Borrow<PyObject>) -> bool {
1257 self.as_object().is(cls.borrow()) || self.mro.read()[1..].iter().any(|c| c.is(cls.borrow()))
1258 }
1259
1260 pub fn mro_map_collect<F, R>(&self, f: F) -> Vec<R>
1261 where
1262 F: Fn(&Self) -> R,
1263 {
1264 self.mro.read().iter().map(|x| x.deref()).map(f).collect()
1265 }
1266
1267 pub fn mro_collect(&self) -> Vec<PyRef<PyType>> {
1268 self.mro
1269 .read()
1270 .iter()
1271 .map(|x| x.deref())
1272 .map(|x| x.to_owned())
1273 .collect()
1274 }
1275
1276 pub fn iter_base_chain(&self) -> impl Iterator<Item = &Self> {
1277 core::iter::successors(Some(self), |cls| cls.base.as_deref())
1278 }
1279
1280 pub fn extend_methods(&'static self, method_defs: &'static [PyMethodDef], ctx: &Context) {
1281 for method_def in method_defs {
1282 let method = method_def.to_proper_method(self, ctx);
1283 self.set_attr(ctx.intern_str(method_def.name), method);
1284 }
1285 }
1286}
1287
1288#[pyclass(
1289 with(
1290 Py,
1291 Constructor,
1292 Initializer,
1293 GetAttr,
1294 SetAttr,
1295 Callable,
1296 AsNumber,
1297 Representable
1298 ),
1299 flags(BASETYPE, HAS_DICT, HAS_WEAKREF)
1300)]
1301impl PyType {
1302 #[pygetset]
1303 fn __bases__(&self, vm: &VirtualMachine) -> PyTupleRef {
1304 vm.ctx.new_tuple(
1305 self.bases
1306 .read()
1307 .iter()
1308 .map(|x| x.as_object().to_owned())
1309 .collect(),
1310 )
1311 }
1312 #[pygetset(setter, name = "__bases__")]
1313 fn set_bases(zelf: &Py<Self>, bases: Vec<PyTypeRef>, vm: &VirtualMachine) -> PyResult<()> {
1314 if zelf.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
1317 return Err(vm.new_type_error(format!(
1318 "cannot set '__bases__' attribute of immutable type '{}'",
1319 zelf.name()
1320 )));
1321 }
1322 if bases.is_empty() {
1323 return Err(vm.new_type_error(format!(
1324 "can only assign non-empty tuple to %s.__bases__, not {}",
1325 zelf.name()
1326 )));
1327 }
1328
1329 *zelf.bases.write() = bases;
1338 fn update_mro_recursively(cls: &PyType, vm: &VirtualMachine) -> PyResult<()> {
1340 let mut mro =
1341 PyType::resolve_mro(&cls.bases.read()).map_err(|msg| vm.new_type_error(msg))?;
1342 mro.insert(0, cls.mro.read()[0].to_owned());
1344 *cls.mro.write() = mro;
1345 for subclass in cls.subclasses.write().iter() {
1346 let subclass = subclass.upgrade().unwrap();
1347 let subclass: &Py<PyType> = subclass.downcast_ref().unwrap();
1348 update_mro_recursively(subclass, vm)?;
1349 }
1350 Ok(())
1351 }
1352 update_mro_recursively(zelf, vm)?;
1353
1354 zelf.modified();
1356
1357 zelf.init_slots(&vm.ctx);
1359
1360 let weakref_type = super::PyWeak::static_type();
1362 for base in zelf.bases.read().iter() {
1363 base.subclasses.write().push(
1364 zelf.as_object()
1365 .downgrade_with_weakref_typ_opt(None, weakref_type.to_owned())
1366 .unwrap(),
1367 );
1368 }
1369
1370 Ok(())
1371 }
1372
1373 #[pygetset]
1374 fn __base__(&self) -> Option<PyTypeRef> {
1375 self.base.clone()
1376 }
1377
1378 #[pygetset]
1379 const fn __flags__(&self) -> u64 {
1380 self.slots.flags.bits()
1381 }
1382
1383 #[pygetset]
1384 fn __basicsize__(&self) -> usize {
1385 crate::object::SIZEOF_PYOBJECT_HEAD + self.slots.basicsize
1386 }
1387
1388 #[pygetset]
1389 fn __itemsize__(&self) -> usize {
1390 self.slots.itemsize
1391 }
1392
1393 #[pygetset]
1394 pub fn __name__(&self, vm: &VirtualMachine) -> PyStrRef {
1395 self.name_inner(
1396 |name| {
1397 vm.ctx
1398 .interned_str(name.rsplit_once('.').map_or(name, |(_, name)| name))
1399 .unwrap_or_else(|| {
1400 panic!(
1401 "static type name must be already interned but {} is not",
1402 self.slot_name()
1403 )
1404 })
1405 .to_owned()
1406 },
1407 |ext| ext.name.read().clone().into_wtf8(),
1408 )
1409 }
1410
1411 #[pygetset]
1412 pub fn __qualname__(&self, vm: &VirtualMachine) -> PyObjectRef {
1413 if let Some(ref heap_type) = self.heaptype_ext {
1414 heap_type.qualname.read().clone().into()
1415 } else {
1416 vm.ctx.new_str(self.name().deref()).into()
1418 }
1419 }
1420
1421 #[pygetset(setter)]
1422 fn set___qualname__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
1423 self.check_set_special_type_attr(identifier!(vm, __qualname__), vm)?;
1424 let value = value.ok_or_else(|| {
1425 vm.new_type_error(format!(
1426 "cannot delete '__qualname__' attribute of immutable type '{}'",
1427 self.name()
1428 ))
1429 })?;
1430
1431 let str_value = downcast_qualname(value, vm)?;
1432
1433 let heap_type = self.heaptype_ext.as_ref().ok_or_else(|| {
1434 vm.new_type_error(format!(
1435 "cannot set '__qualname__' attribute of immutable type '{}'",
1436 self.name()
1437 ))
1438 })?;
1439
1440 let _old_qualname = {
1443 let mut qualname_guard = heap_type.qualname.write();
1444 core::mem::replace(&mut *qualname_guard, str_value)
1445 };
1446 Ok(())
1449 }
1450
1451 #[pygetset]
1452 fn __annotate__(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
1453 if !self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
1454 return Err(vm.new_attribute_error(format!(
1455 "type object '{}' has no attribute '__annotate__'",
1456 self.name()
1457 )));
1458 }
1459
1460 let mut attrs = self.attributes.write();
1461 if let Some(annotate) = attrs.get(identifier!(vm, __annotate__)).cloned() {
1463 return Ok(annotate);
1464 }
1465 if let Some(annotate) = attrs.get(identifier!(vm, __annotate_func__)).cloned() {
1467 return Ok(annotate);
1469 }
1470 let none = vm.ctx.none();
1472 attrs.insert(identifier!(vm, __annotate_func__), none.clone());
1473 Ok(none)
1474 }
1475
1476 #[pygetset(setter)]
1477 fn set___annotate__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
1478 let value = match value {
1479 PySetterValue::Delete => {
1480 return Err(vm.new_type_error("cannot delete __annotate__ attribute"));
1481 }
1482 PySetterValue::Assign(v) => v,
1483 };
1484
1485 if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
1486 return Err(vm.new_type_error(format!(
1487 "cannot set '__annotate__' attribute of immutable type '{}'",
1488 self.name()
1489 )));
1490 }
1491
1492 if !vm.is_none(&value) && !value.is_callable() {
1493 return Err(vm.new_type_error("__annotate__ must be callable or None"));
1494 }
1495
1496 let mut attrs = self.attributes.write();
1497 if !vm.is_none(&value) {
1499 attrs.swap_remove(identifier!(vm, __annotations_cache__));
1500 }
1501 attrs.insert(identifier!(vm, __annotate_func__), value.clone());
1502
1503 Ok(())
1504 }
1505
1506 #[pygetset]
1507 fn __annotations__(&self, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
1508 let attrs = self.attributes.read();
1509 if let Some(annotations) = attrs.get(identifier!(vm, __annotations__)).cloned() {
1510 if !annotations.class().is(vm.ctx.types.getset_type) {
1512 if vm.is_none(&annotations)
1513 || annotations.class().is(vm.ctx.types.dict_type)
1514 || self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
1515 {
1516 return Ok(annotations);
1517 }
1518 return Err(vm.new_attribute_error(format!(
1519 "type object '{}' has no attribute '__annotations__'",
1520 self.name()
1521 )));
1522 }
1523 }
1524 if let Some(annotations) = attrs.get(identifier!(vm, __annotations_cache__)).cloned() {
1526 if vm.is_none(&annotations)
1527 || annotations.class().is(vm.ctx.types.dict_type)
1528 || self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
1529 {
1530 return Ok(annotations);
1531 }
1532 return Err(vm.new_attribute_error(format!(
1533 "type object '{}' has no attribute '__annotations__'",
1534 self.name()
1535 )));
1536 }
1537 drop(attrs);
1538
1539 if !self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
1540 return Err(vm.new_attribute_error(format!(
1541 "type object '{}' has no attribute '__annotations__'",
1542 self.name()
1543 )));
1544 }
1545
1546 let annotate = self.__annotate__(vm)?;
1548 let annotations = if annotate.is_callable() {
1549 let result = annotate.call((1i32,), vm)?;
1551 if !result.class().is(vm.ctx.types.dict_type) {
1552 return Err(vm.new_type_error(format!(
1553 "__annotate__ returned non-dict of type '{}'",
1554 result.class().name()
1555 )));
1556 }
1557 result
1558 } else {
1559 vm.ctx.new_dict().into()
1560 };
1561
1562 self.attributes
1564 .write()
1565 .insert(identifier!(vm, __annotations_cache__), annotations.clone());
1566 Ok(annotations)
1567 }
1568
1569 #[pygetset(setter)]
1570 fn set___annotations__(
1571 &self,
1572 value: crate::function::PySetterValue<PyObjectRef>,
1573 vm: &VirtualMachine,
1574 ) -> PyResult<()> {
1575 if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
1576 return Err(vm.new_type_error(format!(
1577 "cannot set '__annotations__' attribute of immutable type '{}'",
1578 self.name()
1579 )));
1580 }
1581
1582 let mut attrs = self.attributes.write();
1583 let has_annotations = attrs.contains_key(identifier!(vm, __annotations__));
1584
1585 match value {
1586 crate::function::PySetterValue::Assign(value) => {
1587 let key = if has_annotations {
1589 identifier!(vm, __annotations__)
1590 } else {
1591 identifier!(vm, __annotations_cache__)
1592 };
1593 attrs.insert(key, value);
1594 if has_annotations {
1595 attrs.swap_remove(identifier!(vm, __annotations_cache__));
1596 }
1597 }
1598 crate::function::PySetterValue::Delete => {
1599 let removed = if has_annotations {
1601 attrs
1602 .swap_remove(identifier!(vm, __annotations__))
1603 .is_some()
1604 } else {
1605 attrs
1606 .swap_remove(identifier!(vm, __annotations_cache__))
1607 .is_some()
1608 };
1609 if !removed {
1610 return Err(vm.new_attribute_error("__annotations__"));
1611 }
1612 if has_annotations {
1613 attrs.swap_remove(identifier!(vm, __annotations_cache__));
1614 }
1615 }
1616 }
1617 attrs.swap_remove(identifier!(vm, __annotate_func__));
1618 attrs.swap_remove(identifier!(vm, __annotate__));
1619
1620 Ok(())
1621 }
1622
1623 #[pygetset]
1624 pub fn __module__(&self, vm: &VirtualMachine) -> PyObjectRef {
1625 self.attributes
1626 .read()
1627 .get(identifier!(vm, __module__))
1628 .cloned()
1629 .and_then(|found| {
1631 if found.fast_isinstance(vm.ctx.types.getset_type) {
1632 None
1633 } else {
1634 Some(found)
1635 }
1636 })
1637 .unwrap_or_else(|| {
1638 let slot_name = self.slot_name();
1640 if let Some((module, _)) = slot_name.rsplit_once('.') {
1641 vm.ctx.intern_str(module).to_object()
1642 } else {
1643 vm.ctx.new_str(ascii!("builtins")).into()
1644 }
1645 })
1646 }
1647
1648 #[pygetset(setter)]
1649 fn set___module__(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
1650 self.check_set_special_type_attr(identifier!(vm, __module__), vm)?;
1651 let mut attributes = self.attributes.write();
1652 attributes.swap_remove(identifier!(vm, __firstlineno__));
1653 attributes.insert(identifier!(vm, __module__), value);
1654 Ok(())
1655 }
1656
1657 #[pyclassmethod]
1658 fn __prepare__(
1659 _cls: PyTypeRef,
1660 _name: OptionalArg<PyObjectRef>,
1661 _bases: OptionalArg<PyObjectRef>,
1662 _kwargs: KwArgs,
1663 vm: &VirtualMachine,
1664 ) -> PyDictRef {
1665 vm.ctx.new_dict()
1666 }
1667
1668 #[pymethod]
1669 fn __subclasses__(&self) -> PyList {
1670 let mut subclasses = self.subclasses.write();
1671 subclasses.retain(|x| x.upgrade().is_some());
1672 PyList::from(
1673 subclasses
1674 .iter()
1675 .map(|x| x.upgrade().unwrap())
1676 .collect::<Vec<_>>(),
1677 )
1678 }
1679
1680 pub fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
1681 or_(other, zelf, vm)
1682 }
1683
1684 pub fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
1685 or_(zelf, other, vm)
1686 }
1687
1688 #[pygetset]
1689 fn __dict__(zelf: PyRef<Self>) -> PyMappingProxy {
1690 PyMappingProxy::from(zelf)
1691 }
1692
1693 #[pygetset(setter)]
1694 fn set___dict__(&self, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
1695 Err(vm.new_not_implemented_error(
1696 "Setting __dict__ attribute on a type isn't yet implemented",
1697 ))
1698 }
1699
1700 fn check_set_special_type_attr(
1701 &self,
1702 name: &PyStrInterned,
1703 vm: &VirtualMachine,
1704 ) -> PyResult<()> {
1705 if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
1706 return Err(vm.new_type_error(format!(
1707 "cannot set '{}' attribute of immutable type '{}'",
1708 name,
1709 self.slot_name()
1710 )));
1711 }
1712 Ok(())
1713 }
1714
1715 #[pygetset(setter)]
1716 fn set___name__(&self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
1717 self.check_set_special_type_attr(identifier!(vm, __name__), vm)?;
1718 let name = value.downcast::<PyStr>().map_err(|value| {
1719 vm.new_type_error(format!(
1720 "can only assign string to {}.__name__, not '{}'",
1721 self.slot_name(),
1722 value.class().slot_name(),
1723 ))
1724 })?;
1725 if name.as_bytes().contains(&0) {
1726 return Err(vm.new_value_error("type name must not contain null characters"));
1727 }
1728 let name = name.try_into_utf8(vm)?;
1729
1730 let heap_type = self.heaptype_ext.as_ref().ok_or_else(|| {
1731 vm.new_type_error(format!(
1732 "cannot set '__name__' attribute of immutable type '{}'",
1733 self.slot_name()
1734 ))
1735 })?;
1736
1737 let _old_name = {
1740 let mut name_guard = heap_type.name.write();
1741 core::mem::replace(&mut *name_guard, name)
1742 };
1743 Ok(())
1746 }
1747
1748 #[pygetset]
1749 fn __text_signature__(&self) -> Option<String> {
1750 self.slots
1751 .doc
1752 .and_then(|doc| get_text_signature_from_internal_doc(&self.name(), doc))
1753 .map(|signature| signature.to_string())
1754 }
1755
1756 #[pygetset]
1757 fn __type_params__(&self, vm: &VirtualMachine) -> PyTupleRef {
1758 let attrs = self.attributes.read();
1759 let key = identifier!(vm, __type_params__);
1760 if let Some(params) = attrs.get(&key)
1761 && let Ok(tuple) = params.clone().downcast::<PyTuple>()
1762 {
1763 return tuple;
1764 }
1765 vm.ctx.empty_tuple.clone()
1767 }
1768
1769 #[pygetset(setter)]
1770 fn set___type_params__(
1771 &self,
1772 value: PySetterValue<PyTupleRef>,
1773 vm: &VirtualMachine,
1774 ) -> PyResult<()> {
1775 match value {
1776 PySetterValue::Assign(ref val) => {
1777 let key = identifier!(vm, __type_params__);
1778 self.check_set_special_type_attr(key, vm)?;
1779 self.modified();
1780 self.attributes.write().insert(key, val.clone().into());
1781 }
1782 PySetterValue::Delete => {
1783 if self.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
1785 return Err(vm.new_type_error(format!(
1786 "cannot delete '__type_params__' attribute of immutable type '{}'",
1787 self.slot_name()
1788 )));
1789 }
1790 let key = identifier!(vm, __type_params__);
1791 self.modified();
1792 self.attributes.write().shift_remove(&key);
1793 }
1794 }
1795 Ok(())
1796 }
1797}
1798
1799impl Constructor for PyType {
1800 type Args = FuncArgs;
1801
1802 fn slot_new(metatype: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1803 vm_trace!("type.__new__ {:?}", args);
1804
1805 let is_type_type = metatype.is(vm.ctx.types.type_type);
1806 if is_type_type && args.args.len() == 1 && args.kwargs.is_empty() {
1807 return Ok(args.args[0].class().to_owned().into());
1808 }
1809
1810 if args.args.len() != 3 {
1811 return Err(vm.new_type_error(if is_type_type {
1812 "type() takes 1 or 3 arguments".to_owned()
1813 } else {
1814 format!(
1815 "type.__new__() takes exactly 3 arguments ({} given)",
1816 args.args.len()
1817 )
1818 }));
1819 }
1820
1821 let (name, bases, dict, kwargs): (PyStrRef, PyTupleRef, PyDictRef, KwArgs) =
1822 args.clone().bind(vm)?;
1823
1824 if name.as_bytes().contains(&0) {
1825 return Err(vm.new_value_error("type name must not contain null characters"));
1826 }
1827 let name = name.try_into_utf8(vm)?;
1828
1829 let (metatype, base, bases, base_is_type) = if bases.is_empty() {
1830 let base = vm.ctx.types.object_type.to_owned();
1831 (metatype, base.clone(), vec![base], false)
1832 } else {
1833 let bases = bases
1834 .iter()
1835 .map(|obj| {
1836 obj.clone().downcast::<Self>().or_else(|obj| {
1837 if vm
1838 .get_attribute_opt(obj, identifier!(vm, __mro_entries__))?
1839 .is_some()
1840 {
1841 Err(vm.new_type_error(
1842 "type() doesn't support MRO entry resolution; \
1843 use types.new_class()",
1844 ))
1845 } else {
1846 Err(vm.new_type_error("bases must be types"))
1847 }
1848 })
1849 })
1850 .collect::<PyResult<Vec<_>>>()?;
1851
1852 let winner = calculate_meta_class(metatype.clone(), &bases, vm)?;
1854 let metatype = if !winner.is(&metatype) {
1855 if let Some(ref slot_new) = winner.slots.new.load() {
1856 return slot_new(winner, args, vm);
1858 }
1859 winner
1860 } else {
1861 metatype
1862 };
1863
1864 let base = best_base(&bases, vm)?;
1865 let base_is_type = base.is(vm.ctx.types.type_type);
1866
1867 (metatype, base.to_owned(), bases, base_is_type)
1868 };
1869
1870 let qualname = dict
1871 .get_item_opt(identifier!(vm, __qualname__), vm)?
1872 .map(|obj| downcast_qualname(obj, vm))
1873 .transpose()?
1874 .unwrap_or_else(|| {
1875 name.clone().into_wtf8()
1877 });
1878
1879 let mut attributes = dict.to_attributes(vm);
1880 attributes.shift_remove(identifier!(vm, __qualname__));
1881
1882 if let Some(doc) = attributes.get(identifier!(vm, __doc__))
1884 && let Some(doc_str) = doc.downcast_ref::<PyStr>()
1885 {
1886 doc_str.ensure_valid_utf8(vm)?;
1887 }
1888
1889 if let Some(f) = attributes.get_mut(identifier!(vm, __init_subclass__))
1890 && f.class().is(vm.ctx.types.function_type)
1891 {
1892 *f = PyClassMethod::from(f.clone()).into_pyobject(vm);
1893 }
1894
1895 if let Some(f) = attributes.get_mut(identifier!(vm, __class_getitem__))
1896 && f.class().is(vm.ctx.types.function_type)
1897 {
1898 *f = PyClassMethod::from(f.clone()).into_pyobject(vm);
1899 }
1900
1901 if let Some(f) = attributes.get_mut(identifier!(vm, __new__))
1902 && f.class().is(vm.ctx.types.function_type)
1903 {
1904 *f = PyStaticMethod::from(f.clone()).into_pyobject(vm);
1905 }
1906
1907 if let Some(current_frame) = vm.current_frame() {
1908 let entry = attributes.entry(identifier!(vm, __module__));
1909 if matches!(entry, Entry::Vacant(_)) {
1910 let module_name = vm.unwrap_or_none(
1911 current_frame
1912 .globals
1913 .get_item_opt(identifier!(vm, __name__), vm)?,
1914 );
1915 entry.or_insert(module_name);
1916 }
1917 }
1918
1919 if attributes.get(identifier!(vm, __eq__)).is_some()
1920 && attributes.get(identifier!(vm, __hash__)).is_none()
1921 {
1922 attributes.insert(identifier!(vm, __hash__), vm.ctx.none.clone().into());
1925 }
1926
1927 let (heaptype_slots, add_dict, add_weakref): (
1928 Option<PyRef<PyTuple<PyStrRef>>>,
1929 bool,
1930 bool,
1931 ) = if let Some(x) = attributes.get(identifier!(vm, __slots__)) {
1932 if x.class().is(vm.ctx.types.bytes_type) {
1934 return Err(vm.new_type_error("__slots__ items must be strings, not 'bytes'"));
1935 }
1936
1937 let slots = if x.class().is(vm.ctx.types.str_type) {
1938 let x = unsafe { x.downcast_unchecked_ref::<PyStr>() };
1939 PyTuple::new_ref_typed(vec![x.to_owned()], &vm.ctx)
1940 } else {
1941 let iter = x.get_iter(vm)?;
1942 let elements = {
1943 let mut elements = Vec::new();
1944 while let PyIterReturn::Return(element) = iter.next(vm)? {
1945 if element.class().is(vm.ctx.types.bytes_type) {
1947 return Err(
1948 vm.new_type_error("__slots__ items must be strings, not 'bytes'")
1949 );
1950 }
1951 elements.push(element);
1952 }
1953 elements
1954 };
1955 let tuple = elements.into_pytuple(vm);
1956 tuple.try_into_typed(vm)?
1957 };
1958
1959 let has_custom_slots = slots
1963 .iter()
1964 .any(|s| !matches!(s.as_bytes(), b"__dict__" | b"__weakref__"));
1965 if has_custom_slots && base.slots.itemsize > 0 {
1966 return Err(vm.new_type_error(format!(
1967 "nonempty __slots__ not supported for subtype of '{}'",
1968 base.name()
1969 )));
1970 }
1971
1972 let mut seen_dict = false;
1974 let mut seen_weakref = false;
1975 for slot in slots.iter() {
1976 if !slot.isidentifier() {
1978 return Err(vm.new_type_error("__slots__ must be identifiers"));
1979 }
1980
1981 let slot_name = slot.as_bytes();
1982
1983 if slot_name == b"__dict__" {
1985 if seen_dict {
1986 return Err(
1987 vm.new_type_error("__dict__ slot disallowed: we already got one")
1988 );
1989 }
1990 seen_dict = true;
1991 }
1992
1993 if slot_name == b"__weakref__" {
1995 if seen_weakref {
1996 return Err(
1997 vm.new_type_error("__weakref__ slot disallowed: we already got one")
1998 );
1999 }
2000 seen_weakref = true;
2001 }
2002
2003 if attributes.contains_key(vm.ctx.intern_str(slot.as_wtf8())) {
2005 return Err(vm.new_value_error(format!(
2006 "'{}' in __slots__ conflicts with a class variable",
2007 slot.as_wtf8()
2008 )));
2009 }
2010 }
2011
2012 if seen_dict && base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
2014 return Err(vm.new_type_error("__dict__ slot disallowed: we already got one"));
2015 }
2016
2017 if seen_weakref && base.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF) {
2019 return Err(vm.new_type_error("__weakref__ slot disallowed: we already got one"));
2020 }
2021
2022 let dict_name = "__dict__";
2024 let weakref_name = "__weakref__";
2025 let has_dict = slots.iter().any(|s| s.as_wtf8() == dict_name);
2026 let add_weakref = seen_weakref;
2027
2028 let filtered_slots = if has_dict || add_weakref {
2031 let filtered: Vec<PyStrRef> = slots
2032 .iter()
2033 .filter(|s| s.as_wtf8() != dict_name && s.as_wtf8() != weakref_name)
2034 .cloned()
2035 .collect();
2036 PyTuple::new_ref_typed(filtered, &vm.ctx)
2037 } else {
2038 slots
2039 };
2040
2041 (Some(filtered_slots), has_dict, add_weakref)
2042 } else {
2043 (None, false, false)
2044 };
2045
2046 let base_member_count = bases
2048 .iter()
2049 .map(|base| base.slots.member_count)
2050 .max()
2051 .unwrap();
2052 let heaptype_member_count = heaptype_slots.as_ref().map(|x| x.len()).unwrap_or(0);
2053 let member_count: usize = base_member_count + heaptype_member_count;
2054
2055 let mut flags = PyTypeFlags::heap_type_flags();
2056
2057 let may_add_dict = !base.slots.flags.has_feature(PyTypeFlags::HAS_DICT);
2061
2062 if (heaptype_slots.is_none() && may_add_dict) || add_dict {
2066 flags |= PyTypeFlags::HAS_DICT | PyTypeFlags::MANAGED_DICT;
2067 }
2068
2069 let may_add_weakref = !base.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF);
2073 if (heaptype_slots.is_none() && may_add_weakref) || add_weakref {
2074 flags |= PyTypeFlags::HAS_WEAKREF | PyTypeFlags::MANAGED_WEAKREF;
2075 }
2076
2077 let (slots, heaptype_ext) = {
2078 let slots = PyTypeSlots {
2079 flags,
2080 member_count,
2081 itemsize: base.slots.itemsize,
2082 ..PyTypeSlots::heap_default()
2083 };
2084 let heaptype_ext = HeapTypeExt {
2085 name: PyRwLock::new(name),
2086 qualname: PyRwLock::new(qualname),
2087 slots: heaptype_slots.clone(),
2088 type_data: PyRwLock::new(None),
2089 specialization_cache: TypeSpecializationCache::new(),
2090 };
2091 (slots, heaptype_ext)
2092 };
2093
2094 let typ = Self::new_heap_inner(
2095 base,
2096 bases,
2097 attributes,
2098 slots,
2099 heaptype_ext,
2100 metatype,
2101 &vm.ctx,
2102 )
2103 .map_err(|e| vm.new_type_error(e))?;
2104
2105 if let Some(ref slots) = heaptype_slots {
2106 let mut offset = base_member_count;
2107 let class_name = typ.name().to_string();
2108 for member in slots.as_slice() {
2109 let member_str = member
2111 .to_str()
2112 .ok_or_else(|| vm.new_type_error("__slots__ must be valid UTF-8 strings"))?;
2113 let mangled_name = mangle_name(&class_name, member_str);
2114 let member_def = PyMemberDef {
2115 name: mangled_name.clone(),
2116 kind: MemberKind::ObjectEx,
2117 getter: MemberGetter::Offset(offset),
2118 setter: MemberSetter::Offset(offset),
2119 doc: None,
2120 };
2121 let attr_name = vm.ctx.intern_str(mangled_name.as_str());
2122 let member_descriptor: PyRef<PyMemberDescriptor> =
2123 vm.ctx.new_pyref(PyMemberDescriptor {
2124 common: PyDescriptorOwned {
2125 typ: typ.clone(),
2126 name: attr_name,
2127 qualname: PyRwLock::new(None),
2128 },
2129 member: member_def,
2130 });
2131 typ.set_attr(attr_name, member_descriptor.into());
2134 offset += 1;
2135 }
2136 }
2137
2138 {
2139 let mut attrs = typ.attributes.write();
2140 if let Some(cell) = attrs.get(identifier!(vm, __classcell__)) {
2141 let cell = PyCellRef::try_from_object(vm, cell.clone()).map_err(|_| {
2142 vm.new_type_error(format!(
2143 "__classcell__ must be a nonlocal cell, not {}",
2144 cell.class().name()
2145 ))
2146 })?;
2147 cell.set(Some(typ.clone().into()));
2148 attrs.shift_remove(identifier!(vm, __classcell__));
2149 }
2150 if let Some(cell) = attrs.get(identifier!(vm, __classdictcell__)) {
2151 let cell = PyCellRef::try_from_object(vm, cell.clone()).map_err(|_| {
2152 vm.new_type_error(format!(
2153 "__classdictcell__ must be a nonlocal cell, not {}",
2154 cell.class().name()
2155 ))
2156 })?;
2157 cell.set(Some(dict.clone().into()));
2158 attrs.shift_remove(identifier!(vm, __classdictcell__));
2159 }
2160 }
2161
2162 if !base_is_type && typ.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
2174 let __dict__ = identifier!(vm, __dict__);
2175 let has_inherited_dict = typ
2176 .mro
2177 .read()
2178 .iter()
2179 .any(|base| base.attributes.read().contains_key(&__dict__));
2180 if !typ.attributes.read().contains_key(&__dict__) && !has_inherited_dict {
2181 unsafe {
2182 let descriptor =
2183 vm.ctx
2184 .new_getset("__dict__", &typ, subtype_get_dict, subtype_set_dict);
2185 typ.attributes.write().insert(__dict__, descriptor.into());
2186 }
2187 }
2188 }
2189
2190 if typ.slots.flags.has_feature(PyTypeFlags::HAS_WEAKREF) {
2192 let __weakref__ = vm.ctx.intern_str("__weakref__");
2193 let has_inherited_weakref = typ
2194 .mro
2195 .read()
2196 .iter()
2197 .any(|base| base.attributes.read().contains_key(&__weakref__));
2198 if !typ.attributes.read().contains_key(&__weakref__) && !has_inherited_weakref {
2199 unsafe {
2200 let descriptor = vm.ctx.new_getset(
2201 "__weakref__",
2202 &typ,
2203 subtype_get_weakref,
2204 subtype_set_weakref,
2205 );
2206 typ.attributes
2207 .write()
2208 .insert(__weakref__, descriptor.into());
2209 }
2210 }
2211 }
2212
2213 {
2217 let __doc__ = identifier!(vm, __doc__);
2218 if !typ.attributes.read().contains_key(&__doc__) {
2219 typ.attributes.write().insert(__doc__, vm.ctx.none());
2220 }
2221 }
2222
2223 let attributes = typ
2225 .attributes
2226 .read()
2227 .iter()
2228 .filter_map(|(name, obj)| {
2229 vm.get_method(obj.clone(), identifier!(vm, __set_name__))
2230 .map(|res| res.map(|meth| (obj.clone(), name.to_owned(), meth)))
2231 })
2232 .collect::<PyResult<Vec<_>>>()?;
2233 for (obj, name, set_name) in attributes {
2234 set_name.call((typ.clone(), name), vm).inspect_err(|e| {
2235 let note = format!(
2238 "Error calling __set_name__ on '{}' instance '{}' in '{}'",
2239 obj.class().name(),
2240 name,
2241 typ.name()
2242 );
2243 drop(vm.call_method(e.as_object(), "add_note", (vm.ctx.new_str(note.as_str()),)));
2245 })?;
2246 }
2247
2248 if let Some(init_subclass) = typ.get_super_attr(identifier!(vm, __init_subclass__)) {
2249 let init_subclass = vm
2250 .call_get_descriptor_specific(&init_subclass, None, Some(typ.clone().into()))
2251 .unwrap_or(Ok(init_subclass))?;
2252 init_subclass.call(kwargs, vm)?;
2253 };
2254
2255 Ok(typ.into())
2256 }
2257
2258 fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
2259 unimplemented!("use slot_new")
2260 }
2261}
2262
2263const SIGNATURE_END_MARKER: &str = ")\n--\n\n";
2264fn get_signature(doc: &str) -> Option<&str> {
2265 doc.find(SIGNATURE_END_MARKER).map(|index| &doc[..=index])
2266}
2267
2268fn find_signature<'a>(name: &str, doc: &'a str) -> Option<&'a str> {
2269 let name = name.rsplit('.').next().unwrap();
2270 let doc = doc.strip_prefix(name)?;
2271 doc.starts_with('(').then_some(doc)
2272}
2273
2274pub(crate) fn get_text_signature_from_internal_doc<'a>(
2275 name: &str,
2276 internal_doc: &'a str,
2277) -> Option<&'a str> {
2278 find_signature(name, internal_doc).and_then(get_signature)
2279}
2280
2281fn get_doc_from_internal_doc<'a>(name: &str, internal_doc: &'a str) -> &'a str {
2283 if let Some(doc_without_sig) = find_signature(name, internal_doc) {
2286 if let Some(sig_end_pos) = doc_without_sig.find(SIGNATURE_END_MARKER) {
2288 let after_sig = &doc_without_sig[sig_end_pos + SIGNATURE_END_MARKER.len()..];
2289 return after_sig;
2291 }
2292 }
2293 internal_doc
2295}
2296
2297impl Initializer for PyType {
2298 type Args = FuncArgs;
2299
2300 fn slot_init(_zelf: PyObjectRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> {
2302 if args.args.len() == 1 && !args.kwargs.is_empty() {
2304 return Err(vm.new_type_error("type.__init__() takes no keyword arguments"));
2305 }
2306 if args.args.len() != 1 && args.args.len() != 3 {
2307 return Err(vm.new_type_error("type.__init__() takes 1 or 3 arguments"));
2308 }
2309 Ok(())
2310 }
2311
2312 fn init(_zelf: PyRef<Self>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<()> {
2313 unreachable!("slot_init is defined")
2314 }
2315}
2316
2317impl GetAttr for PyType {
2318 fn getattro(zelf: &Py<Self>, name_str: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
2319 #[cold]
2320 fn attribute_error(
2321 zelf: &Py<PyType>,
2322 name: &Wtf8,
2323 vm: &VirtualMachine,
2324 ) -> PyBaseExceptionRef {
2325 vm.new_attribute_error(format!(
2326 "type object '{}' has no attribute '{}'",
2327 zelf.slot_name(),
2328 name,
2329 ))
2330 }
2331
2332 let Some(name) = vm.ctx.interned_str(name_str) else {
2333 return Err(attribute_error(zelf, name_str.as_wtf8(), vm));
2334 };
2335 vm_trace!("type.__getattribute__({:?}, {:?})", zelf, name);
2336 let mcl = zelf.class();
2337 let mcl_attr = mcl.get_attr(name);
2338
2339 if let Some(ref attr) = mcl_attr {
2340 let attr_class = attr.class();
2341 let has_descr_set = attr_class.slots.descr_set.load().is_some();
2342 if has_descr_set {
2343 let descr_get = attr_class.slots.descr_get.load();
2344 if let Some(descr_get) = descr_get {
2345 let mcl = mcl.to_owned().into();
2346 return descr_get(attr.clone(), Some(zelf.to_owned().into()), Some(mcl), vm);
2347 }
2348 }
2349 }
2350
2351 let zelf_attr = zelf.get_attr(name);
2352
2353 if let Some(attr) = zelf_attr {
2354 let descr_get = attr.class().slots.descr_get.load();
2355 if let Some(descr_get) = descr_get {
2356 descr_get(attr, None, Some(zelf.to_owned().into()), vm)
2357 } else {
2358 Ok(attr)
2359 }
2360 } else if let Some(attr) = mcl_attr {
2361 vm.call_if_get_descriptor(&attr, zelf.to_owned().into())
2362 } else {
2363 Err(attribute_error(zelf, name_str.as_wtf8(), vm))
2364 }
2365 }
2366}
2367
2368#[pyclass]
2369impl Py<PyType> {
2370 #[pygetset]
2371 fn __mro__(&self) -> PyTuple {
2372 let elements: Vec<PyObjectRef> = self.mro_map_collect(|x| x.as_object().to_owned());
2373 PyTuple::new_unchecked(elements.into_boxed_slice())
2374 }
2375
2376 #[pygetset]
2377 fn __doc__(&self, vm: &VirtualMachine) -> PyResult {
2378 if !self.slots.flags.has_feature(PyTypeFlags::HEAPTYPE)
2381 && let Some(internal_doc) = self.slots.doc
2382 {
2383 let doc_str = get_doc_from_internal_doc(&self.name(), internal_doc);
2385 return Ok(vm.ctx.new_str(doc_str).into());
2386 }
2387
2388 if let Some(doc_attr) = self.get_direct_attr(vm.ctx.intern_str("__doc__")) {
2391 let descr_get = doc_attr.class().slots.descr_get.load();
2393 if let Some(descr_get) = descr_get {
2394 descr_get(doc_attr, None, Some(self.to_owned().into()), vm)
2395 } else {
2396 Ok(doc_attr)
2397 }
2398 } else {
2399 Ok(vm.ctx.none())
2400 }
2401 }
2402
2403 #[pygetset(setter)]
2404 fn set___doc__(&self, value: PySetterValue, vm: &VirtualMachine) -> PyResult<()> {
2405 let value = value.ok_or_else(|| {
2407 vm.new_type_error(format!(
2408 "cannot delete '__doc__' attribute of type '{}'",
2409 self.name()
2410 ))
2411 })?;
2412
2413 self.check_set_special_type_attr(identifier!(vm, __doc__), vm)?;
2415
2416 self.attributes
2418 .write()
2419 .insert(identifier!(vm, __doc__), value);
2420
2421 Ok(())
2422 }
2423
2424 #[pymethod]
2425 fn __dir__(&self) -> PyList {
2426 let attributes: Vec<PyObjectRef> = self
2427 .get_attributes()
2428 .into_iter()
2429 .map(|(k, _)| k.to_object())
2430 .collect();
2431 PyList::from(attributes)
2432 }
2433
2434 #[pymethod]
2435 fn __instancecheck__(&self, obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
2436 obj.real_is_instance(self.as_object(), vm)
2438 }
2439
2440 #[pymethod]
2441 fn __subclasscheck__(&self, subclass: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
2442 subclass.real_is_subclass(self.as_object(), vm)
2445 }
2446
2447 #[pyclassmethod]
2448 fn __subclasshook__(_args: FuncArgs, vm: &VirtualMachine) -> PyObjectRef {
2449 vm.ctx.not_implemented()
2450 }
2451
2452 #[pymethod]
2453 fn mro(&self) -> Vec<PyObjectRef> {
2454 self.mro_map_collect(|cls| cls.to_owned().into())
2455 }
2456}
2457
2458impl SetAttr for PyType {
2459 fn setattro(
2460 zelf: &Py<Self>,
2461 attr_name: &Py<PyStr>,
2462 value: PySetterValue,
2463 vm: &VirtualMachine,
2464 ) -> PyResult<()> {
2465 let attr_name = vm.ctx.intern_str(attr_name.as_wtf8());
2466 if zelf.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
2467 return Err(vm.new_type_error(format!(
2468 "cannot set '{}' attribute of immutable type '{}'",
2469 attr_name,
2470 zelf.slot_name()
2471 )));
2472 }
2473 if let Some(attr) = zelf.get_class_attr(attr_name) {
2474 let descr_set = attr.class().slots.descr_set.load();
2475 if let Some(descriptor) = descr_set {
2476 return descriptor(&attr, zelf.to_owned().into(), value, vm);
2477 }
2478 }
2479 let assign = value.is_assign();
2480
2481 zelf.modified();
2485
2486 if let PySetterValue::Assign(value) = value {
2487 zelf.attributes.write().insert(attr_name, value);
2488 } else {
2489 let prev_value = zelf.attributes.write().shift_remove(attr_name); if prev_value.is_none() {
2491 return Err(vm.new_attribute_error(format!(
2492 "type object '{}' has no attribute '{}'",
2493 zelf.name(),
2494 attr_name,
2495 )));
2496 }
2497 }
2498
2499 if attr_name.as_wtf8().starts_with("__") && attr_name.as_wtf8().ends_with("__") {
2500 if assign {
2501 zelf.update_slot::<true>(attr_name, &vm.ctx);
2502 } else {
2503 zelf.update_slot::<false>(attr_name, &vm.ctx);
2504 }
2505 }
2506 Ok(())
2507 }
2508}
2509
2510impl Callable for PyType {
2511 type Args = FuncArgs;
2512 fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
2513 vm_trace!("type_call: {:?}", zelf);
2514
2515 if zelf.is(vm.ctx.types.type_type) {
2516 let num_args = args.args.len();
2517 if num_args == 1 && args.kwargs.is_empty() {
2518 return Ok(args.args[0].obj_type());
2519 }
2520 if num_args != 3 {
2521 return Err(vm.new_type_error("type() takes 1 or 3 arguments"));
2522 }
2523 }
2524
2525 let obj = if let Some(slot_new) = zelf.slots.new.load() {
2526 slot_new(zelf.to_owned(), args.clone(), vm)?
2527 } else {
2528 return Err(vm.new_type_error(format!("cannot create '{}' instances", zelf.slots.name)));
2529 };
2530
2531 if !obj.class().fast_issubclass(zelf) {
2532 return Ok(obj);
2533 }
2534
2535 if let Some(init_method) = obj.class().slots.init.load() {
2536 init_method(obj.clone(), args, vm)?;
2537 }
2538 Ok(obj)
2539 }
2540}
2541
2542impl AsNumber for PyType {
2543 fn as_number() -> &'static PyNumberMethods {
2544 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
2545 or: Some(|a, b, vm| or_(a.to_owned(), b.to_owned(), vm)),
2546 ..PyNumberMethods::NOT_IMPLEMENTED
2547 };
2548 &AS_NUMBER
2549 }
2550}
2551
2552impl Representable for PyType {
2553 #[inline]
2554 fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
2555 let module = zelf.__module__(vm);
2556 let module = module.downcast_ref::<PyStr>().map(|m| m.as_wtf8());
2557
2558 let repr = match module {
2559 Some(module) if module != "builtins" => {
2560 let qualname = zelf.__qualname__(vm);
2561 let qualname = qualname.downcast_ref::<PyStr>().map(|n| n.as_wtf8());
2562 let name = zelf.name();
2563 let qualname = qualname.unwrap_or_else(|| name.as_ref());
2564 format!("<class '{module}.{qualname}'>")
2565 }
2566 _ => format!("<class '{}'>", zelf.slot_name()),
2567 };
2568 Ok(repr)
2569 }
2570}
2571
2572fn get_builtin_base_with_dict(typ: &Py<PyType>, vm: &VirtualMachine) -> Option<PyTypeRef> {
2574 let mut current = Some(typ.to_owned());
2575 while let Some(t) = current {
2576 if t.is(vm.ctx.types.type_type) {
2579 return Some(t);
2580 }
2581 if t.slots.flags.contains(PyTypeFlags::HAS_DICT)
2583 && !t.slots.flags.contains(PyTypeFlags::HEAPTYPE)
2584 {
2585 return Some(t);
2586 }
2587 current = t.__base__();
2588 }
2589 None
2590}
2591
2592fn get_dict_descriptor(base: &Py<PyType>, vm: &VirtualMachine) -> Option<PyObjectRef> {
2594 let dict_attr = identifier!(vm, __dict__);
2595 base.lookup_ref(dict_attr, vm)
2597}
2598
2599fn raise_dict_descriptor_error(obj: &PyObject, vm: &VirtualMachine) -> PyBaseExceptionRef {
2601 vm.new_type_error(format!(
2602 "this __dict__ descriptor does not support '{}' objects",
2603 obj.class().name()
2604 ))
2605}
2606
2607fn subtype_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
2608 let base = get_builtin_base_with_dict(obj.class(), vm);
2609
2610 if let Some(base_type) = base {
2611 if let Some(descr) = get_dict_descriptor(&base_type, vm) {
2612 vm.call_get_descriptor(&descr, obj.clone())
2614 .unwrap_or_else(|| Err(raise_dict_descriptor_error(&obj, vm)))
2615 } else {
2616 Err(raise_dict_descriptor_error(&obj, vm))
2617 }
2618 } else {
2619 object::object_get_dict(obj, vm).map(Into::into)
2621 }
2622}
2623
2624fn subtype_set_dict(obj: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
2626 let base = get_builtin_base_with_dict(obj.class(), vm);
2627
2628 if let Some(base_type) = base {
2629 if let Some(descr) = get_dict_descriptor(&base_type, vm) {
2630 let descr_set = descr
2632 .class()
2633 .slots
2634 .descr_set
2635 .load()
2636 .ok_or_else(|| raise_dict_descriptor_error(&obj, vm))?;
2637 descr_set(&descr, obj, PySetterValue::Assign(value), vm)
2638 } else {
2639 Err(raise_dict_descriptor_error(&obj, vm))
2640 }
2641 } else {
2642 object::object_set_dict(obj, value.try_into_value(vm)?, vm)?;
2644 Ok(())
2645 }
2646}
2647
2648fn subtype_get_weakref(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult {
2650 let weakref = obj.get_weakrefs();
2652 Ok(weakref.unwrap_or_else(|| vm.ctx.none()))
2653}
2654
2655fn subtype_set_weakref(obj: PyObjectRef, _value: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
2657 Err(vm.new_attribute_error(format!(
2658 "attribute '__weakref__' of '{}' objects is not writable",
2659 obj.class().name()
2660 )))
2661}
2662
2663fn vectorcall_type(
2688 zelf_obj: &PyObject,
2689 args: Vec<PyObjectRef>,
2690 nargs: usize,
2691 kwnames: Option<&[PyObjectRef]>,
2692 vm: &VirtualMachine,
2693) -> PyResult {
2694 let zelf: &Py<PyType> = zelf_obj.downcast_ref().unwrap();
2695
2696 if zelf.is(vm.ctx.types.type_type) {
2698 let no_kwargs = kwnames.is_none_or(|kw| kw.is_empty());
2699 if nargs == 1 && no_kwargs {
2700 return Ok(args[0].obj_type());
2701 }
2702 } else if zelf.slots.call.load().is_none() && zelf.slots.new.load().is_some() {
2703 if let Some(type_vc) = zelf.slots.vectorcall.load() {
2706 return type_vc(zelf_obj, args, nargs, kwnames, vm);
2707 }
2708 }
2709
2710 let func_args = FuncArgs::from_vectorcall_owned(args, nargs, kwnames);
2712 PyType::call(zelf, func_args, vm)
2713}
2714
2715pub(crate) fn init(ctx: &'static Context) {
2716 PyType::extend_class(ctx, ctx.types.type_type);
2717 ctx.types
2718 .type_type
2719 .slots
2720 .vectorcall
2721 .store(Some(vectorcall_type));
2722}
2723
2724pub(crate) fn call_slot_new(
2725 typ: PyTypeRef,
2726 subtype: PyTypeRef,
2727 args: FuncArgs,
2728 vm: &VirtualMachine,
2729) -> PyResult {
2730 if subtype
2732 .slots
2733 .flags
2734 .has_feature(PyTypeFlags::DISALLOW_INSTANTIATION)
2735 {
2736 return Err(vm.new_type_error(format!("cannot create '{}' instances", subtype.slot_name())));
2737 }
2738
2739 let mut staticbase = subtype.clone();
2744 while staticbase.slots.flags.has_feature(PyTypeFlags::HEAPTYPE) {
2745 if let Some(base) = staticbase.base.as_ref() {
2746 staticbase = base.clone();
2747 } else {
2748 break;
2749 }
2750 }
2751
2752 let typ_new = typ.slots.new.load();
2754 let staticbase_new = staticbase.slots.new.load();
2755 if typ_new.map(|f| f as usize) != staticbase_new.map(|f| f as usize) {
2756 return Err(vm.new_type_error(format!(
2757 "{}.__new__({}) is not safe, use {}.__new__()",
2758 typ.slot_name(),
2759 subtype.slot_name(),
2760 staticbase.slot_name()
2761 )));
2762 }
2763
2764 let slot_new = typ
2765 .slots
2766 .new
2767 .load()
2768 .expect("Should be able to find a new slot somewhere in the mro");
2769 slot_new(subtype, args, vm)
2770}
2771
2772pub(crate) fn or_(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
2773 union_::or_op(zelf, other, vm)
2774}
2775
2776fn take_next_base(bases: &mut [Vec<PyTypeRef>]) -> Option<PyTypeRef> {
2777 for base in bases.iter() {
2778 let head = base[0].clone();
2779 if !bases.iter().any(|x| x[1..].iter().any(|x| x.is(&head))) {
2780 for item in bases.iter_mut() {
2782 if item[0].is(&head) {
2783 item.remove(0);
2784 }
2785 }
2786
2787 return Some(head);
2788 }
2789 }
2790
2791 None
2792}
2793
2794fn linearise_mro(mut bases: Vec<Vec<PyTypeRef>>) -> Result<Vec<PyTypeRef>, String> {
2795 vm_trace!("Linearise MRO: {:?}", bases);
2796 for (i, base_mro) in bases.iter().enumerate() {
2802 let base = &base_mro[0]; for later_mro in &bases[i + 1..] {
2804 if later_mro[1..].iter().any(|cls| cls.is(base)) {
2807 return Err(format!(
2808 "Cannot create a consistent method resolution order (MRO) for bases {}",
2809 bases.iter().map(|x| x.first().unwrap()).format(", ")
2810 ));
2811 }
2812 }
2813 }
2814
2815 let mut result = vec![];
2816 while !bases.is_empty() {
2817 let head = take_next_base(&mut bases).ok_or_else(|| {
2818 format!(
2821 "Cannot create a consistent method resolution order (MRO) for bases {}",
2822 bases.iter().map(|x| x.first().unwrap()).format(", ")
2823 )
2824 })?;
2825
2826 result.push(head);
2827
2828 bases.retain(|x| !x.is_empty());
2829 }
2830 Ok(result)
2831}
2832
2833fn calculate_meta_class(
2834 metatype: PyTypeRef,
2835 bases: &[PyTypeRef],
2836 vm: &VirtualMachine,
2837) -> PyResult<PyTypeRef> {
2838 let mut winner = metatype;
2840 for base in bases {
2841 let base_type = base.class();
2842
2843 if winner.fast_issubclass(base_type) {
2845 continue;
2846 } else if base_type.fast_issubclass(&winner) {
2847 winner = base_type.to_owned();
2848 continue;
2849 }
2850
2851 let winner_is_subclass = winner.as_object().is_subclass(base_type.as_object(), vm)?;
2854 if winner_is_subclass {
2855 continue;
2856 }
2857
2858 let base_type_is_subclass = base_type.as_object().is_subclass(winner.as_object(), vm)?;
2859 if base_type_is_subclass {
2860 winner = base_type.to_owned();
2861 continue;
2862 }
2863
2864 return Err(vm.new_type_error(
2865 "metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass \
2866 of the metaclasses of all its bases",
2867 ));
2868 }
2869 Ok(winner)
2870}
2871
2872fn shape_differs(t1: &Py<PyType>, t2: &Py<PyType>) -> bool {
2874 t1.__basicsize__() != t2.__basicsize__() || t1.slots.itemsize != t2.slots.itemsize
2875}
2876
2877fn solid_base<'a>(typ: &'a Py<PyType>, vm: &VirtualMachine) -> &'a Py<PyType> {
2878 let base = if let Some(base) = &typ.base {
2879 solid_base(base, vm)
2880 } else {
2881 vm.ctx.types.object_type
2882 };
2883
2884 if shape_differs(typ, base) { typ } else { base }
2885}
2886
2887fn best_base<'a>(bases: &'a [PyTypeRef], vm: &VirtualMachine) -> PyResult<&'a Py<PyType>> {
2888 let mut base: Option<&Py<PyType>> = None;
2889 let mut winner: Option<&Py<PyType>> = None;
2890
2891 for base_i in bases {
2892 if !base_i.slots.flags.has_feature(PyTypeFlags::BASETYPE) {
2898 return Err(vm.new_type_error(format!(
2899 "type '{}' is not an acceptable base type",
2900 base_i.slot_name()
2901 )));
2902 }
2903
2904 let candidate = solid_base(base_i, vm);
2905 if winner.is_none() {
2906 winner = Some(candidate);
2907 base = Some(base_i.deref());
2908 } else if winner.unwrap().fast_issubclass(candidate) {
2909 } else if candidate.fast_issubclass(winner.unwrap()) {
2911 winner = Some(candidate);
2912 base = Some(base_i.deref());
2913 } else {
2914 return Err(vm.new_type_error("multiple bases have instance layout conflict"));
2915 }
2916 }
2917
2918 debug_assert!(base.is_some());
2919 Ok(base.unwrap())
2920}
2921
2922fn mangle_name(class_name: &str, name: &str) -> String {
2925 if !name.starts_with("__") || name.ends_with("__") || name.contains('.') {
2927 return name.to_string();
2928 }
2929 let class_name = class_name.trim_start_matches('_');
2931 format!("_{}{}", class_name, name)
2932}
2933
2934#[cfg(test)]
2935mod tests {
2936 use super::*;
2937
2938 fn map_ids(obj: Result<Vec<PyTypeRef>, String>) -> Result<Vec<usize>, String> {
2939 Ok(obj?.into_iter().map(|x| x.get_id()).collect())
2940 }
2941
2942 #[test]
2943 fn test_linearise() {
2944 let context = Context::genesis();
2945 let object = context.types.object_type.to_owned();
2946 let type_type = context.types.type_type.to_owned();
2947
2948 let a = PyType::new_heap(
2949 "A",
2950 vec![object.clone()],
2951 PyAttributes::default(),
2952 Default::default(),
2953 type_type.clone(),
2954 context,
2955 )
2956 .unwrap();
2957 let b = PyType::new_heap(
2958 "B",
2959 vec![object.clone()],
2960 PyAttributes::default(),
2961 Default::default(),
2962 type_type,
2963 context,
2964 )
2965 .unwrap();
2966
2967 assert_eq!(
2968 map_ids(linearise_mro(vec![
2969 vec![object.clone()],
2970 vec![object.clone()]
2971 ])),
2972 map_ids(Ok(vec![object.clone()]))
2973 );
2974 assert_eq!(
2975 map_ids(linearise_mro(vec![
2976 vec![a.clone(), object.clone()],
2977 vec![b.clone(), object.clone()],
2978 ])),
2979 map_ids(Ok(vec![a, b, object]))
2980 );
2981 }
2982}