Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
slotdef
  • Loading branch information
youknowone committed Dec 28, 2025
commit 0ef02747dc6f8f82e53be6bce70a9448920f1119
1 change: 1 addition & 0 deletions .cspell.dict/cpython.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
argtypes
asdl
asname
attro
augassign
badcert
badsyntax
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1399,7 +1399,6 @@ class Q2:
__qualname__ = object()
__slots__ = ["__qualname__"]

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_slots_descriptor(self):
# Issue2115: slot descriptors did not correctly check
# the type of the given object
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -2126,8 +2126,6 @@ class DataDescriptorSub(DataDescriptorWithNoGet,
self.assertFalse(inspect.ismethoddescriptor(MethodDescriptorSub))
self.assertFalse(inspect.ismethoddescriptor(DataDescriptorSub))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_builtin_descriptors(self):
builtin_slot_wrapper = int.__add__ # This one is mentioned in docs.
class Owner:
Expand Down Expand Up @@ -2217,8 +2215,6 @@ class DataDescriptor2:
self.assertTrue(inspect.isdatadescriptor(DataDescriptor2()),
'class with __set__ = None is a data descriptor')

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_slot(self):
class Slotted:
__slots__ = 'foo',
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_sqlite3/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,6 @@ def test_sqlite_row_hash_cmp(self):

self.assertEqual(hash(row_1), hash(row_2))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_sqlite_row_as_sequence(self):
""" Checks if the row object can act like a sequence """
self.con.row_factory = sqlite.Row
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_unittest/testmock/testhelpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -884,8 +884,6 @@ def f(a, self): pass
a.f.assert_called_with(self=10)


# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_autospec_data_descriptor(self):
class Descriptor(object):
def __init__(self, value):
Expand Down
108 changes: 105 additions & 3 deletions crates/vm/src/builtins/descriptor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ use crate::{
common::hash::PyHash,
convert::{ToPyObject, ToPyResult},
function::{FuncArgs, PyMethodDef, PyMethodFlags, PySetterValue},
protocol::{PyNumberBinaryFunc, PyNumberTernaryFunc, PyNumberUnaryFunc},
types::{
Callable, Comparable, DelFunc, DescrGetFunc, DescrSetFunc, GenericMethod, GetDescriptor,
GetattroFunc, HashFunc, Hashable, InitFunc, IterFunc, IterNextFunc, PyComparisonOp,
Representable, RichCompareFunc, SetattroFunc, StringifyFunc,
GetattroFunc, HashFunc, Hashable, InitFunc, IterFunc, IterNextFunc, MapAssSubscriptFunc,
MapLenFunc, MapSubscriptFunc, PyComparisonOp, Representable, RichCompareFunc,
SeqAssItemFunc, SeqConcatFunc, SeqContainsFunc, SeqItemFunc, SeqLenFunc, SeqRepeatFunc,
SetattroFunc, StringifyFunc,
},
};
use rustpython_common::lock::PyRwLock;
Expand Down Expand Up @@ -394,7 +397,6 @@ pub fn init(ctx: &Context) {

// PyWrapper - wrapper_descriptor

/// Type-erased slot function - mirrors CPython's void* d_wrapped
/// Each variant knows how to call the wrapped function with proper types
#[derive(Clone, Copy)]
pub enum SlotFunc {
Expand All @@ -420,6 +422,25 @@ pub enum SlotFunc {
DescrGet(DescrGetFunc),
DescrSet(DescrSetFunc), // __set__
DescrDel(DescrSetFunc), // __delete__ (same func type, different PySetterValue)

// Sequence sub-slots (sq_*)
SeqLength(SeqLenFunc),
SeqConcat(SeqConcatFunc),
SeqRepeat(SeqRepeatFunc),
SeqItem(SeqItemFunc),
SeqAssItem(SeqAssItemFunc),
SeqContains(SeqContainsFunc),

// Mapping sub-slots (mp_*)
MapLength(MapLenFunc),
MapSubscript(MapSubscriptFunc),
MapAssSubscript(MapAssSubscriptFunc),

// Number sub-slots (nb_*) - grouped by signature
NumBoolean(PyNumberUnaryFunc<bool>), // __bool__
NumUnary(PyNumberUnaryFunc), // __int__, __float__, __index__
NumBinary(PyNumberBinaryFunc), // __add__, __sub__, __mul__, etc.
NumTernary(PyNumberTernaryFunc), // __pow__
}

impl std::fmt::Debug for SlotFunc {
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Expand All @@ -440,6 +461,22 @@ impl std::fmt::Debug for SlotFunc {
SlotFunc::DescrGet(_) => write!(f, "SlotFunc::DescrGet(...)"),
SlotFunc::DescrSet(_) => write!(f, "SlotFunc::DescrSet(...)"),
SlotFunc::DescrDel(_) => write!(f, "SlotFunc::DescrDel(...)"),
// Sequence sub-slots
SlotFunc::SeqLength(_) => write!(f, "SlotFunc::SeqLength(...)"),
SlotFunc::SeqConcat(_) => write!(f, "SlotFunc::SeqConcat(...)"),
SlotFunc::SeqRepeat(_) => write!(f, "SlotFunc::SeqRepeat(...)"),
SlotFunc::SeqItem(_) => write!(f, "SlotFunc::SeqItem(...)"),
SlotFunc::SeqAssItem(_) => write!(f, "SlotFunc::SeqAssItem(...)"),
SlotFunc::SeqContains(_) => write!(f, "SlotFunc::SeqContains(...)"),
// Mapping sub-slots
SlotFunc::MapLength(_) => write!(f, "SlotFunc::MapLength(...)"),
SlotFunc::MapSubscript(_) => write!(f, "SlotFunc::MapSubscript(...)"),
SlotFunc::MapAssSubscript(_) => write!(f, "SlotFunc::MapAssSubscript(...)"),
// Number sub-slots
SlotFunc::NumBoolean(_) => write!(f, "SlotFunc::NumBoolean(...)"),
SlotFunc::NumUnary(_) => write!(f, "SlotFunc::NumUnary(...)"),
SlotFunc::NumBinary(_) => write!(f, "SlotFunc::NumBinary(...)"),
SlotFunc::NumTernary(_) => write!(f, "SlotFunc::NumTernary(...)"),
}
}
}
Expand Down Expand Up @@ -541,6 +578,71 @@ impl SlotFunc {
func(&obj, instance, PySetterValue::Delete, vm)?;
Ok(vm.ctx.none())
}
// Sequence sub-slots
SlotFunc::SeqLength(func) => {
args.bind::<()>(vm)?;
let len = func(obj.sequence_unchecked(), vm)?;
Ok(vm.ctx.new_int(len).into())
}
SlotFunc::SeqConcat(func) => {
let (other,): (PyObjectRef,) = args.bind(vm)?;
func(obj.sequence_unchecked(), &other, vm)
}
SlotFunc::SeqRepeat(func) => {
let (n,): (isize,) = args.bind(vm)?;
func(obj.sequence_unchecked(), n, vm)
}
SlotFunc::SeqItem(func) => {
let (index,): (isize,) = args.bind(vm)?;
func(obj.sequence_unchecked(), index, vm)
}
SlotFunc::SeqAssItem(func) => {
let (index, value): (isize, crate::function::OptionalArg<PyObjectRef>) =
args.bind(vm)?;
func(obj.sequence_unchecked(), index, value.into_option(), vm)?;
Ok(vm.ctx.none())
}
SlotFunc::SeqContains(func) => {
let (item,): (PyObjectRef,) = args.bind(vm)?;
let result = func(obj.sequence_unchecked(), &item, vm)?;
Ok(vm.ctx.new_bool(result).into())
}
// Mapping sub-slots
SlotFunc::MapLength(func) => {
args.bind::<()>(vm)?;
let len = func(obj.mapping_unchecked(), vm)?;
Ok(vm.ctx.new_int(len).into())
}
SlotFunc::MapSubscript(func) => {
let (key,): (PyObjectRef,) = args.bind(vm)?;
func(obj.mapping_unchecked(), &key, vm)
}
SlotFunc::MapAssSubscript(func) => {
let (key, value): (PyObjectRef, crate::function::OptionalArg<PyObjectRef>) =
args.bind(vm)?;
func(obj.mapping_unchecked(), &key, value.into_option(), vm)?;
Ok(vm.ctx.none())
}
// Number sub-slots
SlotFunc::NumBoolean(func) => {
args.bind::<()>(vm)?;
let result = func(obj.number(), vm)?;
Ok(vm.ctx.new_bool(result).into())
}
SlotFunc::NumUnary(func) => {
args.bind::<()>(vm)?;
func(obj.number(), vm)
}
SlotFunc::NumBinary(func) => {
let (other,): (PyObjectRef,) = args.bind(vm)?;
func(&obj, &other, vm)
}
SlotFunc::NumTernary(func) => {
let (y, z): (PyObjectRef, crate::function::OptionalArg<PyObjectRef>) =
args.bind(vm)?;
let z = z.unwrap_or_else(|| vm.ctx.none());
func(&obj, &y, &z, vm)
}
}
}
}
Expand Down
172 changes: 5 additions & 167 deletions crates/vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ use crate::{
protocol::{PyIterReturn, PyNumberMethods},
types::{
AsNumber, Callable, Constructor, GetAttr, Initializer, PyTypeFlags, PyTypeSlots,
Representable, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot,
Representable, SLOT_DEFS, SetAttr, TypeDataRef, TypeDataRefMut, TypeDataSlot,
},
};
use indexmap::{IndexMap, map::Entry};
Expand Down Expand Up @@ -464,173 +464,11 @@ impl PyType {

/// Inherit slots from base type. inherit_slots
pub(crate) fn inherit_slots(&self, base: &Self) {
macro_rules! copyslot {
($slot:ident) => {
if self.slots.$slot.load().is_none() {
if let Some(base_val) = base.slots.$slot.load() {
self.slots.$slot.store(Some(base_val));
}
}
};
}

// Copy init slot only if base actually defines it (not just inherited)
// This is needed for multiple inheritance where a later base might
// have a more specific init slot
macro_rules! copyslot_defined {
($slot:ident) => {
if self.slots.$slot.load().is_none() {
if let Some(base_val) = base.slots.$slot.load() {
// SLOTDEFINED: base->SLOT && (basebase == NULL || base->SLOT != basebase->SLOT)
let basebase = base.base.as_ref();
let slot_defined = match basebase {
None => true,
Some(bb) => bb.slots.$slot.load().map(|v| v as usize)
!= Some(base_val as usize),
};
if slot_defined {
self.slots.$slot.store(Some(base_val));
}
}
}
};
}

// Core slots
copyslot!(hash);
copyslot!(call);
copyslot!(str);
copyslot!(repr);
copyslot!(getattro);
copyslot!(setattro);
copyslot!(richcompare);
copyslot!(iter);
copyslot!(iternext);
copyslot!(descr_get);
copyslot!(descr_set);
// init uses SLOTDEFINED check for multiple inheritance support
copyslot_defined!(init);
copyslot!(del);
// new is handled by set_new()
// as_buffer is inherited at type creation time (not AtomicCell)

// Sub-slots (number, sequence, mapping)
self.inherit_number_slots(base);
self.inherit_sequence_slots(base);
self.inherit_mapping_slots(base);
}

/// Inherit number sub-slots from base type
fn inherit_number_slots(&self, base: &Self) {
macro_rules! copy_num_slot {
($slot:ident) => {
if self.slots.as_number.$slot.load().is_none() {
if let Some(base_val) = base.slots.as_number.$slot.load() {
self.slots.as_number.$slot.store(Some(base_val));
}
}
};
// Use SLOT_DEFS to iterate all slots
// Note: as_buffer is handled in inherit_static_slots (not AtomicCell)
for def in SLOT_DEFS {
def.accessor.copyslot_if_none(self, base);
}

// Binary operations
copy_num_slot!(add);
copy_num_slot!(right_add);
copy_num_slot!(inplace_add);
copy_num_slot!(subtract);
copy_num_slot!(right_subtract);
copy_num_slot!(inplace_subtract);
copy_num_slot!(multiply);
copy_num_slot!(right_multiply);
copy_num_slot!(inplace_multiply);
copy_num_slot!(remainder);
copy_num_slot!(right_remainder);
copy_num_slot!(inplace_remainder);
copy_num_slot!(divmod);
copy_num_slot!(right_divmod);
copy_num_slot!(power);
copy_num_slot!(right_power);
copy_num_slot!(inplace_power);

// Bitwise operations
copy_num_slot!(lshift);
copy_num_slot!(right_lshift);
copy_num_slot!(inplace_lshift);
copy_num_slot!(rshift);
copy_num_slot!(right_rshift);
copy_num_slot!(inplace_rshift);
copy_num_slot!(and);
copy_num_slot!(right_and);
copy_num_slot!(inplace_and);
copy_num_slot!(xor);
copy_num_slot!(right_xor);
copy_num_slot!(inplace_xor);
copy_num_slot!(or);
copy_num_slot!(right_or);
copy_num_slot!(inplace_or);

// Division operations
copy_num_slot!(floor_divide);
copy_num_slot!(right_floor_divide);
copy_num_slot!(inplace_floor_divide);
copy_num_slot!(true_divide);
copy_num_slot!(right_true_divide);
copy_num_slot!(inplace_true_divide);

// Matrix multiplication
copy_num_slot!(matrix_multiply);
copy_num_slot!(right_matrix_multiply);
copy_num_slot!(inplace_matrix_multiply);

// Unary operations
copy_num_slot!(negative);
copy_num_slot!(positive);
copy_num_slot!(absolute);
copy_num_slot!(boolean);
copy_num_slot!(invert);

// Conversion
copy_num_slot!(int);
copy_num_slot!(float);
copy_num_slot!(index);
}

/// Inherit sequence sub-slots from base type
fn inherit_sequence_slots(&self, base: &Self) {
macro_rules! copy_seq_slot {
($slot:ident) => {
if self.slots.as_sequence.$slot.load().is_none() {
if let Some(base_val) = base.slots.as_sequence.$slot.load() {
self.slots.as_sequence.$slot.store(Some(base_val));
}
}
};
}

copy_seq_slot!(length);
copy_seq_slot!(concat);
copy_seq_slot!(repeat);
copy_seq_slot!(item);
copy_seq_slot!(ass_item);
copy_seq_slot!(contains);
copy_seq_slot!(inplace_concat);
copy_seq_slot!(inplace_repeat);
}

/// Inherit mapping sub-slots from base type
fn inherit_mapping_slots(&self, base: &Self) {
macro_rules! copy_map_slot {
($slot:ident) => {
if self.slots.as_mapping.$slot.load().is_none() {
if let Some(base_val) = base.slots.as_mapping.$slot.load() {
self.slots.as_mapping.$slot.store(Some(base_val));
}
}
};
}

copy_map_slot!(length);
copy_map_slot!(subscript);
copy_map_slot!(ass_subscript);
}

// This is used for class initialization where the vm is not yet available.
Expand Down
Loading