mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
impl number protocol float
This commit is contained in:
1 parent
59cedd2213
commit
3e6e348a67
10 files changed
+236
-181
No files matched your search
Vendored
-2
@@ -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
@@ -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
@@ -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()));
|
||||
|
||||
@@ -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
@@ -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
@@ -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()
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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 })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
@@ -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() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user