Skip to content

Commit 1f002e8

Browse files
Merge pull request RustPython#887 from youknowone/to_int
objint::to_int to call __int__
2 parents 08b716f + 388b62a commit 1f002e8

File tree

2 files changed

+51
-9
lines changed

2 files changed

+51
-9
lines changed

tests/snippets/ints.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,37 @@
7474
with assertRaises(TypeError):
7575
# check that first parameter is truly positional only
7676
int(val_options=1)
77+
78+
class A(object):
79+
def __int__(self):
80+
return 10
81+
82+
assert int(A()) == 10
83+
84+
class B(object):
85+
pass
86+
87+
b = B()
88+
b.__int__ = lambda: 20
89+
90+
with assertRaises(TypeError):
91+
assert int(b) == 20
92+
93+
class C(object):
94+
def __int__(self):
95+
return 'str'
96+
97+
with assertRaises(TypeError):
98+
int(C())
99+
100+
class I(int):
101+
def __int__(self):
102+
return 3
103+
104+
assert int(I(1)) == 3
105+
106+
class F(float):
107+
def __int__(self):
108+
return 3
109+
110+
assert int(F(1.2)) == 3

vm/src/obj/objint.rs

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
use std::fmt;
22
use std::hash::{Hash, Hasher};
33

4-
use num_bigint::{BigInt, ToBigInt};
4+
use num_bigint::BigInt;
55
use num_integer::Integer;
66
use num_traits::{Pow, Signed, ToPrimitive, Zero};
77

88
use crate::format::FormatSpec;
9-
use crate::function::OptionalArg;
9+
use crate::function::{OptionalArg, PyFuncArgs};
1010
use crate::pyobject::{
1111
IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject,
1212
TypeProtocol,
@@ -510,11 +510,8 @@ fn int_new(cls: PyClassRef, options: IntOptions, vm: &VirtualMachine) -> PyResul
510510
}
511511

512512
// Casting function:
513-
// TODO: this should just call `__int__` on the object
514513
pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<BigInt> {
515514
match_class!(obj.clone(),
516-
i @ PyInt => Ok(i.as_bigint().clone()),
517-
f @ PyFloat => Ok(f.to_f64().to_bigint().unwrap()),
518515
s @ PyString => {
519516
i32::from_str_radix(s.as_str(), base)
520517
.map(BigInt::from)
@@ -523,10 +520,21 @@ pub fn to_int(vm: &VirtualMachine, obj: &PyObjectRef, base: u32) -> PyResult<Big
523520
base, s
524521
)))
525522
},
526-
obj => Err(vm.new_type_error(format!(
527-
"int() argument must be a string or a number, not '{}'",
528-
obj.class().name
529-
)))
523+
obj => {
524+
if let Ok(f) = vm.get_method(obj.clone(), "__int__") {
525+
let int_res = vm.invoke(f, PyFuncArgs::default())?;
526+
match int_res.payload::<PyInt>() {
527+
Some(i) => Ok(i.as_bigint().clone()),
528+
None => Err(vm.new_type_error(format!(
529+
"TypeError: __int__ returned non-int (type '{}')", int_res.class().name))),
530+
}
531+
} else {
532+
Err(vm.new_type_error(format!(
533+
"int() argument must be a string or a number, not '{}'",
534+
obj.class().name
535+
)))
536+
}
537+
}
530538
)
531539
}
532540

0 commit comments

Comments
 (0)