mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
184 lines
6.3 KiB
Python
184 lines
6.3 KiB
Python
from evennia.utils.utils import string_partial_matching
|
|
from evennia.utils.containers import OPTION_CLASSES
|
|
from django.utils.translation import gettext as _
|
|
|
|
_GA = object.__getattribute__
|
|
_SA = object.__setattr__
|
|
|
|
|
|
class InMemorySaveHandler:
|
|
"""
|
|
Fallback SaveHandler, implementing a minimum of the required save mechanism
|
|
and storing data in memory.
|
|
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.storage = {}
|
|
|
|
def add(self, key, value=None, **kwargs):
|
|
self.storage[key] = value
|
|
|
|
def get(self, key, default=None, **kwargs):
|
|
return self.storage.get(key, default)
|
|
|
|
|
|
class OptionHandler:
|
|
"""
|
|
This is a generic Option handler. Retrieve options either as properties on
|
|
this handler or by using the .get method.
|
|
|
|
This is used for Account.options but it could be used by Scripts or Objects
|
|
just as easily. All it needs to be provided is an options_dict.
|
|
|
|
"""
|
|
|
|
def __init__(
|
|
self,
|
|
obj,
|
|
options_dict=None,
|
|
savefunc=None,
|
|
loadfunc=None,
|
|
save_kwargs=None,
|
|
load_kwargs=None,
|
|
):
|
|
"""
|
|
Initialize an OptionHandler.
|
|
|
|
Args:
|
|
obj (object): The object this handler sits on. This is usually a TypedObject.
|
|
options_dict (dict): A dictionary of option keys, where the values
|
|
are options. The format of those tuples is: ('key', "Description to
|
|
show", 'option_type', <default value>)
|
|
savefunc (callable): A callable for all options to call when saving itself.
|
|
It will be called as `savefunc(key, value, **save_kwargs)`. A common one
|
|
to pass would be AttributeHandler.add.
|
|
loadfunc (callable): A callable for all options to call when loading data into
|
|
itself. It will be called as `loadfunc(key, default=default, **load_kwargs)`.
|
|
A common one to pass would be AttributeHandler.get.
|
|
save_kwargs (any): Optional extra kwargs to pass into `savefunc` above.
|
|
load_kwargs (any): Optional extra kwargs to pass into `loadfunc` above.
|
|
|
|
Notes:
|
|
Both loadfunc and savefunc must be specified. If only one is given, the other
|
|
will be ignored and in-memory storage will be used.
|
|
|
|
"""
|
|
self.obj = obj
|
|
self.options_dict = {} if options_dict is None else options_dict
|
|
|
|
if not savefunc and loadfunc:
|
|
self._in_memory_handler = InMemorySaveHandler()
|
|
savefunc = InMemorySaveHandler.add
|
|
loadfunc = InMemorySaveHandler.get
|
|
self.savefunc = savefunc
|
|
self.loadfunc = loadfunc
|
|
self.save_kwargs = {} if save_kwargs is None else save_kwargs
|
|
self.load_kwargs = {} if load_kwargs is None else load_kwargs
|
|
|
|
# This dictionary stores the in-memory Options objects by their key for
|
|
# quick lookup.
|
|
self.options = {}
|
|
|
|
def __getattr__(self, key):
|
|
"""
|
|
Allow for obj.options.key
|
|
|
|
"""
|
|
return self.get(key)
|
|
|
|
def __setattr__(self, key, value):
|
|
"""
|
|
Allow for obj.options.key = value
|
|
|
|
But we must be careful to avoid infinite loops!
|
|
|
|
"""
|
|
try:
|
|
if key in _GA(self, "options_dict"):
|
|
_GA(self, "set")(key, value)
|
|
except AttributeError:
|
|
pass
|
|
_SA(self, key, value)
|
|
|
|
def _load_option(self, key):
|
|
"""
|
|
Loads option on-demand if it has not been loaded yet.
|
|
|
|
Args:
|
|
key (str): The option being loaded.
|
|
|
|
Returns:
|
|
|
|
"""
|
|
desc, clsname, default_val = self.options_dict[key]
|
|
loaded_option = OPTION_CLASSES.get(clsname)(self, key, desc, default_val)
|
|
# store the value for future easy access
|
|
self.options[key] = loaded_option
|
|
return loaded_option
|
|
|
|
def get(self, key, default=None, return_obj=False, raise_error=False):
|
|
"""
|
|
Retrieves an Option stored in the handler. Will load it if it doesn't exist.
|
|
|
|
Args:
|
|
key (str): The option key to retrieve.
|
|
default (any): What to return if the option is defined.
|
|
return_obj (bool, optional): If True, returns the actual option
|
|
object instead of its value.
|
|
raise_error (bool, optional): Raise Exception if key is not found in options.
|
|
Returns:
|
|
option_value (any or Option): An option value the Option itself.
|
|
Raises:
|
|
KeyError: If option is not defined.
|
|
|
|
"""
|
|
if key not in self.options_dict:
|
|
if raise_error:
|
|
raise KeyError(_("Option not found!"))
|
|
return default
|
|
# get the options or load/recache it
|
|
op_found = self.options.get(key) or self._load_option(key)
|
|
return op_found if return_obj else op_found.value
|
|
|
|
def set(self, key, value, **kwargs):
|
|
"""
|
|
Change an individual option.
|
|
|
|
Args:
|
|
key (str): The key of an option that can be changed. Allows partial matching.
|
|
value (str): The value that should be checked, coerced, and stored.:
|
|
kwargs (any, optional): These are passed into the Option's validation function,
|
|
save function and display function and allows to customize either.
|
|
|
|
Returns:
|
|
value (any): Value stored in option, after validation.
|
|
|
|
"""
|
|
if not key:
|
|
raise ValueError(_("Option field blank!"))
|
|
match = string_partial_matching(list(self.options_dict.keys()), key, ret_index=False)
|
|
if not match:
|
|
raise ValueError(_("Option not found!"))
|
|
if len(match) > 1:
|
|
raise ValueError(_("Multiple matches:")
|
|
+ f"{', '.join(match)}. "
|
|
+ _("Please be more specific."))
|
|
match = match[0]
|
|
op = self.get(match, return_obj=True)
|
|
op.set(value, **kwargs)
|
|
return op.value
|
|
|
|
def all(self, return_objs=False):
|
|
"""
|
|
Get all options defined on this handler.
|
|
|
|
Args:
|
|
return_objs (bool, optional): Return the actual Option objects rather
|
|
than their values.
|
|
Returns:
|
|
all_options (dict): All options on this handler, either `{key: value}`
|
|
or `{key: <Option>}` if `return_objs` is `True`.
|
|
|
|
"""
|
|
return [self.get(key, return_obj=return_objs) for key in self.options_dict]
|