From 4be3a365ce2156ec5598392629b026b5fe793319 Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Fri, 28 Feb 2020 03:39:49 +0900 Subject: [PATCH] non-int indexable base for int() --- Lib/test/test_int.py | 1 - vm/src/obj/objint.rs | 15 +++++++++++---- vm/src/obj/objslice.rs | 24 ++++++------------------ vm/src/vm.rs | 26 +++++++++++++++++++++++++- 4 files changed, 42 insertions(+), 24 deletions(-) diff --git a/Lib/test/test_int.py b/Lib/test/test_int.py index 29dfe9920..7cf1ab69c 100644 --- a/Lib/test/test_int.py +++ b/Lib/test/test_int.py @@ -280,7 +280,6 @@ class IntTestCases(unittest.TestCase): with self.assertRaises(TypeError): int('0', 5.0) - @unittest.skip("TODO: RUSTPYTHON") def test_int_base_indexable(self): class MyIndexable(object): def __init__(self, value): diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index d3fa7702c..095cdb84b 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -659,12 +659,13 @@ struct IntOptions { #[pyarg(positional_only, optional = true)] val_options: OptionalArg, #[pyarg(positional_or_keyword, optional = true)] - base: OptionalArg, + base: OptionalArg, } impl IntOptions { fn get_int_value(self, vm: &VirtualMachine) -> PyResult { if let OptionalArg::Present(val) = self.val_options { + // FIXME: unnessessary bigint clone/creation let base = if let OptionalArg::Present(base) = self.base { if !(objtype::isinstance(&val, &vm.ctx.str_type()) || objtype::isinstance(&val, &vm.ctx.bytes_type())) @@ -673,11 +674,17 @@ impl IntOptions { "int() can't convert non-string with explicit base".to_owned(), )); } - base + let base = vm.to_index(&base).unwrap_or_else(|| { + Err(vm.new_type_error(format!( + "'{}' object cannot be interpreted as an integer missing string argument", + base.class().name + ))) + })?; + base.as_bigint().clone() } else { - PyInt::new(10).into_ref(vm) + BigInt::from(10) }; - to_int(vm, &val, base.as_bigint()) + to_int(vm, &val, &base) } else if let OptionalArg::Present(_) = self.base { Err(vm.new_type_error("int() missing string argument".to_owned())) } else { diff --git a/vm/src/obj/objslice.rs b/vm/src/obj/objslice.rs index 3c21ea789..ff11cc17d 100644 --- a/vm/src/obj/objslice.rs +++ b/vm/src/obj/objslice.rs @@ -3,7 +3,6 @@ use super::objtype::PyClassRef; use crate::function::{OptionalArg, PyFuncArgs}; use crate::pyobject::{ IdProtocol, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryIntoRef, - TypeProtocol, }; use crate::vm::VirtualMachine; use num_bigint::{BigInt, ToBigInt}; @@ -323,23 +322,12 @@ fn to_index_value(vm: &VirtualMachine, obj: &PyObjectRef) -> PyResult() { - Ok(Some(val.as_bigint().clone())) - } else { - let cls = obj.class(); - if cls.has_attr("__index__") { - let index_result = vm.call_method(obj, "__index__", vec![])?; - if let Some(val) = index_result.payload::() { - Ok(Some(val.as_bigint().clone())) - } else { - Err(vm.new_type_error("__index__ method returned non integer".to_owned())) - } - } else { - Err(vm.new_type_error( - "slice indices must be integers or None or have an __index__ method".to_owned(), - )) - } - } + let result = vm.to_index(obj).unwrap_or_else(|| { + Err(vm.new_type_error( + "slice indices must be integers or None or have an __index__ method".to_owned(), + )) + })?; + Ok(Some(result.as_bigint().clone())) } pub fn init(context: &PyContext) { diff --git a/vm/src/vm.rs b/vm/src/vm.rs index 6d3493bf3..a39ce7a81 100644 --- a/vm/src/vm.rs +++ b/vm/src/vm.rs @@ -29,7 +29,7 @@ use crate::import; use crate::obj::objbool; use crate::obj::objcode::{PyCode, PyCodeRef}; use crate::obj::objdict::PyDictRef; -use crate::obj::objint::PyInt; +use crate::obj::objint::{PyInt, PyIntRef}; use crate::obj::objiter; use crate::obj::objlist::PyList; use crate::obj::objmodule::{self, PyModule}; @@ -546,6 +546,30 @@ impl VirtualMachine { Ok(self.new_str(ascii)) } + pub fn to_index(&self, obj: &PyObjectRef) -> Option> { + Some( + if let Ok(val) = TryFromObject::try_from_object(self, obj.clone()) { + Ok(val) + } else { + let cls = obj.class(); + if cls.has_attr("__index__") { + self.call_method(obj, "__index__", vec![]).and_then(|r| { + if let Ok(val) = TryFromObject::try_from_object(self, r) { + Ok(val) + } else { + Err(self.new_type_error(format!( + "__index__ returned non-int (type {})", + cls.name + ))) + } + }) + } else { + return None; + } + }, + ) + } + pub fn import(&self, module: &str, from_list: &[String], level: usize) -> PyResult { // if the import inputs seem weird, e.g a package import or something, rather than just // a straight `import ident`