Refactor containers for inheritance and delayed loading
This commit is contained in:
parent
d1baab7c0b
commit
6ddc98a947
8 changed files with 234 additions and 173 deletions
|
|
@ -1,6 +1,14 @@
|
|||
"""
|
||||
Containers
|
||||
|
||||
Containers are storage classes usually initialized from a setting. They
|
||||
represent Singletons and acts as a convenient place to find resources (
|
||||
available as properties on the singleton)
|
||||
|
||||
evennia.GLOBAL_SCRIPTS
|
||||
evennia.VALIDATOR_FUNCS
|
||||
evennia.OPTION_CLASSES
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
|
@ -9,7 +17,82 @@ from evennia.utils.utils import class_from_module, callables_from_module
|
|||
from evennia.utils import logger
|
||||
|
||||
|
||||
class GlobalScriptContainer(object):
|
||||
class Container(object):
|
||||
"""
|
||||
Base container class. A container is simply a storage object whose
|
||||
properties can be acquired as a property on it. This is generally
|
||||
considered a read-only affair.
|
||||
|
||||
The container is initialized by a list of modules containing callables.
|
||||
|
||||
"""
|
||||
storage_modules = []
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Read data from module.
|
||||
|
||||
"""
|
||||
self.loaded_data = None
|
||||
|
||||
def _load_data(self):
|
||||
"""
|
||||
Delayed import to avoid eventual circular imports from inside
|
||||
the storage modules.
|
||||
|
||||
"""
|
||||
if self.loaded_data is None:
|
||||
for module in self.storage_modules:
|
||||
self.loaded_data.update(callables_from_module(module))
|
||||
|
||||
def __getattr__(self, key):
|
||||
self._load_data()
|
||||
return self.loaded_data.get(key)
|
||||
|
||||
def get(self, key):
|
||||
"""
|
||||
Retrive data by key (in case of not knowing it beforehand).
|
||||
|
||||
Args:
|
||||
key (str): The name of the script.
|
||||
|
||||
Returns:
|
||||
any (any): The data loaded on this container.
|
||||
|
||||
"""
|
||||
return self.__getattr__(key)
|
||||
|
||||
def all(self):
|
||||
"""
|
||||
Get all stored data
|
||||
|
||||
Returns:
|
||||
scripts (list): All global script objects stored on the container.
|
||||
|
||||
"""
|
||||
self._load_data()
|
||||
return list(self.loaded_data.values())
|
||||
|
||||
|
||||
class ValidatorContainer(Container):
|
||||
"""
|
||||
Loads and stores the final list of VALIDATOR FUNCTIONS.
|
||||
|
||||
Can access these as properties or dictionary-contents.
|
||||
"""
|
||||
storage_modules = settings.VALIDATOR_FUNC_MODULES
|
||||
|
||||
|
||||
class OptionContainer(Container):
|
||||
"""
|
||||
Loads and stores the final list of OPTION CLASSES.
|
||||
|
||||
Can access these as properties or dictionary-contents.
|
||||
"""
|
||||
storage_modules = settings.OPTION_CLASS_MODULES
|
||||
|
||||
|
||||
class GlobalScriptContainer(Container):
|
||||
"""
|
||||
Simple Handler object loaded by the Evennia API to contain and manage a
|
||||
game's Global Scripts. Scripts to start are defined by
|
||||
|
|
@ -19,45 +102,56 @@ class GlobalScriptContainer(object):
|
|||
import evennia
|
||||
evennia.GLOBAL_SCRIPTS.scriptname
|
||||
|
||||
"""
|
||||
Note:
|
||||
This does not use much of the BaseContainer since it's not loading
|
||||
callables from settings but a custom dict of tuples.
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialize the container by preparing scripts. Lazy-load only when the
|
||||
script is requested.
|
||||
|
||||
Note: We must delay loading of typeclasses since this module may get
|
||||
initialized before Scripts are actually initialized.
|
||||
|
||||
"""
|
||||
self.script_data = {key: {} if data is None else data
|
||||
self.loaded_data = {key: {} if data is None else data
|
||||
for key, data in settings.GLOBAL_SCRIPTS.items()}
|
||||
self.script_storage = {}
|
||||
self.typeclass_storage = {}
|
||||
|
||||
for key, data in self.script_data.items():
|
||||
try:
|
||||
typeclass = data.get('typeclass', settings.BASE_SCRIPT_TYPECLASS)
|
||||
self.typeclass_storage[key] = class_from_module(typeclass)
|
||||
except ImportError as err:
|
||||
logger.log_err(f"GlobalContainer could not start global script {key}: {err}")
|
||||
|
||||
def __getitem__(self, key):
|
||||
|
||||
if key not in self.typeclass_storage:
|
||||
# this script is unknown to the container
|
||||
return None
|
||||
|
||||
# (re)create script on-demand
|
||||
return self.script_storage.get(key) or self._load_script(key)
|
||||
self.typeclass_storage = None
|
||||
|
||||
def __getattr__(self, key):
|
||||
return self[key]
|
||||
if key not in self.loaded_data:
|
||||
return None
|
||||
return self.script_storage.get(key) or self._load_script(key)
|
||||
|
||||
def _load_data(self):
|
||||
"""
|
||||
This delayed import avoids trying to load Scripts before they are
|
||||
initialized.
|
||||
|
||||
"""
|
||||
if self.typeclass_storage is None:
|
||||
self.typeclass_storage = {}
|
||||
for key, data in self.loaded_data.items():
|
||||
try:
|
||||
typeclass = data.get('typeclass', settings.BASE_SCRIPT_TYPECLASS)
|
||||
self.typeclass_storage[key] = class_from_module(typeclass)
|
||||
except ImportError as err:
|
||||
logger.log_err(
|
||||
f"GlobalScriptContainer could not start global script {key}: {err}")
|
||||
|
||||
def _load_script(self, key):
|
||||
|
||||
self._load_data()
|
||||
|
||||
typeclass = self.typeclass_storage[key]
|
||||
found = typeclass.objects.filter(db_key=key).first()
|
||||
interval = self.script_data[key].get('interval', None)
|
||||
start_delay = self.script_data[key].get('start_delay', None)
|
||||
repeats = self.script_data[key].get('repeats', 0)
|
||||
desc = self.script_data[key].get('desc', '')
|
||||
interval = self.loaded_data[key].get('interval', None)
|
||||
start_delay = self.loaded_data[key].get('start_delay', None)
|
||||
repeats = self.loaded_data[key].get('repeats', 0)
|
||||
desc = self.loaded_data[key].get('desc', '')
|
||||
|
||||
if not found:
|
||||
new_script, errors = typeclass.create(key=key, persistent=True,
|
||||
|
|
@ -81,20 +175,6 @@ class GlobalScriptContainer(object):
|
|||
self.script_storage[key] = found
|
||||
return found
|
||||
|
||||
def get(self, key):
|
||||
"""
|
||||
Retrive script by key (in case of not knowing it beforehand).
|
||||
|
||||
Args:
|
||||
key (str): The name of the script.
|
||||
|
||||
Returns:
|
||||
script (Script): The named global script.
|
||||
|
||||
"""
|
||||
# note that this will recreate the script if it doesn't exist/was lost
|
||||
return self[key]
|
||||
|
||||
def all(self):
|
||||
"""
|
||||
Get all scripts.
|
||||
|
|
@ -103,53 +183,11 @@ class GlobalScriptContainer(object):
|
|||
scripts (list): All global script objects stored on the container.
|
||||
|
||||
"""
|
||||
return list(self.script_storage.values())
|
||||
return [self.__getattr__(key) for key in self.loaded_data]
|
||||
|
||||
|
||||
# Create singleton of the GlobalHandler for the API.
|
||||
# Create all singletons
|
||||
|
||||
GLOBAL_SCRIPTS = GlobalScriptContainer()
|
||||
|
||||
|
||||
class ValidatorContainer(object):
|
||||
"""
|
||||
Loads and stores the final list of VALIDATOR FUNCTIONS.
|
||||
|
||||
Can access these as properties or dictionary-contents.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.valid_storage = {}
|
||||
for module in settings.VALIDATOR_FUNC_MODULES:
|
||||
self.valid_storage.update(callables_from_module(module))
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.valid_storage.get(item, None)
|
||||
|
||||
def __getattr__(self, item):
|
||||
return self[item]
|
||||
|
||||
|
||||
# Ensure that we have a Singleton of ValidHandler that is always loaded... and only needs to be loaded once.
|
||||
VALIDATOR_FUNCS = ValidatorContainer()
|
||||
|
||||
|
||||
class OptionContainer(object):
|
||||
"""
|
||||
Loads and stores the final list of OPTION CLASSES.
|
||||
|
||||
Can access these as properties or dictionary-contents.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.option_storage = {}
|
||||
for module in settings.OPTION_CLASS_MODULES:
|
||||
self.option_storage.update(callables_from_module(module))
|
||||
|
||||
def __getitem__(self, item):
|
||||
return self.option_storage.get(item, None)
|
||||
|
||||
def __getattr__(self, item):
|
||||
return self[item]
|
||||
|
||||
|
||||
# Ensure that we have a Singleton that keeps all loaded Option classes
|
||||
OPTION_CLASSES = OptionContainer()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue