Source code for pynenc.util.import_app
import importlib
import logging
import os
import types
from pynenc.app import Pynenc
[docs]
def build_file_path_from_module_name(module_name: str) -> str:
"""
Builds a file path from a module name.
:param str module_name: The name of the module.
:return: The file path corresponding to the module.
:raises ValueError: If the module name does not specify a path.
"""
filepath_arr = module_name.split(".")
if len(filepath_arr) == 1:
raise ValueError(f"Module name '{module_name}' does not specify a path.")
*filepath_arr, _ = filepath_arr
return os.path.join(*filepath_arr) + ".py"
[docs]
def create_module_spec(
app_instance_name: str, file_location: str
) -> importlib.machinery.ModuleSpec:
"""
Creates a module specification from a file location.
:param str app_instance_name: The name of the app instance.
:param str file_location: The location of the file.
:return: A ModuleSpec object.
:raises ValueError: If the module spec could not be created.
"""
spec = importlib.util.spec_from_file_location(app_instance_name, file_location)
if not spec or not spec.loader:
raise ValueError(
f"Module spec could not be created for location '{file_location}'."
)
return spec
[docs]
def load_module_from_spec(spec: importlib.machinery.ModuleSpec) -> types.ModuleType:
"""
Loads a module from a given specification.
:param importlib.machinery.ModuleSpec spec: The module specification.
:return: The loaded module.
:raises ValueError: If the loader is not found for the given spec.
"""
if spec.loader is None:
raise ValueError(f"Loader not found for spec '{spec}'.")
module = importlib.util.module_from_spec(spec)
try:
spec.loader.exec_module(module)
except FileNotFoundError as ex:
raise ValueError(f"Module file not found for spec '{spec}'.") from ex
return module
[docs]
def import_module_as_file(module_name: str, ex: Exception) -> types.ModuleType:
"""
Attempts to import a module as a file.
:param str module_name: The name of the module.
:param Exception ex: The exception to be raised in case of failure.
:return: The imported module.
:raises ValueError: If the module file cannot be found.
"""
filepath = build_file_path_from_module_name(module_name)
*_, app_instance_name = module_name.split(".")
spec = create_module_spec(app_instance_name, filepath)
try:
return load_module_from_spec(spec)
except FileNotFoundError as file_not_found_ex:
raise ValueError(
f"Module file not found for spec '{spec}'."
) from file_not_found_ex
[docs]
def find_pynenc_instance_in_module(module: types.ModuleType) -> Pynenc:
"""
Finds and returns the Pynenc app instance from a given module.
:param types.ModuleType module: The module to search in.
:return: The found Pynenc app instance.
:raises ValueError: If no Pynenc app instance is found.
"""
for attr_name in dir(module):
attr = getattr(module, attr_name)
if isinstance(attr, Pynenc):
return attr
raise ValueError(f"No Pynenc app instance found in '{module.__name__}'")
[docs]
def find_app_instance(module_name: str | None) -> Pynenc:
"""
Finds the Pynenc app instance in the specified module.
:param str | None module_name: The name of the module.
:return: The Pynenc app instance.
:raises ValueError: If no module name is provided.
"""
if not module_name:
raise ValueError("No module name provided")
logging.debug(f"Attempting to find app instance in module: {module_name}")
try:
module = importlib.import_module(module_name)
except ModuleNotFoundError:
module = import_module_as_file(module_name, ModuleNotFoundError())
return find_pynenc_instance_in_module(module)