diff --git a/Lib/test/test_float.py b/Lib/test/test_float.py index 39208dab9..bc3d5bdc7 100644 --- a/Lib/test/test_float.py +++ b/Lib/test/test_float.py @@ -177,8 +177,6 @@ class GeneralFloatCases(unittest.TestCase): self.assertEqual(float(" 25.e-1 "), 2.5) self.assertAlmostEqual(float(" .25e-1 "), .025) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_floatconversion(self): # Make sure that calls to __float__() work properly class Foo1(object): diff --git a/stdlib/src/math.rs b/stdlib/src/math.rs index e118ce77c..08f450323 100644 --- a/stdlib/src/math.rs +++ b/stdlib/src/math.rs @@ -454,8 +454,8 @@ mod math { fn ceil(x: PyObjectRef, vm: &VirtualMachine) -> PyResult { let result_or_err = try_magic_method(identifier!(vm, __ceil__), vm, &x); if result_or_err.is_err() { - if let Ok(Some(v)) = x.try_to_f64(vm) { - let v = try_f64_to_bigint(v.ceil(), vm)?; + if let Ok(Some(v)) = x.try_float_opt(vm) { + let v = try_f64_to_bigint(v.to_f64().ceil(), vm)?; return Ok(vm.ctx.new_int(v).into()); } } @@ -466,8 +466,8 @@ mod math { fn floor(x: PyObjectRef, vm: &VirtualMachine) -> PyResult { let result_or_err = try_magic_method(identifier!(vm, __floor__), vm, &x); if result_or_err.is_err() { - if let Ok(Some(v)) = x.try_to_f64(vm) { - let v = try_f64_to_bigint(v.floor(), vm)?; + if let Ok(Some(v)) = x.try_float_opt(vm) { + let v = try_f64_to_bigint(v.to_f64().floor(), vm)?; return Ok(vm.ctx.new_int(v).into()); } } diff --git a/vm/src/buffer.rs b/vm/src/buffer.rs index 046162fb1..f2e05e828 100644 --- a/vm/src/buffer.rs +++ b/vm/src/buffer.rs @@ -1,8 +1,8 @@ use crate::{ - builtins::{float, PyBaseExceptionRef, PyBytesRef, PyTuple, PyTupleRef, PyTypeRef}, + builtins::{PyBaseExceptionRef, PyBytesRef, PyTuple, PyTupleRef, PyTypeRef}, common::{static_cell, str::wchar_t}, convert::ToPyObject, - function::{ArgBytesLike, ArgIntoBool}, + function::{ArgBytesLike, ArgIntoBool, ArgIntoFloat}, PyObjectRef, PyResult, TryFromObject, VirtualMachine, }; use half::f16; @@ -521,7 +521,7 @@ macro_rules! make_pack_float { arg: PyObjectRef, data: &mut [u8], ) -> PyResult<()> { - let f = float::try_float(&arg, vm)? as $T; + let f = *ArgIntoFloat::try_from_object(vm, arg)? as $T; f.to_bits().pack_int::(data); Ok(()) } @@ -539,7 +539,7 @@ make_pack_float!(f64); impl Packable for f16 { fn pack(vm: &VirtualMachine, arg: PyObjectRef, data: &mut [u8]) -> PyResult<()> { - let f_64 = float::try_float(&arg, vm)?; + let f_64 = *ArgIntoFloat::try_from_object(vm, arg)?; let f_16 = f16::from_f64(f_64); if f_16.is_infinite() != f_64.is_infinite() { return Err(vm.new_overflow_error("float too large to pack with e format".to_owned())); diff --git a/vm/src/builtins/complex.rs b/vm/src/builtins/complex.rs index 2cfc84c7b..9e4f5af90 100644 --- a/vm/src/builtins/complex.rs +++ b/vm/src/builtins/complex.rs @@ -65,8 +65,8 @@ impl PyObjectRef { if let Some(complex) = self.payload_if_subclass::(vm) { return Ok(Some((complex.value, true))); } - if let Some(float) = self.try_to_f64(vm)? { - return Ok(Some((Complex64::new(float, 0.0), false))); + if let Some(float) = self.try_float_opt(vm)? { + return Ok(Some((Complex64::new(float.to_f64(), 0.0), false))); } Ok(None) } diff --git a/vm/src/builtins/float.rs b/vm/src/builtins/float.rs index 5453971cb..771292117 100644 --- a/vm/src/builtins/float.rs +++ b/vm/src/builtins/float.rs @@ -1,18 +1,20 @@ +use std::borrow::Cow; + use super::{ try_bigint_to_f64, PyByteArray, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyType, PyTypeRef, }; -use crate::common::{float_ops, hash}; use crate::{ class::PyClassImpl, - convert::ToPyObject, + common::{float_ops, hash}, + convert::{ToPyObject, ToPyResult}, format::FormatSpec, function::{ ArgBytesLike, OptionalArg, OptionalOption, PyArithmeticValue::{self, *}, PyComparisonValue, }, - identifier, - types::{Comparable, Constructor, Hashable, PyComparisonOp}, + protocol::{PyNumber, PyNumberMethods}, + types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, TryFromObject, VirtualMachine, }; @@ -58,32 +60,13 @@ impl From for PyFloat { } impl PyObject { - pub fn try_to_f64(&self, vm: &VirtualMachine) -> PyResult> { - if let Some(float) = self.payload_if_exact::(vm) { - return Ok(Some(float.value)); - } - if let Some(method) = vm.get_method(self.to_owned(), identifier!(vm, __float__)) { - let result = vm.invoke(&method?, ())?; - // TODO: returning strict subclasses of float in __float__ is deprecated - return match result.payload::() { - Some(float_obj) => Ok(Some(float_obj.value)), - None => Err(vm.new_type_error(format!( - "__float__ returned non-float (type '{}')", - result.class().name() - ))), - }; - } - if let Some(r) = vm.to_index_opt(self.to_owned()).transpose()? { - return Ok(Some(try_bigint_to_f64(r.as_bigint(), vm)?)); - } - Ok(None) + pub fn try_float_opt(&self, vm: &VirtualMachine) -> PyResult>> { + PyNumber::from(self).float_opt(vm) } -} -pub fn try_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult { - obj.try_to_f64(vm)?.ok_or_else(|| { - vm.new_type_error(format!("must be real number, not {}", obj.class().name())) - }) + pub fn try_float(&self, vm: &VirtualMachine) -> PyResult> { + PyNumber::from(self).float(vm) + } } pub(crate) fn to_op_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult> { @@ -170,17 +153,10 @@ impl Constructor for PyFloat { let float_val = match arg { OptionalArg::Missing => 0.0, OptionalArg::Present(val) => { - let val = if cls.is(vm.ctx.types.float_type) { - match val.downcast_exact::(vm) { - Ok(f) => return Ok(f.into()), - Err(val) => val, - } - } else { - val - }; - - if let Some(f) = val.try_to_f64(vm)? { - f + if cls.is(vm.ctx.types.float_type) && val.class().is(PyFloat::class(vm)) { + unsafe { val.downcast_unchecked::().value } + } else if let Some(f) = val.try_float_opt(vm)? { + f.value } else { float_from_string(val, vm)? } @@ -220,7 +196,7 @@ fn float_from_string(val: PyObjectRef, vm: &VirtualMachine) -> PyResult { }) } -#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Constructor))] +#[pyimpl(flags(BASETYPE), with(Comparable, Hashable, Constructor, AsNumber))] impl PyFloat { #[pymethod(magic)] fn format(&self, spec: PyStrRef, vm: &VirtualMachine) -> PyResult { @@ -562,6 +538,75 @@ impl Hashable for PyFloat { } } +impl AsNumber for PyFloat { + fn as_number(_zelf: &crate::Py, _vm: &VirtualMachine) -> Cow<'static, PyNumberMethods> { + Cow::Borrowed(&Self::NUMBER_METHODS) + } +} + +impl PyFloat { + fn np_general_op( + number: &PyNumber, + other: &PyObject, + op: F, + vm: &VirtualMachine, + ) -> PyResult + where + F: FnOnce(f64, f64, &VirtualMachine) -> R, + R: ToPyResult, + { + if let (Some(a), Some(b)) = (to_op_float(number.obj, vm)?, to_op_float(other, vm)?) { + op(a, b, vm).to_pyresult(vm) + } else { + Ok(vm.ctx.not_implemented()) + } + } + + fn np_float_op(number: &PyNumber, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult + where + F: FnOnce(f64, f64) -> f64, + { + Self::np_general_op(number, other, |a, b, _vm| op(a, b), vm) + } + + fn np_float(number: &PyNumber, vm: &VirtualMachine) -> PyRef { + if let Some(zelf) = number.obj.downcast_ref_if_exact::(vm) { + zelf.to_owned() + } else { + vm.ctx.new_float(Self::number_downcast(number).value) + } + } + + const NUMBER_METHODS: PyNumberMethods = PyNumberMethods { + add: Some(|number, other, vm| Self::np_float_op(number, other, |a, b| a + b, vm)), + subtract: Some(|number, other, vm| Self::np_float_op(number, other, |a, b| a - b, vm)), + multiply: Some(|number, other, vm| Self::np_float_op(number, other, |a, b| a * b, vm)), + remainder: Some(|number, other, vm| Self::np_general_op(number, other, inner_mod, vm)), + divmod: Some(|number, other, vm| Self::np_general_op(number, other, inner_divmod, vm)), + power: Some(|number, other, vm| Self::np_general_op(number, other, float_pow, vm)), + negative: Some(|number, vm| { + let value = Self::number_downcast(number).value; + (-value).to_pyresult(vm) + }), + positive: Some(|number, vm| Self::np_float(number, vm).to_pyresult(vm)), + absolute: Some(|number, vm| { + let value = Self::number_downcast(number).value; + value.abs().to_pyresult(vm) + }), + boolean: Some(|number, _vm| Ok(Self::number_downcast(number).value.is_zero())), + int: Some(|number, vm| { + let value = Self::number_downcast(number).value; + try_to_bigint(value, vm).map(|x| vm.ctx.new_int(x)) + }), + float: Some(|number, vm| Ok(Self::np_float(number, vm))), + floor_divide: Some(|number, other, vm| { + Self::np_general_op(number, other, inner_floordiv, vm) + }), + true_divide: Some(|number, other, vm| Self::np_general_op(number, other, inner_div, vm)), + ..*PyNumberMethods::not_implemented() + }; +} + // Retrieve inner float value: pub(crate) fn get_value(obj: &PyObject) -> f64 { obj.payload::().unwrap().value diff --git a/vm/src/builtins/int.rs b/vm/src/builtins/int.rs index 524b6bc8b..c4baea3a2 100644 --- a/vm/src/builtins/int.rs +++ b/vm/src/builtins/int.rs @@ -9,7 +9,8 @@ use crate::{ ArgByteOrder, ArgIntoBool, OptionalArg, OptionalOption, PyArithmeticValue, PyComparisonValue, }, - types::{Comparable, Constructor, Hashable, PyComparisonOp}, + protocol::{PyNumber, PyNumberMethods}, + types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp}, AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromBorrowedObject, VirtualMachine, }; @@ -17,8 +18,8 @@ use bstr::ByteSlice; use num_bigint::{BigInt, BigUint, Sign}; use num_integer::Integer; use num_traits::{One, Pow, PrimInt, Signed, ToPrimitive, Zero}; -use std::borrow::Cow; use std::fmt; +use std::{borrow::Cow, ops::Neg}; /// int(x=0) -> integer /// int(x, base=10) -> integer @@ -264,7 +265,6 @@ impl Constructor for PyInt { val }; - // try_int(&val, vm) PyNumber::from(val.as_ref()) .int(vm) .map(|x| x.as_bigint().clone()) @@ -762,22 +762,25 @@ impl AsNumber for PyInt { } impl PyInt { - fn number_protocol_binop( - number: &PyNumber, - other: &PyObject, - op: &str, - f: F, - vm: &VirtualMachine, - ) -> PyResult + fn np_general_op(number: &PyNumber, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult + where + F: FnOnce(&BigInt, &BigInt, &VirtualMachine) -> PyResult, + { + if let (Some(a), Some(b)) = (number.obj.payload::(), other.payload::()) { + op(&a.value, &b.value, vm) + } else { + Ok(vm.ctx.not_implemented()) + } + } + + fn np_int_op(number: &PyNumber, other: &PyObject, op: F, vm: &VirtualMachine) -> PyResult where F: FnOnce(&BigInt, &BigInt) -> BigInt, { - let (a, b) = Self::downcast_or_binop_error(number, other, op, vm)?; - let ret = f(&a.value, &b.value); - Ok(vm.ctx.new_int(ret).into()) + Self::np_general_op(number, other, |a, b, _vm| op(a, b).to_pyresult(vm), vm) } - fn number_protocol_int(number: &PyNumber, vm: &VirtualMachine) -> PyIntRef { + fn np_int(number: &PyNumber, vm: &VirtualMachine) -> PyIntRef { if let Some(zelf) = number.obj.downcast_ref_if_exact::(vm) { zelf.to_owned() } else { @@ -787,78 +790,43 @@ impl PyInt { } const NUMBER_METHODS: PyNumberMethods = PyNumberMethods { - add: Some(|number, other, vm| { - Self::number_protocol_binop(number, other, "+", |a, b| a + b, vm) - }), - subtract: Some(|number, other, vm| { - Self::number_protocol_binop(number, other, "-", |a, b| a - b, vm) - }), - multiply: Some(|number, other, vm| { - Self::number_protocol_binop(number, other, "*", |a, b| a * b, vm) - }), - remainder: Some(|number, other, vm| { - let (a, b) = Self::downcast_or_binop_error(number, other, "%", vm)?; - inner_mod(&a.value, &b.value, vm) - }), - divmod: Some(|number, other, vm| { - let (a, b) = Self::downcast_or_binop_error(number, other, "divmod()", vm)?; - inner_divmod(&a.value, &b.value, vm) - }), - power: Some(|number, other, vm| { - let (a, b) = Self::downcast_or_binop_error(number, other, "** or pow()", vm)?; - inner_pow(&a.value, &b.value, vm) - }), + add: Some(|number, other, vm| Self::np_int_op(number, other, |a, b| a + b, vm)), + subtract: Some(|number, other, vm| Self::np_int_op(number, other, |a, b| a - b, vm)), + multiply: Some(|number, other, vm| Self::np_int_op(number, other, |a, b| a * b, vm)), + remainder: Some(|number, other, vm| Self::np_general_op(number, other, inner_mod, vm)), + divmod: Some(|number, other, vm| Self::np_general_op(number, other, inner_divmod, vm)), + power: Some(|number, other, vm| Self::np_general_op(number, other, inner_pow, vm)), negative: Some(|number, vm| { - let zelf = Self::number_downcast(number); - Ok(vm.ctx.new_int(-&zelf.value).into()) - }), - positive: Some(|number, vm| Ok(Self::number_protocol_int(number, vm).into())), - absolute: Some(|number, vm| { - let zelf = Self::number_downcast(number); - Ok(vm.ctx.new_int(zelf.value.abs()).into()) - }), - boolean: Some(|number, _vm| { - let zelf = Self::number_downcast(number); - Ok(zelf.value.is_zero()) + Self::number_downcast(number) + .value + .clone() + .neg() + .to_pyresult(vm) }), + positive: Some(|number, vm| Ok(Self::np_int(number, vm).into())), + absolute: Some(|number, vm| Self::number_downcast(number).value.abs().to_pyresult(vm)), + boolean: Some(|number, _vm| Ok(Self::number_downcast(number).value.is_zero())), invert: Some(|number, vm| { - let zelf = Self::number_downcast(number); - Ok(vm.ctx.new_int(!&zelf.value).into()) + let value = Self::number_downcast(number).value.clone(); + (!value).to_pyresult(vm) }), - lshift: Some(|number, other, vm| { - let (a, b) = Self::downcast_or_binop_error(number, other, "<<", vm)?; - inner_lshift(&a.value, &b.value, vm) - }), - rshift: Some(|number, other, vm| { - let (a, b) = Self::downcast_or_binop_error(number, other, ">>", vm)?; - inner_rshift(&a.value, &b.value, vm) - }), - and: Some(|number, other, vm| { - Self::number_protocol_binop(number, other, "&", |a, b| a & b, vm) - }), - xor: Some(|number, other, vm| { - Self::number_protocol_binop(number, other, "^", |a, b| a ^ b, vm) - }), - or: Some(|number, other, vm| { - Self::number_protocol_binop(number, other, "|", |a, b| a | b, vm) - }), - int: Some(|number, other| Ok(Self::number_protocol_int(number, other))), + lshift: Some(|number, other, vm| Self::np_general_op(number, other, inner_lshift, vm)), + rshift: Some(|number, other, vm| Self::np_general_op(number, other, inner_rshift, vm)), + and: Some(|number, other, vm| Self::np_int_op(number, other, |a, b| a & b, vm)), + xor: Some(|number, other, vm| Self::np_int_op(number, other, |a, b| a ^ b, vm)), + or: Some(|number, other, vm| Self::np_int_op(number, other, |a, b| a | b, vm)), + int: Some(|number, other| Ok(Self::np_int(number, other))), float: Some(|number, vm| { - let zelf = number - .obj - .downcast_ref::() - .ok_or_else(|| vm.new_type_error("an integer is required".to_owned()))?; + let zelf = Self::number_downcast(number); try_to_float(&zelf.value, vm).map(|x| vm.ctx.new_float(x)) }), floor_divide: Some(|number, other, vm| { - let (a, b) = Self::downcast_or_binop_error(number, other, "//", vm)?; - inner_floordiv(&a.value, &b.value, vm) + Self::np_general_op(number, other, inner_floordiv, vm) }), true_divide: Some(|number, other, vm| { - let (a, b) = Self::downcast_or_binop_error(number, other, "/", vm)?; - inner_truediv(&a.value, &b.value, vm) + Self::np_general_op(number, other, inner_truediv, vm) }), - index: Some(|number, vm| Ok(Self::number_protocol_int(number, vm))), + index: Some(|number, vm| Ok(Self::np_int(number, vm))), ..*PyNumberMethods::not_implemented() }; } diff --git a/vm/src/function/number.rs b/vm/src/function/number.rs index 96ff57827..62922c85b 100644 --- a/vm/src/function/number.rs +++ b/vm/src/function/number.rs @@ -1,4 +1,4 @@ -use crate::{AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine}; +use crate::{protocol::PyNumber, AsObject, PyObjectRef, PyResult, TryFromObject, VirtualMachine}; use num_complex::Complex64; use std::ops::Deref; @@ -82,9 +82,7 @@ impl Deref for ArgIntoFloat { impl TryFromObject for ArgIntoFloat { // Equivalent to PyFloat_AsDouble. fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult { - let value = obj.try_to_f64(vm)?.ok_or_else(|| { - vm.new_type_error(format!("must be real number, not {}", obj.class().name())) - })?; + let value = PyNumber::from(obj.as_ref()).float(vm)?.to_f64(); Ok(ArgIntoFloat { value }) } } diff --git a/vm/src/protocol/number.rs b/vm/src/protocol/number.rs index 160f4cd43..68b575c55 100644 --- a/vm/src/protocol/number.rs +++ b/vm/src/protocol/number.rs @@ -125,8 +125,13 @@ impl PyNumber<'_> { let ret = f(self, vm)?; if !ret.class().is(PyInt::class(vm)) { warnings::warn( - vm.ctx.exceptions.deprecation_warning.clone(), - format!("__int__ returned non-int (type {})", ret.class()), + vm.ctx.exceptions.deprecation_warning, + format!( + "__int__ returned non-int (type {}). \ + The ability to return an instance of a strict subclass of int \ + is deprecated, and may be removed in a future version of Python.", + ret.class() + ), 1, vm, )? @@ -134,7 +139,9 @@ impl PyNumber<'_> { Ok(ret) } else if self.methods(vm).index.is_some() { self.index(vm) - } else if let Ok(Ok(f)) = vm.get_special_method(self.obj.to_owned(), "__trunc__") { + } else if let Ok(Ok(f)) = + vm.get_special_method(self.obj.to_owned(), identifier!(vm, __trunc__)) + { // TODO: Deprecate in 3.11 // warnings::warn( // vm.ctx.exceptions.deprecation_warning.clone(), @@ -173,8 +180,13 @@ impl PyNumber<'_> { let ret = f(self, vm)?; if !ret.class().is(PyInt::class(vm)) { warnings::warn( - vm.ctx.exceptions.deprecation_warning.clone(), - format!("__index__ returned non-int (type {})", ret.class()), + vm.ctx.exceptions.deprecation_warning, + format!( + "__index__ returned non-int (type {}). \ + The ability to return an instance of a strict subclass of int \ + is deprecated, and may be removed in a future version of Python.", + ret.class() + ), 1, vm, )? @@ -187,6 +199,46 @@ impl PyNumber<'_> { ))) } } + + pub fn float_opt(&self, vm: &VirtualMachine) -> PyResult>> { + if self.obj.class().is(PyFloat::class(vm)) { + Ok(Some(unsafe { + self.obj.to_owned().downcast_unchecked::() + })) + } else if let Some(f) = self.methods(vm).float { + let ret = f(self, vm)?; + if !ret.class().is(PyFloat::class(vm)) { + warnings::warn( + vm.ctx.exceptions.deprecation_warning, + format!( + "__float__ returned non-float (type {}). \ + The ability to return an instance of a strict subclass of float \ + is deprecated, and may be removed in a future version of Python.", + ret.class() + ), + 1, + vm, + )?; + Ok(Some(vm.ctx.new_float(ret.to_f64()))) + } else { + Ok(Some(ret)) + } + } else if self.methods(vm).index.is_some() { + let i = self.index(vm)?; + let value = int::try_to_float(i.as_bigint(), vm)?; + Ok(Some(vm.ctx.new_float(value))) + } else if let Some(value) = self.obj.downcast_ref::() { + Ok(Some(vm.ctx.new_float(value.to_f64()))) + } else { + Ok(None) + } + } + + pub fn float(&self, vm: &VirtualMachine) -> PyResult> { + self.float_opt(vm)?.ok_or_else(|| { + vm.new_type_error(format!("must be real number, not {}", self.obj.class())) + }) + } } const NOT_IMPLEMENTED: PyNumberMethods = PyNumberMethods { diff --git a/vm/src/stdlib/warnings.rs b/vm/src/stdlib/warnings.rs index e9b5f30ef..4bf14ef22 100644 --- a/vm/src/stdlib/warnings.rs +++ b/vm/src/stdlib/warnings.rs @@ -1,9 +1,9 @@ pub(crate) use _warnings::make_module; -use crate::{builtins::PyTypeRef, PyResult, VirtualMachine}; +use crate::{builtins::PyType, Py, PyResult, VirtualMachine}; pub fn warn( - category: PyTypeRef, + category: &Py, message: String, stack_level: usize, vm: &VirtualMachine, @@ -14,7 +14,7 @@ pub fn warn( // TODO if let Ok(module) = vm.import("warnings", None, 0) { if let Ok(func) = module.get_attr("warn", vm) { - let _ = vm.invoke(&func, (message, category, stack_level)); + let _ = vm.invoke(&func, (message, category.to_owned(), stack_level)); } } Ok(()) diff --git a/vm/src/types/slot.rs b/vm/src/types/slot.rs index 3811f0c97..0ca434398 100644 --- a/vm/src/types/slot.rs +++ b/vm/src/types/slot.rs @@ -1,6 +1,6 @@ use crate::common::{hash::PyHash, lock::PyRwLock}; use crate::{ - builtins::{PyInt, PyStrInterned, PyStrRef, PyType, PyTypeRef}, + builtins::{PyFloat, PyInt, PyStrInterned, PyStrRef, PyType, PyTypeRef}, bytecode::ComparisonOperator, convert::{ToPyObject, ToPyResult}, function::Either, @@ -340,29 +340,41 @@ fn as_sequence_generic(zelf: &PyObject, vm: &VirtualMachine) -> &'static PySeque static_as_sequence_generic(has_length, has_ass_item) } -fn as_number_wrapper(zelf: &PyObject, _vm: &VirtualMachine) -> Cow<'static, PyNumberMethods> { +fn as_number_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> Cow<'static, PyNumberMethods> { Cow::Owned(PyNumberMethods { - int: then_some_closure!(zelf.class().has_attr("__int__"), |num, vm| { - let ret = vm.call_special_method(num.obj.to_owned(), "__int__", ())?; - ret.downcast::().map_err(|obj| { - vm.new_type_error(format!("__int__ returned non-int (type {})", obj.class())) - }) - }), - float: then_some_closure!(zelf.class().has_attr("__float__"), |num, vm| { - let ret = vm.call_special_method(num.obj.to_owned(), "__float__", ())?; - ret.downcast::().map_err(|obj| { - vm.new_type_error(format!( - "__float__ returned non-float (type {})", - obj.class() - )) - }) - }), - index: then_some_closure!(zelf.class().has_attr("__index__"), |num, vm| { - let ret = vm.call_special_method(num.obj.to_owned(), "__index__", ())?; - ret.downcast::().map_err(|obj| { - vm.new_type_error(format!("__index__ returned non-int (type {})", obj.class())) - }) - }), + int: then_some_closure!( + zelf.class().has_attr(identifier!(vm, __int__)), + |num, vm| { + let ret = + vm.call_special_method(num.obj.to_owned(), identifier!(vm, __int__), ())?; + ret.downcast::().map_err(|obj| { + vm.new_type_error(format!("__int__ returned non-int (type {})", obj.class())) + }) + } + ), + float: then_some_closure!( + zelf.class().has_attr(identifier!(vm, __float__)), + |num, vm| { + let ret = + vm.call_special_method(num.obj.to_owned(), identifier!(vm, __float__), ())?; + ret.downcast::().map_err(|obj| { + vm.new_type_error(format!( + "__float__ returned non-float (type {})", + obj.class() + )) + }) + } + ), + index: then_some_closure!( + zelf.class().has_attr(identifier!(vm, __index__)), + |num, vm| { + let ret = + vm.call_special_method(num.obj.to_owned(), identifier!(vm, __index__), ())?; + ret.downcast::().map_err(|obj| { + vm.new_type_error(format!("__index__ returned non-int (type {})", obj.class())) + }) + } + ), ..*PyNumberMethods::not_implemented() }) } @@ -1035,25 +1047,7 @@ pub trait AsNumber: PyPayload { fn as_number(zelf: &Py, vm: &VirtualMachine) -> Cow<'static, PyNumberMethods>; fn number_downcast<'a>(number: &'a PyNumber) -> &'a Py { - unsafe { number.obj.downcast_unchecked_ref::() } - } - - fn downcast_or_binop_error<'a, 'b>( - a: &'a PyNumber, - b: &'b PyObject, - op: &str, - vm: &VirtualMachine, - ) -> PyResult<(&'a Self, &'b Self)> { - if let (Some(a), Some(b)) = (a.obj.payload::(), b.payload::()) { - Ok((a, b)) - } else { - Err(vm.new_type_error(format!( - "unsupported operand type(s) for {}: '{}' and '{}'", - op, - a.obj.class(), - b.class() - ))) - } + unsafe { number.obj.downcast_unchecked_ref() } } }