Skip to content

Commit b6e8a87

Browse files
Resolve number slots via MRO in PyNumber and operator, ensure inherit… (RustPython#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>
1 parent 6b25fe5 commit b6e8a87

File tree

3 files changed

+126
-97
lines changed

3 files changed

+126
-97
lines changed

Lib/ctypes/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,9 +299,7 @@ def create_unicode_buffer(init, size=None):
299299
return buf
300300
elif isinstance(init, int):
301301
_sys.audit("ctypes.create_unicode_buffer", None, init)
302-
# XXX: RUSTPYTHON
303-
# buftype = c_wchar * init
304-
buftype = c_wchar.__mul__(init)
302+
buftype = c_wchar * init
305303
buf = buftype()
306304
return buf
307305
raise TypeError(init)

vm/src/protocol/number.rs

Lines changed: 110 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -443,117 +443,140 @@ impl<'a> PyNumber<'a> {
443443

444444
// PyNumber_Check
445445
pub fn check(obj: &PyObject) -> bool {
446-
let methods = &obj.class().slots.as_number;
447-
methods.int.load().is_some()
448-
|| methods.index.load().is_some()
449-
|| methods.float.load().is_some()
450-
|| obj.downcastable::<PyComplex>()
446+
let cls = &obj.class();
447+
// TODO: when we finally have a proper slot inheritance, mro_find_map can be removed
448+
// methods.int.load().is_some()
449+
// || methods.index.load().is_some()
450+
// || methods.float.load().is_some()
451+
// || obj.downcastable::<PyComplex>()
452+
let has_number = cls
453+
.mro_find_map(|x| {
454+
let methods = &x.slots.as_number;
455+
if methods.int.load().is_some()
456+
|| methods.index.load().is_some()
457+
|| methods.float.load().is_some()
458+
{
459+
Some(())
460+
} else {
461+
None
462+
}
463+
})
464+
.is_some();
465+
has_number || obj.downcastable::<PyComplex>()
451466
}
452467
}
453468

454469
impl PyNumber<'_> {
455470
// PyIndex_Check
456471
pub fn is_index(self) -> bool {
457-
self.class().slots.as_number.index.load().is_some()
472+
self.class()
473+
.mro_find_map(|x| x.slots.as_number.index.load())
474+
.is_some()
458475
}
459476

460477
#[inline]
461478
pub fn int(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
462-
self.class().slots.as_number.int.load().map(|f| {
463-
let ret = f(self, vm)?;
464-
465-
if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
466-
return Ok(ret.to_owned());
467-
}
468-
469-
let ret_class = ret.class().to_owned();
470-
if let Some(ret) = ret.downcast_ref::<PyInt>() {
471-
warnings::warn(
472-
vm.ctx.exceptions.deprecation_warning,
473-
format!(
474-
"__int__ returned non-int (type {ret_class}). \
479+
self.class()
480+
.mro_find_map(|x| x.slots.as_number.int.load())
481+
.map(|f| {
482+
let ret = f(self, vm)?;
483+
484+
if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
485+
return Ok(ret.to_owned());
486+
}
487+
488+
let ret_class = ret.class().to_owned();
489+
if let Some(ret) = ret.downcast_ref::<PyInt>() {
490+
warnings::warn(
491+
vm.ctx.exceptions.deprecation_warning,
492+
format!(
493+
"__int__ returned non-int (type {ret_class}). \
475494
The ability to return an instance of a strict subclass of int \
476495
is deprecated, and may be removed in a future version of Python."
477-
),
478-
1,
479-
vm,
480-
)?;
481-
482-
Ok(ret.to_owned())
483-
} else {
484-
Err(vm.new_type_error(format!(
485-
"{}.__int__ returned non-int(type {})",
486-
self.class(),
487-
ret_class
488-
)))
489-
}
490-
})
496+
),
497+
1,
498+
vm,
499+
)?;
500+
501+
Ok(ret.to_owned())
502+
} else {
503+
Err(vm.new_type_error(format!(
504+
"{}.__int__ returned non-int(type {})",
505+
self.class(),
506+
ret_class
507+
)))
508+
}
509+
})
491510
}
492511

493512
#[inline]
494513
pub fn index(self, vm: &VirtualMachine) -> Option<PyResult<PyIntRef>> {
495-
self.class().slots.as_number.index.load().map(|f| {
496-
let ret = f(self, vm)?;
497-
498-
if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
499-
return Ok(ret.to_owned());
500-
}
501-
502-
let ret_class = ret.class().to_owned();
503-
if let Some(ret) = ret.downcast_ref::<PyInt>() {
504-
warnings::warn(
505-
vm.ctx.exceptions.deprecation_warning,
506-
format!(
507-
"__index__ returned non-int (type {ret_class}). \
514+
self.class()
515+
.mro_find_map(|x| x.slots.as_number.index.load())
516+
.map(|f| {
517+
let ret = f(self, vm)?;
518+
519+
if let Some(ret) = ret.downcast_ref_if_exact::<PyInt>(vm) {
520+
return Ok(ret.to_owned());
521+
}
522+
523+
let ret_class = ret.class().to_owned();
524+
if let Some(ret) = ret.downcast_ref::<PyInt>() {
525+
warnings::warn(
526+
vm.ctx.exceptions.deprecation_warning,
527+
format!(
528+
"__index__ returned non-int (type {ret_class}). \
508529
The ability to return an instance of a strict subclass of int \
509530
is deprecated, and may be removed in a future version of Python."
510-
),
511-
1,
512-
vm,
513-
)?;
514-
515-
Ok(ret.to_owned())
516-
} else {
517-
Err(vm.new_type_error(format!(
518-
"{}.__index__ returned non-int(type {})",
519-
self.class(),
520-
ret_class
521-
)))
522-
}
523-
})
531+
),
532+
1,
533+
vm,
534+
)?;
535+
536+
Ok(ret.to_owned())
537+
} else {
538+
Err(vm.new_type_error(format!(
539+
"{}.__index__ returned non-int(type {})",
540+
self.class(),
541+
ret_class
542+
)))
543+
}
544+
})
524545
}
525546

526547
#[inline]
527548
pub fn float(self, vm: &VirtualMachine) -> Option<PyResult<PyRef<PyFloat>>> {
528-
self.class().slots.as_number.float.load().map(|f| {
529-
let ret = f(self, vm)?;
530-
531-
if let Some(ret) = ret.downcast_ref_if_exact::<PyFloat>(vm) {
532-
return Ok(ret.to_owned());
533-
}
534-
535-
let ret_class = ret.class().to_owned();
536-
if let Some(ret) = ret.downcast_ref::<PyFloat>() {
537-
warnings::warn(
538-
vm.ctx.exceptions.deprecation_warning,
539-
format!(
540-
"__float__ returned non-float (type {ret_class}). \
549+
self.class()
550+
.mro_find_map(|x| x.slots.as_number.float.load())
551+
.map(|f| {
552+
let ret = f(self, vm)?;
553+
554+
if let Some(ret) = ret.downcast_ref_if_exact::<PyFloat>(vm) {
555+
return Ok(ret.to_owned());
556+
}
557+
558+
let ret_class = ret.class().to_owned();
559+
if let Some(ret) = ret.downcast_ref::<PyFloat>() {
560+
warnings::warn(
561+
vm.ctx.exceptions.deprecation_warning,
562+
format!(
563+
"__float__ returned non-float (type {ret_class}). \
541564
The ability to return an instance of a strict subclass of float \
542565
is deprecated, and may be removed in a future version of Python."
543-
),
544-
1,
545-
vm,
546-
)?;
547-
548-
Ok(ret.to_owned())
549-
} else {
550-
Err(vm.new_type_error(format!(
551-
"{}.__float__ returned non-float(type {})",
552-
self.class(),
553-
ret_class
554-
)))
555-
}
556-
})
566+
),
567+
1,
568+
vm,
569+
)?;
570+
571+
Ok(ret.to_owned())
572+
} else {
573+
Err(vm.new_type_error(format!(
574+
"{}.__float__ returned non-float(type {})",
575+
self.class(),
576+
ret_class
577+
)))
578+
}
579+
})
557580
}
558581
}
559582

vm/src/vm/vm_ops.rs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -160,11 +160,12 @@ impl VirtualMachine {
160160
let class_a = a.class();
161161
let class_b = b.class();
162162

163-
let slot_a = class_a.slots.as_number.left_binary_op(op_slot);
163+
// Look up number slots across MRO for inheritance
164+
let slot_a = class_a.mro_find_map(|x| x.slots.as_number.left_binary_op(op_slot));
164165
let mut slot_b = None;
165166

166167
if !class_a.is(class_b) {
167-
let slot_bb = class_b.slots.as_number.right_binary_op(op_slot);
168+
let slot_bb = class_b.mro_find_map(|x| x.slots.as_number.right_binary_op(op_slot));
168169
if slot_bb.map(|x| x as usize) != slot_a.map(|x| x as usize) {
169170
slot_b = slot_bb;
170171
}
@@ -230,7 +231,10 @@ impl VirtualMachine {
230231
iop_slot: PyNumberBinaryOp,
231232
op_slot: PyNumberBinaryOp,
232233
) -> PyResult {
233-
if let Some(slot) = a.class().slots.as_number.left_binary_op(iop_slot) {
234+
if let Some(slot) = a
235+
.class()
236+
.mro_find_map(|x| x.slots.as_number.left_binary_op(iop_slot))
237+
{
234238
let x = slot(a, b, self)?;
235239
if !x.is(&self.ctx.not_implemented) {
236240
return Ok(x);
@@ -266,11 +270,12 @@ impl VirtualMachine {
266270
let class_b = b.class();
267271
let class_c = c.class();
268272

269-
let slot_a = class_a.slots.as_number.left_ternary_op(op_slot);
273+
// Look up number slots across MRO for inheritance
274+
let slot_a = class_a.mro_find_map(|x| x.slots.as_number.left_ternary_op(op_slot));
270275
let mut slot_b = None;
271276

272277
if !class_a.is(class_b) {
273-
let slot_bb = class_b.slots.as_number.right_ternary_op(op_slot);
278+
let slot_bb = class_b.mro_find_map(|x| x.slots.as_number.right_ternary_op(op_slot));
274279
if slot_bb.map(|x| x as usize) != slot_a.map(|x| x as usize) {
275280
slot_b = slot_bb;
276281
}
@@ -299,7 +304,7 @@ impl VirtualMachine {
299304
}
300305
}
301306

302-
if let Some(slot_c) = class_c.slots.as_number.left_ternary_op(op_slot)
307+
if let Some(slot_c) = class_c.mro_find_map(|x| x.slots.as_number.left_ternary_op(op_slot))
303308
&& slot_a.is_some_and(|slot_a| !std::ptr::fn_addr_eq(slot_a, slot_c))
304309
&& slot_b.is_some_and(|slot_b| !std::ptr::fn_addr_eq(slot_b, slot_c))
305310
{
@@ -338,7 +343,10 @@ impl VirtualMachine {
338343
op_slot: PyNumberTernaryOp,
339344
op_str: &str,
340345
) -> PyResult {
341-
if let Some(slot) = a.class().slots.as_number.left_ternary_op(iop_slot) {
346+
if let Some(slot) = a
347+
.class()
348+
.mro_find_map(|x| x.slots.as_number.left_ternary_op(iop_slot))
349+
{
342350
let x = slot(a, b, c, self)?;
343351
if !x.is(&self.ctx.not_implemented) {
344352
return Ok(x);

0 commit comments

Comments
 (0)