diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index d566f29ca2..2b0b14ffc3 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -1701,7 +1701,6 @@ class CmdSetAttribute(ObjManipCommand): del deep[del_key] except (IndexError, KeyError, TypeError): continue - obj.attributes.add(key, val) return "\nDeleted attribute '%s' (= nested) from %s." % (attr, obj.name) else: exists = obj.attributes.has(key) @@ -1710,22 +1709,48 @@ class CmdSetAttribute(ObjManipCommand): return "\n%s has no attribute '%s'." % (obj.name, attr) def set_attr(self, obj, attr, value): + done = False for key, nested_keys in self.split_nested_attr(attr): if obj.attributes.has(key) and nested_keys: acc_key = nested_keys[-1] lookup_value = obj.attributes.get(key) deep = self.do_nested_lookup(lookup_value, *nested_keys[:-1]) if deep is not self.not_found: - # TODO - insert/append in lists - deep[acc_key] = value + # To support appending and inserting to lists + # a key that starts with @ will insert a new item at that + # location, and move the other elements down. + # Just '@' will append to the list + if isinstance(acc_key, str) and acc_key[0] == '@': + try: + if len(acc_key) > 1: + where = int(acc_key[1:]) + deep.insert(where, value) + else: + deep.append(value) + except AttributeError: + pass + else: + value = lookup_value + attr = key + done = True + break + + # List magic failed, just use like a key/index + try: + deep[acc_key] = value + except TypeError as err: + # Tuples can't be modified + return "\n%s - %s" % (err, deep) + value = lookup_value attr = key + done = True break verb = "Modified" if obj.attributes.has(attr) else "Created" try: - - obj.attributes.add(attr, value) + if not done: + obj.attributes.add(attr, value) return "\n%s attribute %s/%s = %s" % (verb, obj.name, attr, repr(value)) except SyntaxError: # this means literal_eval tried to parse a faulty string diff --git a/evennia/commands/default/tests.py b/evennia/commands/default/tests.py index ed88b4e02c..7444ddb086 100644 --- a/evennia/commands/default/tests.py +++ b/evennia/commands/default/tests.py @@ -536,7 +536,7 @@ class TestBuilding(CommandTest): self.call(building.CmdWipe(), "Obj2", "Wiped all attributes on Obj2.") def test_nested_attribute_commands(self): - # adding white space proves real parsing + # list - adding white space proves real parsing self.call(building.CmdSetAttribute(), "Obj/test1=[1,2]", "Created attribute Obj/test1 = [1, 2]") self.call(building.CmdSetAttribute(), "Obj/test1", "Attribute Obj/test1 = [1, 2]") self.call(building.CmdSetAttribute(), "Obj/test1[0]", "Attribute Obj/test1[0] = 1") @@ -551,8 +551,13 @@ class TestBuilding(CommandTest): # Delete non-existent self.call(building.CmdSetAttribute(), "Obj/test1[5] =", "Obj has no attribute 'test1[5]'.") + # Append + self.call(building.CmdSetAttribute(), + "Obj/test1[@] = 42", "Modified attribute Obj/test1 = [2, 42]") + self.call(building.CmdSetAttribute(), + "Obj/test1[@0] = -1", "Modified attribute Obj/test1 = [-1, 2, 42]") - # removing white space proves real parsing + # dict - removing white space proves real parsing self.call(building.CmdSetAttribute(), "Obj/test2={ 'one': 1, 'two': 2 }", "Created attribute Obj/test2 = {'one': 1, 'two': 2}") self.call(building.CmdSetAttribute(), "Obj/test2", "Attribute Obj/test2 = {'one': 1, 'two': 2}") @@ -572,6 +577,23 @@ class TestBuilding(CommandTest): self.call(building.CmdSetAttribute(), "Obj/test2[0]", "Obj has no attribute 'test2[0]'.") self.call(building.CmdSetAttribute(), "Obj/test2['five'] =", "Obj has no attribute 'test2['five']'.") + self.call(building.CmdSetAttribute(), + "Obj/test2[@]=42", "Modified attribute Obj/test2 = {'one': 99, 'three': 3, '@': 42}") + self.call(building.CmdSetAttribute(), + "Obj/test2[@1]=33", + "Modified attribute Obj/test2 = {'one': 99, 'three': 3, '@': 42, '@1': 33}") + + # tuple + self.call(building.CmdSetAttribute(), "Obj/tup = (1,2)", "Created attribute Obj/tup = (1, 2)") + self.call(building.CmdSetAttribute(), + "Obj/tup[1] = 99", "'tuple' object does not support item assignment - (1, 2)") + self.call(building.CmdSetAttribute(), + "Obj/tup[@] = 99", "'tuple' object does not support item assignment - (1, 2)") + self.call(building.CmdSetAttribute(), + "Obj/tup[@1] = 99", "'tuple' object does not support item assignment - (1, 2)") + self.call(building.CmdSetAttribute(), + # Special case for tuple, could have a better message + "Obj/tup[1] = ", "Obj has no attribute 'tup[1]'.") # Deaper nesting self.call(building.CmdSetAttribute(),