diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index be218da341..cc2aff0f57 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -1283,8 +1283,6 @@ class SimpleNamespaceTests(unittest.TestCase): del ns1.spam self.assertEqual(vars(ns1), {}) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_repr(self): ns1 = types.SimpleNamespace(x=1, y=2, w=3) ns2 = types.SimpleNamespace() @@ -1295,8 +1293,6 @@ class SimpleNamespaceTests(unittest.TestCase): self.assertEqual(repr(ns1), "{name}(x=1, y=2, w=3)".format(name=name)) self.assertEqual(repr(ns2), "{name}(x='spam', _y=5)".format(name=name)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_equal(self): ns1 = types.SimpleNamespace(x=1) ns2 = types.SimpleNamespace() @@ -1335,8 +1331,6 @@ class SimpleNamespaceTests(unittest.TestCase): self.assertEqual(ns3.spam, ns2) self.assertEqual(ns2.spam.spam, ns2) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_recursive_repr(self): ns1 = types.SimpleNamespace(c='cookie') ns2 = types.SimpleNamespace() diff --git a/vm/src/builtins/namespace.rs b/vm/src/builtins/namespace.rs index 36edb1ef6c..910bc354fd 100644 --- a/vm/src/builtins/namespace.rs +++ b/vm/src/builtins/namespace.rs @@ -1,8 +1,11 @@ use super::PyTypeRef; use crate::{ - function::{IntoPyObject, KwArgs}, - types::Constructor, - PyClassImpl, PyContext, PyRef, PyResult, PyValue, VirtualMachine, + builtins::PyDict, + function::FuncArgs, + types::{Comparable, Constructor, PyComparisonOp}, + vm::ReprGuard, + IdProtocol, PyClassImpl, PyComparisonValue, PyContext, PyObjectRef, PyRef, PyResult, PyValue, + TypeProtocol, VirtualMachine, }; /// A simple attribute-based namespace. @@ -19,14 +22,10 @@ impl PyValue for PyNamespace { } impl Constructor for PyNamespace { - type Args = KwArgs; + type Args = FuncArgs; - fn py_new(cls: PyTypeRef, kwargs: Self::Args, vm: &VirtualMachine) -> PyResult { - let zelf = PyNamespace.into_ref_with_type(vm, cls)?; - for (name, value) in kwargs.into_iter() { - zelf.as_object().set_attr(name, value, vm)?; - } - Ok(zelf.into_pyobject(vm)) + fn py_new(cls: PyTypeRef, _args: Self::Args, vm: &VirtualMachine) -> PyResult { + PyNamespace {}.into_pyresult_with_type(vm, cls) } } @@ -36,8 +35,63 @@ impl PyNamespace { } } -#[pyimpl(flags(BASETYPE, HAS_DICT), with(Constructor))] -impl PyNamespace {} +#[pyimpl(flags(BASETYPE, HAS_DICT), with(Constructor, Comparable))] +impl PyNamespace { + #[pymethod(magic)] + fn init(zelf: PyRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult<()> { + if !args.args.is_empty() { + return Err(vm.new_type_error("no positional arguments expected".to_owned())); + } + for (name, value) in args.kwargs.into_iter() { + zelf.as_object().set_attr(name, value, vm)?; + } + Ok(()) + } + + #[pymethod(magic)] + fn repr(zelf: PyRef, vm: &VirtualMachine) -> PyResult { + let o = zelf.as_object(); + let name = if o.class().is(&vm.ctx.types.namespace_type) { + "namespace".to_owned() + } else { + o.class().slot_name() + }; + + let repr = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) { + let parts = if let Some(dict) = zelf.as_object().dict() { + let mut parts = Vec::with_capacity(dict.len()); + for (key, value) in dict { + let k = vm.to_repr(&key)?; + let key_str = k.as_str(); + let value_repr = vm.to_repr(&value)?; + parts.push(format!("{}={}", &key_str[1..key_str.len() - 1], value_repr)); + } + parts + } else { + vec![] + }; + format!("{}({})", name, parts.join(", ")) + } else { + format!("{}(...)", name) + }; + Ok(repr) + } +} + +impl Comparable for PyNamespace { + fn cmp( + zelf: &PyRef, + other: &PyObjectRef, + op: PyComparisonOp, + vm: &VirtualMachine, + ) -> PyResult { + let other = class_or_notimplemented!(Self, other); + match (zelf.as_object().dict(), other.as_object().dict()) { + (Some(d1), Some(d2)) => PyDict::cmp(&d1, d2.as_object(), op, vm), + _ => Ok(PyComparisonValue::NotImplemented), + } + } +} pub fn init(context: &PyContext) { PyNamespace::extend_class(context, &context.types.namespace_type);