From 4dc31e21ec39510b49a86ba568263f8f0236ded6 Mon Sep 17 00:00:00 2001 From: JimJeon Date: Sat, 29 Jun 2019 16:15:37 +0900 Subject: [PATCH 1/6] Fix tests for float.__round__ Added type checkings for float.__round__ tests and fixed tests when ndigits is zero. --- tests/snippets/floats.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index 101d58cc9..9af25565b 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -151,12 +151,18 @@ assert float(1.2) == 1.2 assert math.trunc(1.2) == 1 assert_raises(OverflowError, float('inf').__trunc__) assert_raises(ValueError, float('nan').__trunc__) -assert 0.5.__round__() == 0.0 -assert 1.5.__round__() == 2.0 +assert isinstance(0.5.__round__(), int) +assert isinstance(1.5.__round__(), int) +assert 0.5.__round__() == 0 +assert 1.5.__round__() == 2 +assert isinstance(0.5.__round__(0), float) +assert isinstance(1.5.__round__(0), float) assert 0.5.__round__(0) == 0.0 assert 1.5.__round__(0) == 2.0 -assert 0.5.__round__(None) == 0.0 -assert 1.5.__round__(None) == 2.0 +assert isinstance(0.5.__round__(None), int) +assert isinstance(1.5.__round__(None), int) +assert 0.5.__round__(None) == 0 +assert 1.5.__round__(None) == 2 assert_raises(OverflowError, float('inf').__round__) assert_raises(ValueError, float('nan').__round__) From be887526c1616657d6844c8ba0beecec806f948f Mon Sep 17 00:00:00 2001 From: JimJeon Date: Sat, 29 Jun 2019 17:34:28 +0900 Subject: [PATCH 2/6] Fix implements of `round` - Fix implements when ndigits argument of builtin_round is None - Fix implements when ndigits argument of float_round is 0 --- tests/snippets/builtin_round.py | 4 ++++ vm/src/builtins.rs | 16 +++++++++++++--- vm/src/obj/objfloat.rs | 26 ++++++++++++++++++-------- 3 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 tests/snippets/builtin_round.py diff --git a/tests/snippets/builtin_round.py b/tests/snippets/builtin_round.py new file mode 100644 index 000000000..8abb3d88f --- /dev/null +++ b/tests/snippets/builtin_round.py @@ -0,0 +1,4 @@ +assert round(0.0, 0) == 0.0 +assert round(0, 0) == 0 +with assertRaises(TypeError): + round(0, 0.0) \ No newline at end of file diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index 55f85baaa..1d1d8d73b 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -733,9 +733,19 @@ fn builtin_round(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult { optional = [(ndigits, None)] ); if let Some(ndigits) = ndigits { - let ndigits = vm.call_method(ndigits, "__int__", vec![])?; - let rounded = vm.call_method(number, "__round__", vec![ndigits])?; - Ok(rounded) + if objtype::isinstance(ndigits, &vm.ctx.int_type()) { + let ndigits = vm.call_method(ndigits, "__int__", vec![])?; + let rounded = vm.call_method(number, "__round__", vec![ndigits])?; + Ok(rounded) + } else if vm.ctx.none().is(ndigits) { + let rounded = &vm.call_method(number, "__round__", vec![])?; + Ok(vm.ctx.new_int(objint::get_value(rounded).clone())) + } else { + Err(vm.new_type_error(format!( + "'{}' object cannot be interpreted as an integer", + ndigits.class().name + ))) + } } else { // without a parameter, the result type is coerced to int let rounded = &vm.call_method(number, "__round__", vec![])?; diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index cfac52aa9..e0b0cfbd6 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -448,17 +448,29 @@ impl PyFloat { value.class().name ))); }; - if ndigits.is_zero() { - None - } else { - Some(ndigits) - } + Some(ndigits) } else { None } } }; - if ndigits.is_none() { + if let Some(ndigits) = ndigits { + if ndigits.is_zero() { + let fract = self.value.fract(); + let value = if (fract.abs() - 0.5).abs() < std::f64::EPSILON { + if self.value.trunc() % 2.0 == 0.0 { + self.value - fract + } else { + self.value + fract + } + } else { + self.value.round() + }; + Ok(vm.ctx.new_float(value)) + } else { + Ok(vm.ctx.not_implemented()) + } + } else { let fract = self.value.fract(); let value = if (fract.abs() - 0.5).abs() < std::f64::EPSILON { if self.value.trunc() % 2.0 == 0.0 { @@ -471,8 +483,6 @@ impl PyFloat { }; let int = try_to_bigint(value, vm)?; Ok(vm.ctx.new_int(int)) - } else { - Ok(vm.ctx.not_implemented()) } } From 9c4b08c7cd021b9887f4d80d334c48087848eb2e Mon Sep 17 00:00:00 2001 From: JimJeon Date: Thu, 15 Aug 2019 16:16:06 +0900 Subject: [PATCH 3/6] Add tests for round functions --- tests/snippets/builtin_round.py | 21 +++++++++++++++++++-- tests/snippets/floats.py | 2 ++ tests/snippets/ints.py | 13 +++++++++++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/tests/snippets/builtin_round.py b/tests/snippets/builtin_round.py index 8abb3d88f..6d116b307 100644 --- a/tests/snippets/builtin_round.py +++ b/tests/snippets/builtin_round.py @@ -1,4 +1,21 @@ -assert round(0.0, 0) == 0.0 +from testutils import assertRaises + +assert round(0) == 0 +assert isinstance(round(0), int) +assert round(0.0) == 0 +assert isinstance(round(0.0), int) + +assert round(0, None) == 0 +assert isinstance(round(0, None), int) +assert round(0.0, None) == 0 +assert isinstance(round(0, None), int) + assert round(0, 0) == 0 +assert isinstance(round(0, 0), int) +assert round(0.0, 0) == 0.0 # Cannot check the type +assert isinstance(round(0.0, 0), float) + with assertRaises(TypeError): - round(0, 0.0) \ No newline at end of file + round(0, 0.0) +with assertRaises(TypeError): + round(0.0, 0.0) diff --git a/tests/snippets/floats.py b/tests/snippets/floats.py index 9af25565b..886ad7d2e 100644 --- a/tests/snippets/floats.py +++ b/tests/snippets/floats.py @@ -163,6 +163,8 @@ assert isinstance(0.5.__round__(None), int) assert isinstance(1.5.__round__(None), int) assert 0.5.__round__(None) == 0 assert 1.5.__round__(None) == 2 +assert_raises(TypeError, lambda: 0.5.__round__(0.0)) +assert_raises(TypeError, lambda: 1.5.__round__(0.0)) assert_raises(OverflowError, float('inf').__round__) assert_raises(ValueError, float('nan').__round__) diff --git a/tests/snippets/ints.py b/tests/snippets/ints.py index 40061e7e5..56642c0a8 100644 --- a/tests/snippets/ints.py +++ b/tests/snippets/ints.py @@ -139,3 +139,16 @@ class F(float): return 3 assert int(F(1.2)) == 3 + +assert isinstance((0).__round__(), int) +assert isinstance((1).__round__(), int) +assert (0).__round__() == 0 +assert (1).__round__() == 1 +assert isinstance((0).__round__(0), int) +assert isinstance((1).__round__(0), int) +assert (0).__round__(0) == 0 +assert (1).__round__(0) == 1 +assert_raises(TypeError, lambda: (0).__round__(None)) +assert_raises(TypeError, lambda: (1).__round__(None)) +assert_raises(TypeError, lambda: (0).__round__(0.0)) +assert_raises(TypeError, lambda: (1).__round__(0.0)) \ No newline at end of file From befb9a239e0967464698c73c7d0f6006e43dcac9 Mon Sep 17 00:00:00 2001 From: JimJeon Date: Thu, 15 Aug 2019 16:16:40 +0900 Subject: [PATCH 4/6] Add round feature for objfloat when ndigits is 0 --- vm/src/obj/objfloat.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/vm/src/obj/objfloat.rs b/vm/src/obj/objfloat.rs index e0b0cfbd6..b89633b58 100644 --- a/vm/src/obj/objfloat.rs +++ b/vm/src/obj/objfloat.rs @@ -440,20 +440,21 @@ impl PyFloat { OptionalArg::Missing => None, OptionalArg::Present(ref value) => { if !vm.get_none().is(value) { - let ndigits = if objtype::isinstance(value, &vm.ctx.int_type()) { - objint::get_value(value) - } else { + if !objtype::isinstance(value, &vm.ctx.int_type()) { return Err(vm.new_type_error(format!( - "TypeError: '{}' object cannot be interpreted as an integer", + "'{}' object cannot be interpreted as an integer", value.class().name ))); }; + // Only accept int type ndigits + let ndigits = objint::get_value(value); Some(ndigits) } else { None } } }; + if let Some(ndigits) = ndigits { if ndigits.is_zero() { let fract = self.value.fract(); From a2e3e6b31e657379259b18da05da81c63c33fe97 Mon Sep 17 00:00:00 2001 From: JimJeon Date: Thu, 15 Aug 2019 16:19:16 +0900 Subject: [PATCH 5/6] Add round feature for objint --- vm/src/obj/objint.rs | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 88e0233b2..cfa5ba22b 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -9,12 +9,13 @@ use crate::function::{KwArgs, OptionalArg, PyFuncArgs}; use crate::pyhash; use crate::pyobject::{ IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, - TypeProtocol, + TypeProtocol, IdProtocol, }; use crate::vm::VirtualMachine; use super::objbyteinner::PyByteInner; use super::objbytes::PyBytes; +use super::objint; use super::objstr::{PyString, PyStringRef}; use super::objtype; use crate::obj::objtype::PyClassRef; @@ -434,8 +435,29 @@ impl PyInt { zelf: PyRef, _precision: OptionalArg, _vm: &VirtualMachine, - ) -> PyIntRef { - zelf + ) -> PyResult { + let _ndigits = match _precision { + OptionalArg::Missing => None, + OptionalArg::Present(ref value) => { + if !_vm.get_none().is(value) { + if !objtype::isinstance(value, &_vm.ctx.int_type()) { + return Err(_vm.new_type_error(format!( + "'{}' object cannot be interpreted as an integer", + value.class().name + ))); + }; + // Only accept int type _ndigits + let _ndigits = objint::get_value(value); + Some(_ndigits) + } else { + return Err(_vm.new_type_error(format!( + "'{}' object cannot be interpreted as an integer", + value.class().name + ))); + } + } + }; + Ok(zelf) } #[pymethod(name = "__int__")] From b8b83cc4414dd6ba62a47c99e8c4b63165543168 Mon Sep 17 00:00:00 2001 From: JimJeon Date: Thu, 15 Aug 2019 16:47:37 +0900 Subject: [PATCH 6/6] Apply cargo-fmt --- vm/src/obj/objint.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index cfa5ba22b..b0c671d4d 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -8,8 +8,8 @@ use crate::format::FormatSpec; use crate::function::{KwArgs, OptionalArg, PyFuncArgs}; use crate::pyhash; use crate::pyobject::{ - IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, TryFromObject, - TypeProtocol, IdProtocol, + IdProtocol, IntoPyObject, PyClassImpl, PyContext, PyObjectRef, PyRef, PyResult, PyValue, + TryFromObject, TypeProtocol, }; use crate::vm::VirtualMachine;