From 79f223a477a5cdd256b2017296092c97a7e21c0b Mon Sep 17 00:00:00 2001 From: Ryan Porter Date: Sat, 5 Jun 2021 17:10:50 -0700 Subject: [PATCH] Implement MDGModifier::renameAttribute binding --- src/MDGModifier.inl | 34 ++++++++++++++++++++++-- tests/test_MDGModifier.py | 56 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/src/MDGModifier.inl b/src/MDGModifier.inl index 9593c83..090e0cc 100644 --- a/src/MDGModifier.inl +++ b/src/MDGModifier.inl @@ -472,8 +472,38 @@ There should be no function sets attached to the attribute at the time of the ca throw std::logic_error{"Function not yet implemented."}; }, R"pbdoc(Adds an operation to the modifier to remove an element of a multi (array) plug.)pbdoc") - .def("renameAttribute", [](MDGModifier & self, MObject node, MObject attribute, MString shortName, MString longName) { - throw std::logic_error{"Function not yet implemented."}; + .def("renameAttribute", [](MDGModifier & self, MObject node, MObject attribute, std::string shortName, std::string longName) { + if (node.isNull()) + { + throw std::invalid_argument("Cannot rename an attribute from a null node."); + } else if (!node.hasFn(MFn::kDependencyNode)) { + MString error_msg("Cannot rename attribute - node must be a 'node' object , not a '^1s' object."); + error_msg.format(error_msg, node.apiTypeStr()); + throw pybind11::type_error(error_msg.asChar()); + } + + if (attribute.isNull()) + { + throw std::invalid_argument("Cannot rename a null attribute."); + } else if (!attribute.hasFn(MFn::kAttribute)) { + MString error_msg("Cannot rename attribute - 'attribute' must be a 'kAttribute' object, not a(n) '^1s' object."); + error_msg.format(error_msg, attribute.apiTypeStr()); + throw pybind11::type_error(error_msg.asChar()); + } + + if (shortName.empty() || longName.empty()) + { + throw std::invalid_argument("Cannot rename an attribute to an empty string."); + } + + // TODO: When MFnAttribute is implement, raise a TypeError if `attribute` is not dynamic. + // TODO: Regex to restrict names to [a-zA-Z0-9_]? + // TODO: Do short/long name have length constraints? + + MStatus status = self.renameAttribute(node, attribute, MString(shortName.c_str()), MString(longName.c_str())); + + CHECK_STATUS(status) + }, R"pbdoc(Adds an operation to the modifer that renames a dynamic attribute on the given dependency node.)pbdoc") .def("renameNode", [](MDGModifier & self, MObject node, std::string newName) { diff --git a/tests/test_MDGModifier.py b/tests/test_MDGModifier.py index c59cbe1..e98a7bf 100644 --- a/tests/test_MDGModifier.py +++ b/tests/test_MDGModifier.py @@ -376,7 +376,7 @@ def test_removeAttribute_fail(): [TypeError, "a non-node object", (plug.attribute(), null)], [TypeError, "a non-attribute object", (plug.node(), plug.node())], ): - test_removeAttribute_fail.__doc__ = """Test MDGModifier::removeAttribute raises an error if with {}.""".format(doc) + test_removeAttribute_fail.__doc__ = """Test MDGModifier::removeAttribute raises an error if called with {}.""".format(doc) yield _removeAttribute_fail, exc, node, attr @@ -398,6 +398,60 @@ def test_removeExtensionAttribute_pass(): +@nose.with_setup(teardown=new_scene) +def test_renameAttribute(): + """Test MDGModfifier::renameAttribute binding.""" + + node = cmds.createNode('network') + + cmds.addAttr(node, ln='fizz') + + plug = as_plug(node + '.fizz') + + node_obj = plug.node() + attr_obj = plug.attribute() + null_obj = cmdc.Object() + + mod = cmdc.DGModifier() + mod.renameAttribute(node_obj, attr_obj, 'buzz', 'buzz') + + mod.doIt() + assert plug.name() == node + '.buzz', 'DGModifier.renameAttribute doIt failed' + + mod.undoIt() + assert plug.name() == node + '.fizz', 'DGModifier.renameAttribute undo failed' + + +@nose.with_setup(teardown=new_scene) +def test_renameAttribute_fail(): + """Test MDGModfifier::renameAttribute binding error handling.""" + + node = cmds.createNode('network') + cmds.addAttr(node, ln='fizz') + plug = as_plug(node + '.fizz') + + node_obj = plug.node() + attr_obj = plug.attribute() + null_obj = cmdc.Object() + + for exception, doc, (node, attr, short_name, long_name) in ( + [ValueError, "a null node", (null_obj, attr_obj, '', '')], + [ValueError, "a null attribute", (node_obj, null_obj, '', '')], + [ValueError, "an empty short name", (node_obj, attr_obj, '', '')], + [ValueError, "an empty long name", (node_obj, attr_obj, 'buzz', '')], + ): + test_renameAttribute_fail.__doc__ = """Test MDGModifier::renameAttribute raises an error if called with {}.""".format(doc) + yield _renameAttribute_fail, exception, node, attr, short_name, long_name + + +def _renameAttribute_fail(exception, node_obj, attr_obj, short_name, long_name): + nose.tools.assert_raises( + exception, + cmdc.DGModifier().renameAttribute, + node_obj, attr_obj, short_name, long_name + ) + + @nose.with_setup(teardown=new_scene) def test_renameNode(): """Test MDGModfifier::renameNode binding."""