Resolve number slots via MRO in PyNumber and operator, ensure inherit… (#6222)

* Resolve number slots via MRO in PyNumber and operator, ensure inherited and dynamically added methods are found.

Use class().mro_find_map() to mimic the same behaviour as CPython.

Signed-off-by: Yash Suthar <yashsuthar983@gmail.com>
This commit is contained in:
Yash Suthar authored and GitHub committed 2025-10-28 12:00:22 +09:00
1 parent 6b25fe5c95
commit b6e8a875ac
3 files changed
+117 -88

No files matched your search

+1 -3
View File
@@ -299,9 +299,7 @@ def create_unicode_buffer(init, size=None):
return buf
elif isinstance(init, int):
_sys.audit("ctypes.create_unicode_buffer", None, init)
# XXX: RUSTPYTHON
# buftype = c_wchar * init
buftype = c_wchar.__mul__(init)
buftype = c_wchar * init
buf = buftype()
return buf
raise TypeError(init)
+101 -78
View File
@@ -443,117 +443,140 @@ impl<'a> PyNumber<'a> {
// PyNumber_Check
pub fn check(obj: &PyObject) -> bool {
let methods = &obj.class().slots.as_number;
methods.int.load().is_some()
|| methods.index.load().is_some()
|| methods.float.load().is_some()
|| obj.downcastable::<PyComplex>()
let cls = &obj.class();
// TODO: when we finally have a proper slot inheritance, mro_find_map can be removed
// methods.int.load().is_some()
// || methods.index.load().is_some()
// || methods.float.load().is_some()
// || obj.downcastable::<PyComplex>()
let has_number = cls
.mro_find_map(|x| {
let methods = &x.slots.as_number;
if methods.int.load().is_some()
|| methods.index.load().is_some()
|| methods.float.load().is_some()
{
Some(())
} else {
None
}
})
.is_some();
has_number || obj.downcastable::<PyComplex>()
}
}
impl PyNumber<'_> {
// PyIndex_Check
pub fn is_index(self) -> bool {
self.class().slots.as_number.index.load().is_some()
self.class()
.mro_find_map(|x| x.slots.as_number.index.load())
.is_some()
}
#[inline]
pub fn int(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
self.class().slots.as_number.int.load().map(|f| {
let ret = f(self, vm)?;
self.class()
.mro_find_map(|x| x.slots.as_number.int.load())
.map(|f| {
let ret = f(self, vm)?;
if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
return Ok(ret.to_owned());
}
if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
return Ok(ret.to_owned());
}
let ret_class = ret.class().to_owned();
if let Some(ret) = ret.downcast_ref::<PyInt>() {
warnings::warn(
vm.ctx.exceptions.deprecation_warning,
format!(
"__int__ returned non-int (type {ret_class}). \
let ret_class = ret.class().to_owned();
if let Some(ret) = ret.downcast_ref::<PyInt>() {
warnings::warn(
vm.ctx.exceptions.deprecation_warning,
format!(
"__int__ returned non-int (type {ret_class}). \
The ability to return an instance of a strict subclass of int \
is deprecated, and may be removed in a future version of Python."
),
1,
vm,
)?;
),
1,
vm,
)?;
Ok(ret.to_owned())
} else {
Err(vm.new_type_error(format!(
"{}.__int__ returned non-int(type {})",
self.class(),
ret_class
)))
}
})
Ok(ret.to_owned())
} else {
Err(vm.new_type_error(format!(
"{}.__int__ returned non-int(type {})",
self.class(),
ret_class
)))
}
})
}
#[inline]
pub fn index(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
self.class().slots.as_number.index.load().map(|f| {
let ret = f(self, vm)?;
self.class()
.mro_find_map(|x| x.slots.as_number.index.load())
.map(|f| {
let ret = f(self, vm)?;
if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
return Ok(ret.to_owned());
}
if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
return Ok(ret.to_owned());
}
let ret_class = ret.class().to_owned();
if let Some(ret) = ret.downcast_ref::<PyInt>() {
warnings::warn(
vm.ctx.exceptions.deprecation_warning,
format!(
"__index__ returned non-int (type {ret_class}). \
let ret_class = ret.class().to_owned();
if let Some(ret) = ret.downcast_ref::<PyInt>() {
warnings::warn(
vm.ctx.exceptions.deprecation_warning,
format!(
"__index__ returned non-int (type {ret_class}). \
The ability to return an instance of a strict subclass of int \
is deprecated, and may be removed in a future version of Python."
),
1,
vm,
)?;
),
1,
vm,
)?;
Ok(ret.to_owned())
} else {
Err(vm.new_type_error(format!(
"{}.__index__ returned non-int(type {})",
self.class(),
ret_class
)))
}
})
Ok(ret.to_owned())
} else {
Err(vm.new_type_error(format!(
"{}.__index__ returned non-int(type {})",
self.class(),
ret_class
)))
}
})
}
#[inline]
pub fn float(self, vm: &VirtualMachine) -> Option<PyResult<PyRef<PyFloat>>> {
self.class().slots.as_number.float.load().map(|f| {
let ret = f(self, vm)?;
self.class()
.mro_find_map(|x| x.slots.as_number.float.load())
.map(|f| {
let ret = f(self, vm)?;
if let Some(ret) = ret.downcast_ref_if_exact::<PyFloat>(vm) {
return Ok(ret.to_owned());
}
if let Some(ret) = ret.downcast_ref_if_exact::<PyFloat>(vm) {
return Ok(ret.to_owned());
}
let ret_class = ret.class().to_owned();
if let Some(ret) = ret.downcast_ref::<PyFloat>() {
warnings::warn(
vm.ctx.exceptions.deprecation_warning,
format!(
"__float__ returned non-float (type {ret_class}). \
let ret_class = ret.class().to_owned();
if let Some(ret) = ret.downcast_ref::<PyFloat>() {
warnings::warn(
vm.ctx.exceptions.deprecation_warning,
format!(
"__float__ returned non-float (type {ret_class}). \
The ability to return an instance of a strict subclass of float \
is deprecated, and may be removed in a future version of Python."
),
1,
vm,
)?;
),
1,
vm,
)?;
Ok(ret.to_owned())
} else {
Err(vm.new_type_error(format!(
"{}.__float__ returned non-float(type {})",
self.class(),
ret_class
)))
}
})
Ok(ret.to_owned())
} else {
Err(vm.new_type_error(format!(
"{}.__float__ returned non-float(type {})",
self.class(),
ret_class
)))
}
})
}
}
+15 -7
View File
@@ -160,11 +160,12 @@ impl VirtualMachine {
let class_a = a.class();
let class_b = b.class();
let slot_a = class_a.slots.as_number.left_binary_op(op_slot);
// Look up number slots across MRO for inheritance
let slot_a = class_a.mro_find_map(|x| x.slots.as_number.left_binary_op(op_slot));
let mut slot_b = None;
if !class_a.is(class_b) {
let slot_bb = class_b.slots.as_number.right_binary_op(op_slot);
let slot_bb = class_b.mro_find_map(|x| x.slots.as_number.right_binary_op(op_slot));
if slot_bb.map(|x| x as usize) != slot_a.map(|x| x as usize) {
slot_b = slot_bb;
}
@@ -230,7 +231,10 @@ impl VirtualMachine {
iop_slot: PyNumberBinaryOp,
op_slot: PyNumberBinaryOp,
) -> PyResult {
if let Some(slot) = a.class().slots.as_number.left_binary_op(iop_slot) {
if let Some(slot) = a
.class()
.mro_find_map(|x| x.slots.as_number.left_binary_op(iop_slot))
{
let x = slot(a, b, self)?;
if !x.is(&self.ctx.not_implemented) {
return Ok(x);
@@ -266,11 +270,12 @@ impl VirtualMachine {
let class_b = b.class();
let class_c = c.class();
let slot_a = class_a.slots.as_number.left_ternary_op(op_slot);
// Look up number slots across MRO for inheritance
let slot_a = class_a.mro_find_map(|x| x.slots.as_number.left_ternary_op(op_slot));
let mut slot_b = None;
if !class_a.is(class_b) {
let slot_bb = class_b.slots.as_number.right_ternary_op(op_slot);
let slot_bb = class_b.mro_find_map(|x| x.slots.as_number.right_ternary_op(op_slot));
if slot_bb.map(|x| x as usize) != slot_a.map(|x| x as usize) {
slot_b = slot_bb;
}
@@ -299,7 +304,7 @@ impl VirtualMachine {
}
}
if let Some(slot_c) = class_c.slots.as_number.left_ternary_op(op_slot)
if let Some(slot_c) = class_c.mro_find_map(|x| x.slots.as_number.left_ternary_op(op_slot))
&& slot_a.is_some_and(|slot_a| !std::ptr::fn_addr_eq(slot_a, slot_c))
&& slot_b.is_some_and(|slot_b| !std::ptr::fn_addr_eq(slot_b, slot_c))
{
@@ -338,7 +343,10 @@ impl VirtualMachine {
op_slot: PyNumberTernaryOp,
op_str: &str,
) -> PyResult {
if let Some(slot) = a.class().slots.as_number.left_ternary_op(iop_slot) {
if let Some(slot) = a
.class()
.mro_find_map(|x| x.slots.as_number.left_ternary_op(iop_slot))
{
let x = slot(a, b, c, self)?;
if !x.is(&self.ctx.not_implemented) {
return Ok(x);