"""Load model and project code from :mod:`message_data`."""
import re
from importlib import util
from importlib.abc import MetaPathFinder
from importlib.machinery import ModuleSpec, SourceFileLoader
[docs]class MessageDataFinder(MetaPathFinder):
"""Load model and project code from :mod:`message_data`."""
# Expression for supported module names
expr = re.compile(r"message_ix_models\.(?P<name>(model|project)\..*)")
# NB coverage is excluded, because the message-ix-models test suite does not
# install/use message-data.
@classmethod
def find_spec(cls, name, path, target=None): # pragma: no cover
match = cls.expr.match(name)
try:
# Construct the name for the actual module to load
new_name = f"message_data.{match.group('name')}"
except AttributeError:
# `match` was None; unsupported. Let the importlib defaults take over.
return None
# Get an import spec for the message_data submodule
spec = util.find_spec(new_name)
if not spec:
return None
# Create a new spec that loads message_data.model.foo as if it were
# message_ix_models.model.foo
new_spec = ModuleSpec(
name=name,
# Create a new loader that loads from the actual file with the desired name
loader=SourceFileLoader(fullname=name, path=spec.origin),
origin=spec.origin,
)
# These can't be passed through the constructor
new_spec.submodule_search_locations = spec.submodule_search_locations
return new_spec