Source code for smif.data_layer.model_loader
"""ModelLoader reads python modules as specified at runtime, loading and instantiating
objects.
"""
import importlib
import logging
import os
[docs]class ModelLoader(object):
"""Load Model from config
Examples
--------
Call :py:meth:`ModelLoader.load` to create, load and return a
:class:`Model` object.
>>> loader = ModelLoader()
>>> sector_model = loader.load(sector_model_config)
>>> conversion_model = loader.load(conversion_model_config)
"""
def __init__(self):
self.logger = logging.getLogger(__name__)
[docs] def load(self, config):
"""Loads the model class specified by the config, returns an instance of that class
using the Model.from_dict method.
Arguments
---------
config : dict
The model configuration data. Must include:
- name (name for smif internal use)
- path (absolute path to python module file)
- classname (name of Model implementation class)
- anything required by the Model.from_dict classmethod
Returns
-------
:class:`~smif.model.Model`
"""
klass = self.load_model_class(
config["name"], config["path"], config["classname"]
)
if not hasattr(klass, "from_dict"):
msg = (
"Model '{}' does not have a ``from_dict`` method and "
"cannot be loaded from config"
)
raise KeyError(msg.format(config["name"]))
model_instance = klass.from_dict(config)
if model_instance:
return model_instance
else:
raise ValueError("Model not initialised from configuration data")
[docs] def load_model_class(self, model_name, model_path, classname):
"""Dynamically load model class
Arguments
---------
model_name : str
The name used internally to identify the SectorModel
model_path : str
The path to the python module which contains the SectorModel
implementation
classname : str
The name of the class of the SectorModel implementation
Returns
-------
class
The SectorModel implementation
"""
if not os.path.exists(model_path):
msg = "Cannot find '{}' for the '{}' model".format(model_path, model_name)
raise FileNotFoundError(msg)
msg = "Importing model %s as class %s from module at %s"
self.logger.info(msg, model_name, classname, model_path)
spec = importlib.util.spec_from_file_location(model_name, model_path)
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
klass = module.__dict__[classname]
return klass