diff --git a/evennia/objects/tests.py b/evennia/objects/tests.py index a718d9b01b..b447eed09f 100644 --- a/evennia/objects/tests.py +++ b/evennia/objects/tests.py @@ -239,23 +239,32 @@ class SubAttributeProperty(AttributeProperty): class SubTagProperty(TagProperty): pass +class CustomizedProperty(AttributeProperty): + def at_set(self, value, obj): + obj.settest = value + return value + + def at_get(self, value, obj): + return value + obj.awaretest class TestObjectPropertiesClass(DefaultObject): attr1 = AttributeProperty(default="attr1") attr2 = AttributeProperty(default="attr2", category="attrcategory") attr3 = AttributeProperty(default="attr3", autocreate=False) attr4 = SubAttributeProperty(default="attr4") + cusattr = CustomizedProperty(default=5) tag1 = TagProperty() tag2 = TagProperty(category="tagcategory") tag3 = SubTagProperty() testalias = AliasProperty() testperm = PermissionProperty() + awaretest = 5 + settest = 0 @property def base_property(self): self.property_initialized = True - class TestProperties(EvenniaTestCase): """ Test Properties. @@ -263,7 +272,7 @@ class TestProperties(EvenniaTestCase): """ def setUp(self): - self.obj = create.create_object(TestObjectPropertiesClass, key="testobj") + self.obj: TestObjectPropertiesClass = create.create_object(TestObjectPropertiesClass, key="testobj") def tearDown(self): self.obj.delete() @@ -305,3 +314,30 @@ class TestProperties(EvenniaTestCase): # Verify that regular properties do not get fetched in init_evennia_properties, # only Attribute or TagProperties. self.assertFalse(hasattr(obj, "property_initialized")) + + def test_object_awareness(self): + '''Test the "object-awareness" of customized AttributeProperty getter/setters''' + obj = self.obj + + # attribute properties receive on obj ref in the getter/setter that can customize return + self.assertEqual(obj.cusattr, 10) + self.assertEqual(obj.settest, 5) + obj.awaretest = 10 + self.assertEqual(obj.cusattr, 15) + obj.cusattr = 10 + self.assertEqual(obj.cusattr, 20) + self.assertEqual(obj.settest, 10) + + # attribute value mutates if you do += or similar (combined get-set) + obj.cusattr += 10 + self.assertEqual(obj.attributes.get("cusattr"), 30) + self.assertEqual(obj.settest, 30) + self.assertEqual(obj.cusattr, 40) + obj.awaretest = 0 + obj.cusattr += 20 + self.assertEqual(obj.attributes.get("cusattr"), 50) + self.assertEqual(obj.settest, 50) + self.assertEqual(obj.cusattr, 50) + del obj.cusattr + self.assertEqual(obj.cusattr, 5) + self.assertEqual(obj.settest, 5) diff --git a/evennia/typeclasses/attributes.py b/evennia/typeclasses/attributes.py index 9830befa7f..b8c68bb7ac 100644 --- a/evennia/typeclasses/attributes.py +++ b/evennia/typeclasses/attributes.py @@ -218,15 +218,13 @@ class AttributeProperty: """ value = self._default try: - value = self.at_get( - getattr(instance, self.attrhandler_name).get( - key=self._key, - default=self._default, - category=self._category, - strattr=self._strattr, - raise_exception=self._autocreate, - ) - ) + value = self.at_get(getattr(instance, self.attrhandler_name).get( + key=self._key, + default=self._default, + category=self._category, + strattr=self._strattr, + raise_exception=self._autocreate, + ), instance) except AttributeError: if self._autocreate: # attribute didn't exist and autocreate is set @@ -243,7 +241,7 @@ class AttributeProperty: ( getattr(instance, self.attrhandler_name).add( self._key, - self.at_set(value), + self.at_set(value, instance), category=self._category, lockstring=self._lockstring, strattr=self._strattr, @@ -259,13 +257,14 @@ class AttributeProperty: """ getattr(instance, self.attrhandler_name).remove(key=self._key, category=self._category) - def at_set(self, value): + def at_set(self, value, obj): """ The value to set is passed through the method. It can be used to customize/validate the input in a custom child class. Args: value (any): The value about to the stored in this Attribute. + obj (object): Object the attribute is attached to Returns: any: The value to store. @@ -276,13 +275,14 @@ class AttributeProperty: """ return value - def at_get(self, value): + def at_get(self, value, obj): """ The value returned from the Attribute is passed through this method. It can be used to react to the retrieval or modify the result in some way. Args: value (any): Value returned from the Attribute. + obj (object): Object the attribute is attached to Returns: any: The value to return to the caller.