"""
Module containing all function to import new plugins
"""
import importlib
from pydantic import BaseModel
[docs]class ModuleImport(BaseModel):
    """
    Data-Class to import a given python file
    from ``import_path`` and load the given
    ``class_name``
    """
    import_path: str
    class_name: str
[docs]    def import_class(self):
        """Import the Module with class_name from the import path"""
        module = importlib.import_module(self.import_path)
        return getattr(module, self.class_name)  
[docs]class SaveUpdateDict(dict):
    """
    Custom object to safely update the dictionary.
    Duplicate entries will raise an error.
    """
[docs]    def update(self, new_dict, **kwargs) -> None:
        """Check if all modules have distinct identifier strings
        Args:
            new_dict:
            **kwargs:
        """
        duplicates = set(self.keys()).intersection(new_dict.keys())
        if duplicates:
            raise KeyError(
                "One or multiple MODULE_TYPES contain "
                "the same string identifier (type). "
                f"The type has to be unique! {', '.join(duplicates)}"
            )
        super().update(new_dict)  
[docs]def load_plugin(name: str, loaded_classes: SaveUpdateDict, plugin_types_name: str):
    """
    Loads the plugin based on the given name.
    Args:
        name str:
            Name of the plugin
        loaded_classes SaveUpdateDict:
            SaveUpdateDict instance with already
            loaded classes (modules or models)
        plugin_types_name str:
            Name of the dictionary in the plugin.
            Typical values are "MODULE_TYPES" or "MODEL_TYPES".
    """
    for key in loaded_classes:
        if key.startswith(f"{name}."):
            return  # Already loaded
    try:
        plugin = importlib.import_module(name)
    except ImportError as err:
        raise ImportError(f"Plugin '{name}' is not installed.") from err
    try:
        plugin_dict = getattr(plugin, plugin_types_name)
    except AttributeError:
        raise ImportError(
            f"Plugin '{name}' has no dictionary called "
            f"'{plugin_types_name}' to import plugin types."
        )
    if not isinstance(plugin_dict, dict):
        raise TypeError(
            f"Loaded object '{plugin_types_name}' is not a dictionary "
            f"but a {type(plugin_dict)}"
        )
    for key, value in plugin_dict.items():
        loaded_classes.update({f"{name}.{key}": value})