From fc863aaba5466e8c109fa3f8e9b2209aaeaa3e11 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Feb 2019 18:20:55 -0800 Subject: [PATCH 1/5] Add complex.__abs__ --- tests/snippets/builtin_complex.py | 3 +++ vm/src/obj/objcomplex.rs | 8 ++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/snippets/builtin_complex.py diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py new file mode 100644 index 0000000000..5ac70ff597 --- /dev/null +++ b/tests/snippets/builtin_complex.py @@ -0,0 +1,3 @@ +assert complex(3, 4).__abs__() == 5 +assert complex(3, -4).__abs__() == 5 +assert complex(1.5, 2.5).__abs__() == 2.9154759474226504 diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 353b89b7f5..00556096b6 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -13,6 +13,7 @@ pub fn init(context: &PyContext) { "Create a complex number from a real part and an optional imaginary part.\n\n\ This is equivalent to (real + imag*1j) where imag defaults to 0."; + context.set_attr(&complex_type, "__abs__", context.new_rustfunc(complex_abs)); context.set_attr(&complex_type, "__add__", context.new_rustfunc(complex_add)); context.set_attr(&complex_type, "__new__", context.new_rustfunc(complex_new)); context.set_attr( @@ -70,6 +71,13 @@ fn complex_new(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { )) } +fn complex_abs(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.complex_type()))]); + + let Complex64 { re, im } = get_value(zelf); + Ok(vm.ctx.new_float(re.hypot(im))) +} + fn complex_add(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!( vm, From d66ca54a2d05d83b1629b53921cd79a6eba0350f Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Feb 2019 18:20:58 -0800 Subject: [PATCH 2/5] Add complex.{__eq__, __neg__} --- tests/snippets/builtin_complex.py | 19 ++++++++++++++++++ vm/src/obj/objcomplex.rs | 32 +++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index 5ac70ff597..8c897fd85c 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -1,3 +1,22 @@ +# __abs__ + assert complex(3, 4).__abs__() == 5 assert complex(3, -4).__abs__() == 5 assert complex(1.5, 2.5).__abs__() == 2.9154759474226504 + +# __eq__ + +assert complex(1, -1).__eq__(complex(1, -1)) +assert complex(1, 0).__eq__(1) +assert not complex(1, 1).__eq__(1) +assert complex(1, 0).__eq__(1.0) +assert not complex(1, 1).__eq__(1.0) +assert not complex(1, 0).__eq__(1.5) +assert complex(1, 0).__eq__(True) +assert not complex(1, 2).__eq__(complex(1, 1)) +#assert complex(1, 2).__eq__('foo') == NotImplemented + +# __neg__ + +assert complex(1, -1).__neg__() == complex(-1, 1) +assert complex(0, 0).__neg__() == complex(0, 0) diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index 00556096b6..e8bb66ea75 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -3,8 +3,10 @@ use super::super::pyobject::{ }; use super::super::vm::VirtualMachine; use super::objfloat; +use super::objint; use super::objtype; use num_complex::Complex64; +use num_traits::ToPrimitive; pub fn init(context: &PyContext) { let complex_type = &context.complex_type; @@ -15,6 +17,8 @@ pub fn init(context: &PyContext) { context.set_attr(&complex_type, "__abs__", context.new_rustfunc(complex_abs)); context.set_attr(&complex_type, "__add__", context.new_rustfunc(complex_add)); + context.set_attr(&complex_type, "__eq__", context.new_rustfunc(complex_eq)); + context.set_attr(&complex_type, "__neg__", context.new_rustfunc(complex_neg)); context.set_attr(&complex_type, "__new__", context.new_rustfunc(complex_new)); context.set_attr( &complex_type, @@ -100,6 +104,34 @@ fn complex_conjugate(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { Ok(vm.ctx.new_complex(v1.conj())) } +fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!( + vm, + args, + required = [(zelf, Some(vm.ctx.complex_type())), (other, None)] + ); + + let z = get_value(zelf); + let result = if objtype::isinstance(other, &vm.ctx.complex_type()) { + z == get_value(other) + } else if objtype::isinstance(other, &vm.ctx.int_type()) { + match objint::get_value(other).to_f64() { + Some(f) => z.im == 0.0f64 && z.re == f, + None => false, + } + } else if objtype::isinstance(other, &vm.ctx.float_type()) { + z.im == 0.0 && z.re == objfloat::get_value(other) + } else { + false + }; + Ok(vm.ctx.new_bool(result)) +} + +fn complex_neg(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { + arg_check!(vm, args, required = [(zelf, Some(vm.ctx.complex_type()))]); + Ok(vm.ctx.new_complex(-get_value(zelf))) +} + fn complex_repr(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { arg_check!(vm, args, required = [(obj, Some(vm.ctx.complex_type()))]); let v = get_value(obj); From 520f71f354ff5ec1af554cc4b8fdc603f48b84c9 Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Fri, 8 Feb 2019 19:24:08 -0800 Subject: [PATCH 3/5] Add NotImplemented built-in constant --- tests/snippets/builtin_complex.py | 2 +- vm/src/builtins.rs | 3 +++ vm/src/obj/objcomplex.rs | 4 +++- vm/src/pyobject.rs | 13 +++++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index 8c897fd85c..ae88777b4e 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -14,7 +14,7 @@ assert not complex(1, 0).__eq__(1.5) assert complex(1, 0).__eq__(True) assert not complex(1, 2).__eq__(complex(1, 1)) -#assert complex(1, 2).__eq__('foo') == NotImplemented +assert complex(1, 2).__eq__('foo') == NotImplemented # __neg__ diff --git a/vm/src/builtins.rs b/vm/src/builtins.rs index f8df09a624..ff903c116e 100644 --- a/vm/src/builtins.rs +++ b/vm/src/builtins.rs @@ -685,6 +685,9 @@ pub fn make_module(ctx: &PyContext) -> PyObjectRef { ctx.set_attr(&py_mod, "type", ctx.type_type()); ctx.set_attr(&py_mod, "zip", ctx.zip_type()); + // Constants + ctx.set_attr(&py_mod, "NotImplemented", ctx.not_implemented.clone()); + // Exceptions: ctx.set_attr( &py_mod, diff --git a/vm/src/obj/objcomplex.rs b/vm/src/obj/objcomplex.rs index e8bb66ea75..77f200f7ee 100644 --- a/vm/src/obj/objcomplex.rs +++ b/vm/src/obj/objcomplex.rs @@ -112,6 +112,7 @@ fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { ); let z = get_value(zelf); + let result = if objtype::isinstance(other, &vm.ctx.complex_type()) { z == get_value(other) } else if objtype::isinstance(other, &vm.ctx.int_type()) { @@ -122,8 +123,9 @@ fn complex_eq(vm: &mut VirtualMachine, args: PyFuncArgs) -> PyResult { } else if objtype::isinstance(other, &vm.ctx.float_type()) { z.im == 0.0 && z.re == objfloat::get_value(other) } else { - false + return Ok(vm.ctx.not_implemented()); }; + Ok(vm.ctx.new_bool(result)) } diff --git a/vm/src/pyobject.rs b/vm/src/pyobject.rs index d486bfaefd..cf9d7b2317 100644 --- a/vm/src/pyobject.rs +++ b/vm/src/pyobject.rs @@ -130,6 +130,7 @@ pub struct PyContext { pub map_type: PyObjectRef, pub memoryview_type: PyObjectRef, pub none: PyObjectRef, + pub not_implemented: PyObjectRef, pub tuple_type: PyObjectRef, pub set_type: PyObjectRef, pub staticmethod_type: PyObjectRef, @@ -223,6 +224,11 @@ impl PyContext { create_type("NoneType", &type_type, &object_type, &dict_type), ); + let not_implemented = PyObject::new( + PyObjectPayload::NotImplemented, + create_type("NotImplementedType", &type_type, &object_type, &dict_type), + ); + let true_value = PyObject::new( PyObjectPayload::Integer { value: One::one() }, bool_type.clone(), @@ -258,6 +264,7 @@ impl PyContext { zip_type, dict_type, none, + not_implemented, str_type, range_type, object: object_type, @@ -423,6 +430,9 @@ impl PyContext { pub fn none(&self) -> PyObjectRef { self.none.clone() } + pub fn not_implemented(&self) -> PyObjectRef { + self.not_implemented.clone() + } pub fn object(&self) -> PyObjectRef { self.object.clone() } @@ -956,6 +966,7 @@ pub enum PyObjectPayload { dict: PyObjectRef, }, None, + NotImplemented, Class { name: String, dict: RefCell, @@ -1002,6 +1013,7 @@ impl fmt::Debug for PyObjectPayload { PyObjectPayload::Module { .. } => write!(f, "module"), PyObjectPayload::Scope { .. } => write!(f, "scope"), PyObjectPayload::None => write!(f, "None"), + PyObjectPayload::NotImplemented => write!(f, "NotImplemented"), PyObjectPayload::Class { ref name, .. } => write!(f, "class {:?}", name), PyObjectPayload::Instance { .. } => write!(f, "instance"), PyObjectPayload::RustFunction { .. } => write!(f, "rust function"), @@ -1058,6 +1070,7 @@ impl PyObject { ), PyObjectPayload::WeakRef { .. } => String::from("weakref"), PyObjectPayload::None => String::from("None"), + PyObjectPayload::NotImplemented => String::from("NotImplemented"), PyObjectPayload::Class { ref name, dict: ref _dict, From 09dc751869e1216c97132004baba42cc5872e9ec Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 9 Feb 2019 08:09:48 -0800 Subject: [PATCH 4/5] Avoid using magic methods in tests where possible --- tests/snippets/builtin_complex.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index ae88777b4e..82210cb904 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -1,22 +1,23 @@ # __abs__ -assert complex(3, 4).__abs__() == 5 -assert complex(3, -4).__abs__() == 5 -assert complex(1.5, 2.5).__abs__() == 2.9154759474226504 +assert abs(complex(3, 4)) == 5 +assert abs(complex(3, -4)) == 5 +assert abs(complex(1.5, 2.5)) == 2.9154759474226504 # __eq__ -assert complex(1, -1).__eq__(complex(1, -1)) -assert complex(1, 0).__eq__(1) -assert not complex(1, 1).__eq__(1) -assert complex(1, 0).__eq__(1.0) -assert not complex(1, 1).__eq__(1.0) -assert not complex(1, 0).__eq__(1.5) -assert complex(1, 0).__eq__(True) -assert not complex(1, 2).__eq__(complex(1, 1)) +assert complex(1, -1) == complex(1, -1) +assert complex(1, 0) == 1 +assert not complex(1, 1) == 1 +assert complex(1, 0) == 1.0 +assert not complex(1, 1) == 1.0 +assert not complex(1, 0) == 1.5 +assert bool(complex(1, 0)) +assert not complex(1, 2) == complex(1, 1) +assert complex(1, 2) != 'foo' assert complex(1, 2).__eq__('foo') == NotImplemented # __neg__ -assert complex(1, -1).__neg__() == complex(-1, 1) -assert complex(0, 0).__neg__() == complex(0, 0) +assert -complex(1, -1) == complex(-1, 1) +assert -complex(0, 0) == complex(0, 0) From d3d73bdc227a8711f1b45b85950163be7aa6200c Mon Sep 17 00:00:00 2001 From: Joey Hain Date: Sat, 9 Feb 2019 10:24:17 -0800 Subject: [PATCH 5/5] Disable broken test for now --- tests/snippets/builtin_complex.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/snippets/builtin_complex.py b/tests/snippets/builtin_complex.py index 82210cb904..ef644b4da0 100644 --- a/tests/snippets/builtin_complex.py +++ b/tests/snippets/builtin_complex.py @@ -14,7 +14,8 @@ assert not complex(1, 0) == 1.5 assert bool(complex(1, 0)) assert not complex(1, 2) == complex(1, 1) -assert complex(1, 2) != 'foo' +# Currently broken - see issue #419 +# assert complex(1, 2) != 'foo' assert complex(1, 2).__eq__('foo') == NotImplemented # __neg__