<commit_before># -*- coding: utf-8 -*-
#
#  NAZS
#  Copyright (C) 2014 <NAME> <<EMAIL>>
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Affero General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU Affero General Public License for more details.
#
#  You should have received a copy of the GNU Affero General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

import logging
import collections

from django.db.models import get_app, get_models
from abc import ABCMeta

from .models import ModuleInfo
from .catalog import NAZSInterface
from .files import ConfFile
from .daemons import Daemon
from .signals import (pre_install, post_install,
                      pre_enable, post_enable,
                      pre_disable, post_disable,
                      pre_save, post_save)


logger = logging.getLogger(__name__)


class ModuleMeta(ABCMeta):
    """
    Module metaclass, to allow some black magic tricks on module definitions
    """
    # Modules instances
    MODULES = collections.OrderedDict()

    def __new__(meta, name, bases, dct):
        # Construct the class
        cls = super(ModuleMeta, meta).__new__(meta, name, bases, dct)

        # Ignore base class
        if object in bases:
            return cls

        # Wrap enable event signals
        cls.install = meta.install_wrapper(cls.install, cls)
        cls.enable = meta.enable_wrapper(cls.enable, cls)
        cls.disable = meta.disable_wrapper(cls.disable, cls)
        cls.save = meta.save_wrapper(cls.save, cls)

        if cls not in ModuleMeta.MODULES:
            ModuleMeta.MODULES[cls] = None
        return cls

    def __call__(cls, *args, **kw):
        if cls not in ModuleMeta.MODULES or ModuleMeta.MODULES[cls] is None:
            ModuleMeta.MODULES[cls] = \
                super(ModuleMeta, cls).__call__(*args, **kw)
        return ModuleMeta.MODULES[cls]

    @classmethod
    def install_wrapper(cls, install, new_class):
        """
        Wrap the install method to call pre and post enable signals and update
        module status
        """
        def _wrapped(self, *args, **kwargs):
            if self.installed:
                raise AssertionError('Module %s is already installed'
                                     % self.name)

            logger.info("Installing %s module" % self.name)
            pre_install.send(sender=self)
            res = install(self, *args, **kwargs)
            post_install.send(sender=self)

            info = self._info
            info.status = ModuleInfo.DISABLED
            info.save()

            logger.info("Installed %s module" % self.name)
            return res

        return _wrapped

    @classmethod
    def enable_wrapper(cls, enable, new_class):
        """
        Wrap the enable method to call pre and post enable signals and update
        module status
        """
        def _wrapped(self, *args, **kwargs):
            if not self.installed:
                raise AssertionError('Module %s cannot be enabled'
                                     ', you should install it first'
                                     % self.name)

            if self.enabled:
                raise AssertionError('Module %s is already enabled'
                                     % self.name)

            logger.info("Enabling %s module" % self.name)
            pre_enable.send(sender=self)
            res = enable(self, *args, **kwargs)

            # Register interfaces (if present)
            if isinstance(self, NAZSInterface):
                self.register()

            post_enable.send(sender=self)

            info = self._info
            info.status = ModuleInfo.ENABLED
            info.save()

            logger.info("Enabled %s module" % self.name)
            return res

        return _wrapped

    @classmethod
    def save_wrapper(cls, save, new_class):
        """
        Wrap the save method to call pre and post enable signals and update
        module status
        """
        def _wrapped(self, *args, **kwargs):
            if not self.installed:
                raise AssertionError('Module %s is not installed' % self.name)

            logger.info("Saving %s module" % self.name)
            pre_save.send(sender=self)
            res = save(self, *args, **kwargs)
            post_save.send(sender=self)
            logger.info("Saved %s module" % self.name)

            return res

        return _wrapped

    @classmethod
    def disable_wrapper(cls, disable, new_class):
        """
        Wrap the disable method to call pre and post disable signals and update
        module status
        """
        def _wrapped(self, *args, **kwargs):
            if not self.enabled:
                raise AssertionError('Module %s is already disabled'
                                     % self.name)

            logger.info("Disabling %s module" % self.name)
            pre_disable.send(sender=self)
            res = disable(self, *args, **kwargs)

            # Unregister interfaces (if present)
            if isinstance(self, NAZSInterface):
                self.unregister()

            post_disable.send(sender=self)

            info = self._info
            info.status = ModuleInfo.DISABLED
            info.save()

            logger.info("Disabled %s module" % self.name)
            return res

        return _wrapped


class Module(object):
    """
    NAZS module, implements everything needed to provide a feature.

    Module lifecycle
    ----------------<commit_msg>Use modules verbose_name instead of name for logs<commit_after># -*- coding: utf-8 -*-
#
#  NAZS
#  Copyright (C) 2014 <NAME> <<EMAIL>>
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU Affero General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU Affero General Public License for more details.
#
#  You should have received a copy of the GNU Affero General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

import logging
import collections

from django.db.models import get_app, get_models
from abc import ABCMeta

from .models import ModuleInfo
from .catalog import NAZSInterface
from .files import ConfFile
from .daemons import Daemon
from .signals import (pre_install, post_install,
                      pre_enable, post_enable,
                      pre_disable, post_disable,
                      pre_save, post_save)


logger = logging.getLogger(__name__)


class ModuleMeta(ABCMeta):
    """
    Module metaclass, to allow some black magic tricks on module definitions
    """
    # Modules instances
    MODULES = collections.OrderedDict()

    def __new__(meta, name, bases, dct):
        # Construct the class
        cls = super(ModuleMeta, meta).__new__(meta, name, bases, dct)

        # Ignore base class
        if object in bases:
            return cls

        # Wrap enable event signals
        cls.install = meta.install_wrapper(cls.install, cls)
        cls.enable = meta.enable_wrapper(cls.enable, cls)
        cls.disable = meta.disable_wrapper(cls.disable, cls)
        cls.save = meta.save_wrapper(cls.save, cls)

        if cls not in ModuleMeta.MODULES:
            ModuleMeta.MODULES[cls] = None
        return cls

    def __call__(cls, *args, **kw):
        if cls not in ModuleMeta.MODULES or ModuleMeta.MODULES[cls] is None:
            ModuleMeta.MODULES[cls] = \
                super(ModuleMeta, cls).__call__(*args, **kw)
        return ModuleMeta.MODULES[cls]

    @classmethod
    def install_wrapper(cls, install, new_class):
        """
        Wrap the install method to call pre and post enable signals and update
        module status
        """
        def _wrapped(self, *args, **kwargs):
            if self.installed:
                raise AssertionError('Module %s is already installed'
                                     % self.verbose_name)

            logger.info("Installing %s module" % self.verbose_name)
            pre_install.send(sender=self)
            res = install(self, *args, **kwargs)
            post_install.send(sender=self)

            info = self._info
            info.status = ModuleInfo.DISABLED
            info.save()

            logger.info("Installed %s module" % self.verbose_name)
            return res

        return _wrapped

    @classmethod
    def enable_wrapper(cls, enable, new_class):
        """
        Wrap the enable method to call pre and post enable signals and update
        module status
        """
        def _wrapped(self, *args, **kwargs):
            if not self.installed:
                raise AssertionError('Module %s cannot be enabled'
                                     ', you should install it first'
                                     % self.verbose_name)

            if self.enabled:
                raise AssertionError('Module %s is already enabled'
                                     % self.verbose_name)

            logger.info("Enabling %s module" % self.verbose_name)
            pre_enable.send(sender=self)
            res = enable(self, *args, **kwargs)

            # Register interfaces (if present)
            if isinstance(self, NAZSInterface):
                self.register()

            post_enable.send(sender=self)

            info = self._info
            info.status = ModuleInfo.ENABLED
            info.save()

            logger.info("Enabled %s module" % self.verbose_name)
            return res

        return _wrapped

    @classmethod
    def save_wrapper(cls, save, new_class):
        """
        Wrap the save method to call pre and post enable signals and update
        module status
        """
        def _wrapped(self, *args, **kwargs):
            if not self.installed:
                raise AssertionError('Module %s is not installed' % self.verbose_name)

            logger.info("Saving %s module" % self.verbose_name)
            pre_save.send(sender=self)
            res = save(self, *args, **kwargs)
            post_save.send(sender=self)
            logger.info("Saved %s module" % self.verbose_name)

            return res

        return _wrapped

    @classmethod
    def disable_wrapper(cls, disable, new_class):
        """
        Wrap the disable method to call pre and post disable signals and update
        module status
        """
        def _wrapped(self, *args, **kwargs):
            if not self.enabled:
                raise AssertionError('Module %s is already disabled'
                                     % self.verbose_name)

            logger.info("Disabling %s module" % self.verbose_name)
            pre_disable.send(sender=self)
            res = disable(self, *args, **kwargs)

            # Unregister interfaces (if present)
            if isinstance(self, NAZSInterface):
                self.unregister()

            post_disable.send(sender=self)

            info = self._info
            info.status = ModuleInfo.DISABLED
            info.save()

            logger.info("Disabled %s module" % self.verbose_name)
            return res

        return _wrapped


class Module(object):
    """
    NAZS module, implements everything needed to provide a feature.

    Module lifecycle
    ----------------
{'fraction_non_alphanumeric': 0.08469164846244329, 'fraction_numerical': 0.001008233910267182, 'mean_word_length': 1.930329886755293, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4067526', 'n_tokens_mistral': 3167, 'n_tokens_neox': 3083, 'n_words': 1013}