Add Traits.traithandler back reference so custom Traits can find and read values of other traits on the handler. See #2801

This commit is contained in:
Griatch 2024-07-13 19:36:15 +02:00
parent d769a90b62
commit 7a7479955f
3 changed files with 30 additions and 15 deletions

View file

@ -6,6 +6,8 @@
bugs etc in-game (InspectorCaracal) bugs etc in-game (InspectorCaracal)
- [Feat][pull3586]: Add ANSI color support `|U`, `|I`, `|i`, `|s`, `|S` for - [Feat][pull3586]: Add ANSI color support `|U`, `|I`, `|i`, `|s`, `|S` for
underline reset, italic/reset and strikethrough/reset (0xDEADFED5) underline reset, italic/reset and strikethrough/reset (0xDEADFED5)
- Feat: Add `Trait.traithandler` back-reference so custom Traits from the Traits
contrib can find and reference other Traits. (Griatch)
- [Fix][pull3571]: Better visual display of partial multimatch search results - [Fix][pull3571]: Better visual display of partial multimatch search results
(InspectorCaracal) (InspectorCaracal)
- [Fix][pull3550]: Issue where rpsystem contrib search would do a global instead - [Fix][pull3550]: Issue where rpsystem contrib search would do a global instead

View file

@ -9,10 +9,9 @@ Unit test module for Trait classes.
from copy import copy from copy import copy
from anything import Something from anything import Something
from mock import MagicMock, patch
from evennia.objects.objects import DefaultCharacter from evennia.objects.objects import DefaultCharacter
from evennia.utils.test_resources import BaseEvenniaTestCase, EvenniaTest from evennia.utils.test_resources import BaseEvenniaTestCase, EvenniaTest
from mock import MagicMock, patch
from . import traits from . import traits
@ -156,6 +155,16 @@ class TraitHandlerTest(_TraitHandlerBase):
self.obj.attributes.get("traits", category="traits")["test1"]["value"], None self.obj.attributes.get("traits", category="traits")["test1"]["value"], None
) )
def test_related_traits(self):
"""Test traits related to each other via Trait.get_trait()"""
trait1 = self.traithandler.test1
trait2 = self.traithandler.test2
self.assertEqual(trait1.traithandler, self.traithandler)
self.assertEqual(trait1.get_trait("test1"), trait1)
self.assertEqual(trait1.get_trait("test2"), trait2)
class TestTrait(_TraitHandlerBase): class TestTrait(_TraitHandlerBase):
""" """

View file

@ -456,15 +456,9 @@ from functools import total_ordering
from time import time from time import time
from django.conf import settings from django.conf import settings
from evennia.utils import logger from evennia.utils import logger
from evennia.utils.dbserialize import _SaverDict from evennia.utils.dbserialize import _SaverDict
from evennia.utils.utils import ( from evennia.utils.utils import class_from_module, inherits_from, list_to_string, percent
class_from_module,
inherits_from,
list_to_string,
percent,
)
# Available Trait classes. # Available Trait classes.
# This way the user can easily supply their own. Each # This way the user can easily supply their own. Each
@ -657,7 +651,9 @@ class TraitHandler:
if trait is None and trait_key in self.trait_data: if trait is None and trait_key in self.trait_data:
trait_type = self.trait_data[trait_key]["trait_type"] trait_type = self.trait_data[trait_key]["trait_type"]
trait_cls = self._get_trait_class(trait_type) trait_cls = self._get_trait_class(trait_type)
trait = self._cache[trait_key] = trait_cls(_GA(self, "trait_data")[trait_key]) trait = self._cache[trait_key] = trait_cls(
_GA(self, "trait_data")[trait_key], handler=self
)
return trait return trait
def add( def add(
@ -856,7 +852,7 @@ class Trait:
# and have them treated like data to store. # and have them treated like data to store.
allow_extra_properties = True allow_extra_properties = True
def __init__(self, trait_data): def __init__(self, trait_data, handler=None):
""" """
This both initializes and validates the Trait on creation. It must This both initializes and validates the Trait on creation. It must
raise exception if validation fails. The TraitHandler will call this raise exception if validation fails. The TraitHandler will call this
@ -869,12 +865,15 @@ class Trait:
value in cls.data_default_values. Any extra kwargs will be made value in cls.data_default_values. Any extra kwargs will be made
available as extra properties on the Trait, assuming the class available as extra properties on the Trait, assuming the class
variable `allow_extra_properties` is set. variable `allow_extra_properties` is set.
handler (TraitHandler): The handler that this Trait is connected to.
This is for referencing other traits.
Raises: Raises:
TraitException: If input-validation failed. TraitException: If input-validation failed.
""" """
self._data = self.__class__.validate_input(self.__class__, trait_data) self._data = self.__class__.validate_input(self.__class__, trait_data)
self.traithandler = handler
if not isinstance(trait_data, _SaverDict): if not isinstance(trait_data, _SaverDict):
logger.log_warn( logger.log_warn(
@ -955,6 +954,7 @@ class Trait:
"data_default", "data_default",
"trait_type", "trait_type",
"allow_extra_properties", "allow_extra_properties",
"traithandler",
): ):
return _GA(self, key) return _GA(self, key)
try: try:
@ -970,10 +970,9 @@ class Trait:
"""Set extra parameters as attributes. """Set extra parameters as attributes.
Arbitrary attributes set on a Trait object will be Arbitrary attributes set on a Trait object will be
stored in the 'extra' key of the `_data` attribute. stored as extra keys in the Trait's data.
This behavior is enabled by setting the instance This behavior is enabled by setting the instance variable `allow_extra_properties`.
variable `_locked` to True.
""" """
propobj = getattr(self.__class__, key, None) propobj = getattr(self.__class__, key, None)
@ -984,7 +983,7 @@ class Trait:
return return
else: else:
# this is some other value # this is some other value
if key in ("_data",): if key in ("_data", "traithandler"):
_SA(self, key, value) _SA(self, key, value)
return return
if _GA(self, "allow_extra_properties"): if _GA(self, "allow_extra_properties"):
@ -1053,6 +1052,11 @@ class Trait:
"""Display name for the trait.""" """Display name for the trait."""
return self._data["name"] return self._data["name"]
def get_trait(self, trait_key):
"""Get another Trait from the handler. Not used by default, but can be used
for custom traits that are affected by other traits on the same handler."""
return self.traithandler.get(trait_key)
key = name key = name
# Numeric operations # Numeric operations