Skip to content

Commit b96dddf

Browse files
committed
PyObject::try_int
1 parent 7a43a2e commit b96dddf

File tree

2 files changed

+65
-53
lines changed

2 files changed

+65
-53
lines changed

vm/src/builtins/int.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ impl Constructor for PyInt {
266266
val
267267
};
268268

269-
val.to_number().int(vm).map(|x| x.as_bigint().clone())
269+
val.try_int(vm).map(|x| x.as_bigint().clone())
270270
}
271271
} else if let OptionalArg::Present(_) = options.base {
272272
Err(vm.new_type_error("int() missing string argument".to_owned()))

vm/src/protocol/number.rs

Lines changed: 64 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,62 @@ impl PyObject {
4141
))
4242
})
4343
}
44+
45+
pub fn try_int(&self, vm: &VirtualMachine) -> PyResult<PyIntRef> {
46+
fn try_convert(obj: &PyObject, lit: &[u8], vm: &VirtualMachine) -> PyResult<PyIntRef> {
47+
let base = 10;
48+
match int::bytes_to_int(lit, base) {
49+
Some(i) => Ok(PyInt::from(i).into_ref(vm)),
50+
None => Err(vm.new_value_error(format!(
51+
"invalid literal for int() with base {}: {}",
52+
base,
53+
obj.repr(vm)?,
54+
))),
55+
}
56+
}
57+
58+
if let Some(i) = self.downcast_ref_if_exact::<PyInt>(vm) {
59+
Ok(i.to_owned())
60+
} else {
61+
let number = self.to_number();
62+
if let Some(i) = number.int(vm)? {
63+
Ok(i)
64+
} else if let Some(i) = self.try_index_opt(vm) {
65+
i
66+
} else if let Ok(Ok(f)) =
67+
vm.get_special_method(self.to_owned(), identifier!(vm, __trunc__))
68+
{
69+
// TODO: Deprecate in 3.11
70+
// warnings::warn(
71+
// vm.ctx.exceptions.deprecation_warning.clone(),
72+
// "The delegation of int() to __trunc__ is deprecated.".to_owned(),
73+
// 1,
74+
// vm,
75+
// )?;
76+
let ret = f.invoke((), vm)?;
77+
ret.try_index(vm).map_err(|_| {
78+
vm.new_type_error(format!(
79+
"__trunc__ returned non-Integral (type {})",
80+
ret.class()
81+
))
82+
})
83+
} else if let Some(s) = self.payload::<PyStr>() {
84+
try_convert(self, s.as_str().as_bytes(), vm)
85+
} else if let Some(bytes) = self.payload::<PyBytes>() {
86+
try_convert(self, bytes, vm)
87+
} else if let Some(bytearray) = self.payload::<PyByteArray>() {
88+
try_convert(self, &bytearray.borrow_buf(), vm)
89+
} else if let Ok(buffer) = ArgBytesLike::try_from_borrowed_object(vm, self) {
90+
// TODO: replace to PyBuffer
91+
try_convert(self, &buffer.borrow_buf(), vm)
92+
} else {
93+
Err(vm.new_type_error(format!(
94+
"int() argument must be a string, a bytes-like object or a real number, not '{}'",
95+
self.class()
96+
)))
97+
}
98+
}
99+
}
44100
}
45101

46102
#[derive(Default)]
@@ -178,24 +234,11 @@ impl PyNumber<'_> {
178234
self.methods().index.load().is_some()
179235
}
180236

181-
pub fn int(&self, vm: &VirtualMachine) -> PyResult<PyIntRef> {
182-
fn try_convert(obj: &PyObject, lit: &[u8], vm: &VirtualMachine) -> PyResult<PyIntRef> {
183-
let base = 10;
184-
match int::bytes_to_int(lit, base) {
185-
Some(i) => Ok(PyInt::from(i).into_ref(vm)),
186-
None => Err(vm.new_value_error(format!(
187-
"invalid literal for int() with base {}: {}",
188-
base,
189-
obj.repr(vm)?,
190-
))),
191-
}
192-
}
193-
194-
if let Some(i) = self.obj.downcast_ref_if_exact::<PyInt>(vm) {
195-
Ok(i.to_owned())
196-
} else if let Some(f) = self.methods().int.load() {
237+
#[inline]
238+
pub fn int(&self, vm: &VirtualMachine) -> PyResult<Option<PyIntRef>> {
239+
Ok(if let Some(f) = self.methods().int.load() {
197240
let ret = f(self, vm)?;
198-
if !ret.class().is(PyInt::class(vm)) {
241+
Some(if !ret.class().is(PyInt::class(vm)) {
199242
warnings::warn(
200243
vm.ctx.exceptions.deprecation_warning,
201244
format!(
@@ -207,44 +250,13 @@ impl PyNumber<'_> {
207250
1,
208251
vm,
209252
)?;
210-
Ok(vm.ctx.new_bigint(ret.as_bigint()))
253+
vm.ctx.new_bigint(ret.as_bigint())
211254
} else {
212-
Ok(ret)
213-
}
214-
} else if self.methods().index.load().is_some() {
215-
self.obj.try_index(vm)
216-
} else if let Ok(Ok(f)) =
217-
vm.get_special_method(self.obj.to_owned(), identifier!(vm, __trunc__))
218-
{
219-
// TODO: Deprecate in 3.11
220-
// warnings::warn(
221-
// vm.ctx.exceptions.deprecation_warning.clone(),
222-
// "The delegation of int() to __trunc__ is deprecated.".to_owned(),
223-
// 1,
224-
// vm,
225-
// )?;
226-
let ret = f.invoke((), vm)?;
227-
ret.try_index(vm).map_err(|_| {
228-
vm.new_type_error(format!(
229-
"__trunc__ returned non-Integral (type {})",
230-
ret.class()
231-
))
255+
ret
232256
})
233-
} else if let Some(s) = self.obj.payload::<PyStr>() {
234-
try_convert(self.obj, s.as_str().as_bytes(), vm)
235-
} else if let Some(bytes) = self.obj.payload::<PyBytes>() {
236-
try_convert(self.obj, bytes, vm)
237-
} else if let Some(bytearray) = self.obj.payload::<PyByteArray>() {
238-
try_convert(self.obj, &bytearray.borrow_buf(), vm)
239-
} else if let Ok(buffer) = ArgBytesLike::try_from_borrowed_object(vm, self.obj) {
240-
// TODO: replace to PyBuffer
241-
try_convert(self.obj, &buffer.borrow_buf(), vm)
242257
} else {
243-
Err(vm.new_type_error(format!(
244-
"int() argument must be a string, a bytes-like object or a real number, not '{}'",
245-
self.obj.class()
246-
)))
247-
}
258+
None
259+
})
248260
}
249261

250262
#[inline]

0 commit comments

Comments
 (0)