impl number protocol float

This commit is contained in:
Kangzhi Shi authored and Jeong Yunwon committed 2022-05-30 07:36:08 +09:00
1 parent 59cedd2213
commit 3e6e348a67
10 files changed
+236 -181

No files matched your search

-2
View File
@@ -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):
+4 -4
View File
@@ -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());
}
}
+4 -4
View File
@@ -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::<E>(data);
Ok(())
}
@@ -539,7 +539,7 @@ make_pack_float!(f64);
impl Packable for f16 {
fn pack<E: ByteOrder>(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()));
+2 -2
View File
@@ -65,8 +65,8 @@ impl PyObjectRef {
if let Some(complex) = self.payload_if_subclass::<PyComplex>(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)
}
+85 -40
View File
@@ -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<f64> for PyFloat {
}
impl PyObject {
pub fn try_to_f64(&self, vm: &VirtualMachine) -> PyResult<Option<f64>> {
if let Some(float) = self.payload_if_exact::<PyFloat>(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::<PyFloat>() {
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<Option<PyRef<PyFloat>>> {
PyNumber::from(self).float_opt(vm)
}
}
pub fn try_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult<f64> {
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<PyRef<PyFloat>> {
PyNumber::from(self).float(vm)
}
}
pub(crate) fn to_op_float(obj: &PyObject, vm: &VirtualMachine) -> PyResult<Option<f64>> {
@@ -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::<PyFloat>(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::<PyFloat>().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<f64> {
})
}
#[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<String> {
@@ -562,6 +538,75 @@ impl Hashable for PyFloat {
}
}
impl AsNumber for PyFloat {
fn as_number(_zelf: &crate::Py<Self>, _vm: &VirtualMachine) -> Cow<'static, PyNumberMethods> {
Cow::Borrowed(&Self::NUMBER_METHODS)
}
}
impl PyFloat {
fn np_general_op<F, R>(
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<F>(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<PyFloat> {
if let Some(zelf) = number.obj.downcast_ref_if_exact::<Self>(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::<PyFloat>().unwrap().value
+43 -75
View File
@@ -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<F>(
number: &PyNumber,
other: &PyObject,
op: &str,
f: F,
vm: &VirtualMachine,
) -> PyResult
fn np_general_op<F>(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::<Self>(), other.payload::<Self>()) {
op(&a.value, &b.value, vm)
} else {
Ok(vm.ctx.not_implemented())
}
}
fn np_int_op<F>(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::<Self>(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::<Self>()
.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()
};
}
+2 -4
View File
@@ -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<Self> {
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 })
}
}
+57 -5
View File
@@ -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<Option<PyRef<PyFloat>>> {
if self.obj.class().is(PyFloat::class(vm)) {
Ok(Some(unsafe {
self.obj.to_owned().downcast_unchecked::<PyFloat>()
}))
} 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::<PyFloat>() {
Ok(Some(vm.ctx.new_float(value.to_f64())))
} else {
Ok(None)
}
}
pub fn float(&self, vm: &VirtualMachine) -> PyResult<PyRef<PyFloat>> {
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 {
+3 -3
View File
@@ -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<PyType>,
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(())
+36 -42
View File
@@ -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::<PyInt>().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::<PyFloat>().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::<PyInt>().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::<PyInt>().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::<PyFloat>().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::<PyInt>().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<Self>, vm: &VirtualMachine) -> Cow<'static, PyNumberMethods>;
fn number_downcast<'a>(number: &'a PyNumber) -> &'a Py<Self> {
unsafe { number.obj.downcast_unchecked_ref::<Self>() }
}
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::<Self>(), b.payload::<Self>()) {
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() }
}
}