import six
from shuup.apps.provides import get_provide_specs_and_objects
from shuup.utils.importing import load
from shuup.utils.text import force_ascii
[docs]
class ModuleNotFound(ValueError):
pass
[docs]
class ModuleInterface:
_cached_modules_impl = None
module_options_field = "module_data" # May be overridden on class level
module_provides_key = None
def _load_modules(self):
enabled_supplier_modules = self.supplier_modules.all()
loaded_modules = []
options = getattr(self, self.module_options_field, None) or {}
for supplier_module in enabled_supplier_modules:
impls = self.get_module_implementation_map()
if supplier_module.module_identifier not in impls:
raise ModuleNotFound(f"Invalid module identifier {supplier_module.name!r} in {force_ascii(repr(self))}")
spec = impls[supplier_module.module_identifier]
module = load(
spec,
context_explanation=f"Loading module for {force_ascii(repr(self))}",
)
loaded_modules.append(module(self, options))
return loaded_modules
@property
def modules(self):
if not getattr(self, "_cached_modules_impl", None):
self._cached_modules_impl = self._load_modules()
return self._cached_modules_impl
[docs]
@classmethod
def get_module_implementation_map(cls):
"""
Get a dict that maps module spec identifiers (short strings) into actual spec names.
As an example::
{"Eggs": "foo_package.bar_module:EggsClass"}
:rtype: dict[str, str]
"""
identifier_to_spec = {}
for spec, module in six.iteritems(get_provide_specs_and_objects(cls.module_provides_key)):
if module.identifier:
identifier_to_spec[module.identifier] = spec
return identifier_to_spec