Fix `complex()` and `float()` to user dunder methods · RustPython/RustPython@c478c64 · GitHub
Skip to content

Commit c478c64

Browse files
committed
Fix complex() and float() to user dunder methods
This also adds a `to_op_X` method in order to make arithmetic operations work due to the changes in `try_X`
1 parent 9657454 commit c478c64

4 files changed

Lines changed: 83 additions & 41 deletions

File tree

vm/src/builtins/complex.rs

Lines changed: 33 additions & 4 deletions

vm/src/builtins/float.rs

Lines changed: 35 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ pub struct PyFloat {
2626
value: f64,
2727
}
2828

29+
impl<'a> BorrowValue<'a> for PyFloat {
30+
type Borrowed = &'a f64;
31+
32+
fn borrow_value(&'a self) -> Self::Borrowed {
33+
&self.value
34+
}
35+
}
36+
2937
impl PyFloat {
3038
pub fn to_f64(self) -> f64 {
3139
self.value
@@ -55,11 +63,32 @@ impl From<f64> for PyFloat {
5563
}
5664
}
5765

58-
pub fn try_float(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<f64>> {
66+
pub(crate) fn try_float(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<f64>> {
67+
if let Some(float) = obj.payload_if_exact::<PyFloat>(vm) {
68+
return Ok(Some(float.borrow_value().clone()));
69+
}
70+
if let Some(method) = vm.get_method(obj.clone(), "__float__") {
71+
let result = vm.invoke(&method?, ())?;
72+
// TODO: returning strict subclasses of float in __float__ is deprecated
73+
return match result.payload::<PyFloat>() {
74+
Some(float_obj) => Ok(Some(float_obj.borrow_value().clone())),
75+
None => Err(vm.new_type_error(format!(
76+
"__float__ returned non-float (type '{}')",
77+
result.class().name
78+
))),
79+
};
80+
}
81+
if let Some(r) = vm.to_index_opt(obj.clone()).transpose()? {
82+
return Ok(Some(int::to_float(r.borrow_value(), vm)?));
83+
}
84+
Ok(None)
85+
}
86+
87+
pub(crate) fn to_op_float(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<Option<f64>> {
5988
let v = if let Some(float) = obj.payload_if_subclass::<PyFloat>(vm) {
6089
Some(float.value)
6190
} else if let Some(int) = obj.payload_if_subclass::<PyInt>(vm) {
62-
Some(int::try_float(int.borrow_value(), vm)?)
91+
Some(int::to_float(int.borrow_value(), vm)?)
6392
} else {
6493
None
6594
};
@@ -143,7 +172,7 @@ impl PyFloat {
143172
) -> PyResult<PyRef<Self>> {
144173
let float_val = match arg {
145174
OptionalArg::Present(val) => {
146-
if let Some(f) = to_float(vm, &val)? {
175+
if let Some(f) = try_float(&val, vm)? {
147176
f
148177
} else if let Some(s) = val.payload_if_subclass::<PyStr>(vm) {
149178
float_ops::parse_str(s.borrow_value().trim()).ok_or_else(|| {
@@ -193,7 +222,7 @@ impl PyFloat {
193222
where
194223
F: Fn(f64, f64) -> PyResult<f64>,
195224
{
196-
try_float(&other, vm)?.map_or_else(
225+
to_op_float(&other, vm)?.map_or_else(
197226
|| Ok(NotImplemented),
198227
|other| Ok(Implemented(op(self.value, other)?)),
199228
)
@@ -204,7 +233,7 @@ impl PyFloat {
204233
where
205234
F: Fn(f64, f64) -> PyResult,
206235
{
207-
try_float(&other, vm)?.map_or_else(
236+
to_op_float(&other, vm)?.map_or_else(
208237
|| Ok(vm.ctx.not_implemented()),
209238
|other| op(self.value, other),
210239
)
@@ -511,22 +540,6 @@ impl Hashable for PyFloat {
511540
}
512541
}
513542

514-
fn to_float(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<Option<f64>> {
515-
let value = if let Some(float) = obj.payload_if_subclass::<PyFloat>(vm) {
516-
float.value
517-
} else if let Some(int) = obj.payload_if_subclass::<PyInt>(vm) {
518-
int::try_float(int.borrow_value(), vm)?
519-
} else {
520-
let method = match vm.get_method(obj.clone(), "__float__") {
521-
Some(x) => x?,
522-
None => return Ok(None),
523-
};
524-
let result = vm.invoke(&method, ())?;
525-
PyFloatRef::try_from_object(vm, result)?.to_f64()
526-
};
527-
Ok(Some(value))
528-
}
529-
530543
pub type PyFloatRef = PyRef<PyFloat>;
531544

532545
// Retrieve inner float value:
@@ -556,7 +569,7 @@ impl IntoPyFloat {
556569

557570
impl TryFromObject for IntoPyFloat {
558571
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
559-
let value = to_float(vm, &obj)?.ok_or_else(|| {
572+
let value = try_float(&obj, vm)?.ok_or_else(|| {
560573
vm.new_type_error(format!("must be real number, not {}", obj.class().name))
561574
})?;
562575
Ok(IntoPyFloat { value })

vm/src/builtins/int.rs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ impl_try_from_object_int!(
130130

131131
fn inner_pow(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult {
132132
if int2.is_negative() {
133-
let v1 = try_float(int1, vm)?;
134-
let v2 = try_float(int2, vm)?;
133+
let v1 = to_float(int1, vm)?;
134+
let v2 = to_float(int2, vm)?;
135135
float::float_pow(v1, v2, vm).into_pyresult(vm)
136136
} else {
137137
Ok(if let Some(v2) = int2.to_u64() {
@@ -260,9 +260,9 @@ impl PyInt {
260260
.ok_or_else(|| {
261261
vm.new_value_error("int() base must be >= 2 and <= 36, or 0".to_owned())
262262
})?;
263-
to_int_radix(vm, &val, base)
263+
try_int_radix(&val, base, vm)
264264
} else {
265-
to_int(vm, &val)
265+
try_int(&val, vm)
266266
}
267267
} else if let OptionalArg::Present(_) = options.base {
268268
Err(vm.new_type_error("int() missing string argument".to_owned()))
@@ -476,7 +476,7 @@ impl PyInt {
476476

477477
#[pymethod(name = "__float__")]
478478
fn float(&self, vm: &VirtualMachine) -> PyResult<f64> {
479-
try_float(&self.value, vm)
479+
to_float(&self.value, vm)
480480
}
481481

482482
#[pymethod(name = "__trunc__")]
@@ -710,8 +710,8 @@ struct IntToByteArgs {
710710
}
711711

712712
// Casting function:
713-
pub(crate) fn to_int(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<BigInt> {
714-
fn try_convert(lit: &[u8], vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<BigInt> {
713+
pub(crate) fn try_int(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<BigInt> {
714+
fn try_convert(obj: &PyObjectRef, lit: &[u8], vm: &VirtualMachine) -> PyResult<BigInt> {
715715
let base = 10;
716716
match bytes_to_int(lit, base) {
717717
Some(i) => Ok(i),
@@ -725,9 +725,9 @@ pub(crate) fn to_int(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<BigInt>
725725

726726
// test for strings and bytes
727727
if let Some(s) = obj.downcast_ref::<PyStr>() {
728-
return try_convert(s.borrow_value().as_bytes(), vm, obj);
728+
return try_convert(obj, s.borrow_value().as_bytes(), vm);
729729
}
730-
if let Ok(r) = try_bytes_like(vm, &obj, |x| try_convert(x, vm, obj)) {
730+
if let Ok(r) = try_bytes_like(vm, &obj, |x| try_convert(obj, x, vm)) {
731731
return r;
732732
}
733733
// strict `int` check
@@ -747,8 +747,8 @@ pub(crate) fn to_int(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<BigInt>
747747
};
748748
}
749749
// TODO: returning strict subclasses of int in __index__ is deprecated
750-
if let Some(r) = vm.to_index_opt(obj.clone()) {
751-
return r.map(|int_obj| int_obj.borrow_value().clone())
750+
if let Some(r) = vm.to_index_opt(obj.clone()).transpose()? {
751+
return Ok(r.borrow_value().clone());
752752
}
753753
if let Some(method) = vm.get_method(obj.clone(), "__trunc__") {
754754
let result = vm.invoke(&method?, ())?;
@@ -766,7 +766,7 @@ pub(crate) fn to_int(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult<BigInt>
766766
)))
767767
}
768768

769-
fn to_int_radix(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<BigInt> {
769+
fn try_int_radix(obj: &PyObjectRef, base: u32, vm: &VirtualMachine) -> PyResult<BigInt> {
770770
debug_assert!(base == 0 || (2..=36).contains(&base));
771771

772772
let opt = match_class!(match obj.clone() {
@@ -909,7 +909,7 @@ pub fn get_value(obj: &PyObjectRef) -> &BigInt {
909909
&obj.payload::<PyInt>().unwrap().value
910910
}
911911

912-
pub fn try_float(int: &BigInt, vm: &VirtualMachine) -> PyResult<f64> {
912+
pub fn to_float(int: &BigInt, vm: &VirtualMachine) -> PyResult<f64> {
913913
int.to_f64()
914914
.ok_or_else(|| vm.new_overflow_error("int too large to convert to float".to_owned()))
915915
}

vm/src/stdlib/math.rs

Lines changed: 2 additions & 2 deletions

0 commit comments

Comments
 (0)