"""Module module."""
import sys
import logging
from pkg_resources import iter_entry_points


logger = logging.getLogger(__name__)


class AbstractModule:

    """This class serves as a basis for all NITE modules."""

    @property
    def NITE(self):
        """Return NITE."""
        return self._NITE

    @NITE.setter
    def NITE(self, value):
        """Set NITE."""
        self._NITE = value

    def start(self):
        """Start the module.

        This is an abstract method, you should implement your own.

        """
        raise NotImplementedError('All derivatives of `AbstractModule` must implement a `start` method.')

    def stop(self):
        """Stop the module.

        This is an abstract method, you should implement your own.

        """
        raise NotImplementedError('All derivatives of `AbstractModule` must implement a `stop` method.')

    def __init__(self, NITE):
        """Construct an instance of this module."""
        self.NITE = NITE


class ModuleManager:

    """The ModuleManager manages the loading, unloading, etc. of modules."""

    @property
    def NITE(self):
        """Return NITE."""
        return self._NITE

    @NITE.setter
    def NITE(self, value):
        """Set NITE."""
        self._NITE = value

    @property
    def modules(self):
        """Return modules."""
        return self._modules

    @modules.setter
    def modules(self, value):
        """Set modules."""
        self._modules = value

    def load_single(self, identifier):
        """Load a module by its identifier."""
        logger.debug('Attempting to load module "%s"', identifier)

        for entry_point in iter_entry_points(group='nite.modules', name=identifier):
            self.modules[identifier] = entry_point.load()(self.NITE)

        logger.debug('Module "%s" loaded', identifier)

    def unload_single(self, identifier):
        """Unload a module by its identifier."""
        logger.debug('Unloading module "%s"', identifier)

        # We need to remove the module from the python interpreter in order for live codebase updates to work.
        sys.modules.pop(self.modules[identifier].__module__)

        del self.modules[identifier]
        self.modules.pop("", None)
        self.modules.pop(None, None)

        logger.debug('Unloaded module "%s"', identifier)

    def load(self):
        """Load all modules."""
        for entry_point in iter_entry_points(group='nite.modules'):
            self.load_single(entry_point.name)

    def unload(self):
        """Unload all modules."""
        # We need to copy the list of identifiers, because unloading a module removed it from the modules dict
        identifiers = list(self.modules.keys())
        for identifier in identifiers:
            self.unload_single(identifier)

    def start_single(self, identifier):
        """Start a module by its identifier."""
        logger.debug('Starting module "%s"', identifier)
        self.modules[identifier].start()
        logger.debug('Module "%s" started', identifier)

    def stop_single(self, identifier):
        """Stop a module by its identifier."""
        logger.debug('Stopping module "%s"', identifier)
        self.modules[identifier].stop()
        logger.debug('Module "%s" stopped', identifier)

    def start(self):
        """Start all modules."""
        self.load()

        for identifier in self.modules.keys():
            self.start_single(identifier)

    def stop(self):
        """Stop all modules."""
        for identifier in self.modules.keys():
            self.stop_single(identifier)

        self.unload()

    def __init__(self, NITE):
        """Constructor."""
        self.NITE = NITE
        self.modules = {}
        logger.debug('Module manager initialized')

"""Module module."""
import sys
import logging
from pkg_resources import iter_entry_points


logger = logging.getLogger(__name__)


class AbstractModule:

    """This class serves as a basis for all NITE modules."""

    @property
    def NITE(self):
        """Return NITE."""
        return self._NITE

    @NITE.setter
    def NITE(self, value):
        """Set NITE."""
        self._NITE = value

    def start(self):
        """Start the module.

        This is an abstract method, you should implement your own.

        """
        raise NotImplementedError('All derivatives of `AbstractModule` must implement a `start` method.')

    def stop(self):
        """Stop the module.

        This is an abstract method, you should implement your own.

        """
        raise NotImplementedError('All derivatives of `AbstractModule` must implement a `stop` method.')

    def __init__(self, NITE):
        """Construct an instance of this module."""
        self.NITE = NITE


class ModuleManager:

    """The ModuleManager manages the loading, unloading, etc. of modules."""

    @property
    def NITE(self):
        """Return NITE."""
        return self._NITE

    @NITE.setter
    def NITE(self, value):
        """Set NITE."""
        self._NITE = value

    @property
    def modules(self):
        """Return modules."""
        return self._modules

    @modules.setter
    def modules(self, value):
        """Set modules."""
        self._modules = value

    def load_single(self, identifier):
        """Load a module by its identifier."""
        logger.debug('Attempting to load module "%s"', identifier)

        for entry_point in iter_entry_points(group='nite.modules', name=identifier):
            self.modules[identifier] = entry_point.load()(self.NITE)

        logger.debug('Module "%s" loaded', identifier)

    def unload_single(self, identifier):
        """Unload a module by its identifier."""
        logger.debug('Unloading module "%s"', identifier)

        # We need to remove the module from the python interpreter in order for live codebase updates to work.
        sys.modules.pop(self.modules[identifier].__module__)

        del self.modules[identifier]
        self.modules.pop("", None)
        self.modules.pop(None, None)

        logger.debug('Unloaded module "%s"', identifier)

    def load(self):
        """Load all modules."""
        for entry_point in iter_entry_points(group='nite.modules'):
            self.load_single(entry_point.name)

    def unload(self):
        """Unload all modules."""
        # We need to copy the list of identifiers, because unloading a module removed it from the modules dict
        identifiers = list(self.modules.keys())
        for identifier in identifiers:
            self.unload_single(identifier)

    def start_single(self, identifier):
        """Start a module by its identifier."""
        logger.debug('Starting module "%s"', identifier)
        self.modules[identifier].start()
        logger.debug('Module "%s" started', identifier)

    def stop_single(self, identifier):
        """Stop a module by its identifier."""
        logger.debug('Stopping module "%s"', identifier)
        self.modules[identifier].stop()
        logger.debug('Module "%s" stopped', identifier)

    def start(self):
        """Start all modules."""
        self.load()

        for identifier in self.modules.keys():
            self.start_single(identifier)

    def stop(self):
        """Stop all modules."""
        for identifier in self.modules.keys():
            self.stop_single(identifier)

        self.unload()

    def __init__(self, NITE):
        """Constructor."""
        self.NITE = NITE
        self.modules = {}
        logger.debug('Module manager initialized')

Module module. This class serves as a basis for all NITE modules. Return NITE. Set NITE. Start the module.          This is an abstract method, you should implement your own. Stop the module.          This is an abstract method, you should implement your own. Construct an instance of this module. The ModuleManager manages the loading, unloading, etc. of modules. Return NITE. Set NITE. Return modules. Set modules. Load a module by its identifier. Unload a module by its identifier. # We need to remove the module from the python interpreter in order for live codebase updates to work. Load all modules. Unload all modules. # We need to copy the list of identifiers, because unloading a module removed it from the modules dict Start a module by its identifier. Stop a module by its identifier. Start all modules. Stop all modules. Constructor.