예제 #1
0
    def _load_module(self, name, classname, classpath, args):
        """
        Module Loader. Loads one module defined by the parameters classname and classpath.
        Parameters defined in the configuration file are passed to this function as 'args'

        :param name: Section name in module configuration file (etc/module.yaml)
        :param classname: Name of the (main) class in the module
        :param classpath: Path to the Python file containing the class
        :param args: Parameter as specified in the configuration file (etc/module.yaml)
        :type name: str
        :type classname: str
        :type classpath: str
        :type args: dict

        :return: loaded module
        :rtype: object
        """
        logger.debug(
            '_load_module: Section {}, Module {}, classpath {}'.format(
                name, classname, classpath))

        enabled = Utils.strip_quotes(args.get('enabled', 'true').lower())
        if enabled == 'false':
            logger.warning(
                "Not loading module {} from section '{}': Module is disabled".
                format(classname, name))
            return

        logger.info("Loading module '{}': args = '{}'".format(name, args))
        # Load an instance of the module
        try:
            exec("import {0}".format(classpath))
        except Exception as e:
            logger.critical(
                "Module '{}' ({}) exception during import of __init__.py: {}".
                format(name, classpath, e))
            return None

        try:
            exec("self.loadedmodule = {0}.{1}.__new__({0}.{1})".format(
                classpath, classname))
        except Exception as e:
            #logger.error("Module '{}' ({}) exception during initialization: {}".format(name, classpath, e))
            pass

        # load module-specific translations
        translation.load_translations('module', classpath.replace('.', '/'),
                                      'module/' + classpath.split('.')[1])

        # get arguments defined in __init__ of module's class to self.args
        try:
            #            exec("self.args = inspect.getargspec({0}.{1}.__init__)[0][1:]".format(classpath, classname))
            exec("self.args = inspect.getfullargspec({0}.{1}.__init__)[0][1:]".
                 format(classpath, classname))
        except Exception as e:
            logger.critical(
                "Module '{}' ({}) exception during call to __init__.py: {}".
                format(name, classpath, e))
            return None
        #logger.warning("- self.args = '{}'".format(self.args))

        # get list of argument used names, if they are defined in the module's class
        logger.info("Module '{}': args = '{}'".format(classname, str(args)))
        arglist = [name for name in self.args if name in args]
        argstring = ",".join(
            ["{}={}".format(name, args[name]) for name in arglist])

        self.loadedmodule._init_complete = False
        (module_params, params_ok,
         hide_params) = self.meta.check_parameters(args)
        if params_ok == True:
            if module_params != {}:
                # initialize parameters the old way
                argstring = ",".join([
                    "{}={}".format(
                        name, "'" + str(module_params.get(name, '')) + "'")
                    for name in arglist
                ])
            # initialize parameters the new way: Define a dict within the instance
            self.loadedmodule._parameters = module_params
            self.loadedmodule._metadata = self.meta

            # initialize the loaded instance of the module
            self.loadedmodule._init_complete = True  # set to false by module, if an initalization error occurs
            exec("self.loadedmodule.__init__(self._sh{0}{1})".format(
                "," if len(arglist) else "", argstring))

        if self.loadedmodule._init_complete == True:
            try:
                code_version = self.loadedmodule.version
            except:
                code_version = None  # if module code without version
            if self.meta.test_version(code_version):
                logger.info(
                    "Modules: Loaded module '{}' (class '{}') v{}: {}".format(
                        name, str(self.loadedmodule.__class__.__name__),
                        self.meta.get_version(),
                        self.meta.get_mlstring('description')))
                self._moduledict[name] = self.loadedmodule
                self._modules.append(self._moduledict[name])
                return self.loadedmodule
            else:
                logger.error(
                    f"Module {name} not started: Module version mismatch")
                return None
        else:
            logger.error(
                "Modules: Module '{}' initialization failed, module not loaded"
                .format(classpath.split('.')[1]))
            return None
예제 #2
0
    def __init__(self, smarthome, name, classname, classpath, args, instance,
                 meta, configfile):
        """
        Initialization of wrapper class
        """
        logger.debug(
            'PluginWrapper __init__: Section {}, classname {}, classpath {}'.
            format(name, classname, classpath))
        threading.Thread.__init__(self, name=name)

        self._init_complete = False
        self.meta = meta
        # Load an instance of the plugin
        try:
            exec("import {0}".format(classpath))
        except ImportError as e:
            logger.error(
                "Plugin '{}' error importing Python package: {}".format(
                    name, e))
            logger.error(
                "Plugin '{}' initialization failed, plugin not loaded".format(
                    name))
            return
        except Exception as e:
            logger.exception(
                "Plugin '{}' exception during import of __init__.py: {}".
                format(name, e))
            return
        try:
            exec("self.plugin = {0}.{1}.__new__({0}.{1})".format(
                classpath, classname))
        except Exception as e:
            logger.error(
                "Plugin '{}' class name '{}' defined in metadata, but not found in plugin code"
                .format(name, classname))
            logger.error(
                "Plugin '{}' initialization failed, plugin not loaded".format(
                    name))
            return

        # load plugin-specific translations
        self._ptrans = translation.load_translations(
            'plugin', classpath.replace('.', '/'),
            'plugin/' + classpath.split('.')[1])

        # make the plugin a method/function of the main smarthome object  (MS: Ist das zu früh? Falls Init fehlschlägt?)
        #        setattr(smarthome, self.name, self.plugin)
        if self.meta.get_string('state') == 'deprecated':
            logger.warning(
                "Plugin '{}' (section '{}') is deprecated. Consider to use a replacement instead"
                .format(classpath.split('.')[1], name))
        # initialize attributes of the newly created plugin object instance
        if isinstance(self.get_implementation(), SmartPlugin):
            self.get_implementation()._configfilename = configfile
            self.get_implementation()._set_configname(name)
            #            self.get_implementation()._config_section = name
            self.get_implementation()._set_shortname(
                str(classpath).split('.')[1])
            self.get_implementation()._classpath = classpath
            self.get_implementation()._set_classname(classname)
            # if instance != '' war auskommentiert, reaktiviert: 26.01.2018 MS
            if self.get_implementation().ALLOW_MULTIINSTANCE is None:
                self.get_implementation(
                ).ALLOW_MULTIINSTANCE = self.meta.get_bool('multi_instance')
            if instance != '':
                logger.debug("set plugin {0} instance to {1}".format(
                    name, instance))
                self.get_implementation()._set_instance_name(instance)
#           addition by smai
# Customized logger instance for plugin to append name of plugin instance to log text
            self.get_implementation().logger = PluginLoggingAdapter(
                logging.getLogger(classpath), {
                    'plugininstance':
                    self.get_implementation().get_loginstance()
                })
            #           end addition by smai
            self.get_implementation()._set_sh(smarthome)
            self.get_implementation()._set_plugin_dir(
                os.path.join(
                    os.path.dirname(os.path.dirname(
                        os.path.abspath(__file__))),
                    classpath.replace('.', os.sep)))
            self.get_implementation()._plgtype = self.meta.get_string('type')
            self.get_implementation().metadata = self.meta
        else:
            # classic plugin
            #            self.get_implementation()._config_section = name
            self.get_implementation()._configfilename = configfile
            self.get_implementation()._configname = name
            self.get_implementation()._shortname = str(classpath).split('.')[1]
            self.get_implementation()._classpath = classpath
            self.get_implementation()._classname = classname
            self.get_implementation()._plgtype = ''
        self.get_implementation()._itemlist = []

        # get arguments defined in __init__ of plugin's class to self.args
        #        exec("self.args = inspect.getargspec({0}.{1}.__init__)[0][1:]".format(classpath, classname))
        exec("self.args = inspect.getfullargspec({0}.{1}.__init__)[0][1:]".
             format(classpath, classname))
        #logger.warning("- self.args = '{}'".format(self.args))

        # get list of argument used names, if they are defined in the plugin's class
        logger.debug("Plugin '{}': args = '{}'".format(classname, str(args)))
        arglist = [name for name in self.args if name in args]
        argstring = ",".join(
            ["{}={}".format(name, args[name]) for name in arglist])
        #        logger.debug("Plugin '{}' using arguments {}".format(str(classpath).split('.')[1], arglist))

        self.get_implementation()._init_complete = False
        (plugin_params, params_ok,
         hide_params) = self.meta.check_parameters(args)
        if params_ok == True:
            if plugin_params != {}:
                # initialize parameters the old way
                argstring = ",".join([
                    "{}={}".format(
                        name, '"' + str(plugin_params.get(name, '')) + '"')
                    for name in arglist
                ])
            # initialize parameters the new way: Define a dict within the instance
            self.get_implementation()._parameters = plugin_params
            self.get_implementation()._hide_parameters = hide_params
            self.get_implementation()._metadata = self.meta

            # initialize the loaded instance of the plugin
            self.get_implementation(
            )._init_complete = True  # set to false by plugin, if an initalization error occurs

            # initialize the loaded instance of the plugin
            exec("self.plugin.__init__(smarthome{0}{1})".format(
                "," if len(arglist) else "", argstring))

            # set level to make logger appear in internal list of loggers (if not configured by logging.yaml)
            try:  # skip classic plugins
                if self.get_implementation().logger.level == 0:
                    self.get_implementation().logger.setLevel('WARNING')
            except:
                pass

        # set the initialization complete status for the wrapper instance
        self._init_complete = self.get_implementation()._init_complete
        if self.get_implementation()._init_complete == True:
            # make the plugin a method/function of the main smarthome object  (MS: Ist das zu früh? Falls Init fehlschlägt?)
            setattr(smarthome, self.name, self.plugin)
            try:
                code_version = self.get_implementation().PLUGIN_VERSION
            except:
                code_version = None  # if plugin code without version
            if isinstance(self.get_implementation(), SmartPlugin):
                if self.meta.test_version(code_version):
                    # set version in plugin instance (if not defined in code)
                    if code_version == None:
                        self.get_implementation(
                        ).PLUGIN_VERSION = self.meta.get_version()
                    # set multiinstance in plugin instance (if not defined in code)
                    try:
                        dummy = self.get_implementation().ALLOW_MULTIINSTANCE
                    except:
                        #                        logger.warning("self.meta.get_bool('multi_instance') = {}".format(self.meta.get_bool('multi_instance')))
                        self.get_implementation(
                        ).ALLOW_MULTIINSTANCE = self.meta.get_bool(
                            'multi_instance')
#                        logger.warning("get_implementation().ALLOW_MULTIINSTANCE = {}".format(self.get_implementation().ALLOW_MULTIINSTANCE))
                    if not self.get_implementation(
                    )._set_multi_instance_capable(
                            self.meta.get_bool('multi_instance')):
                        logger.error(
                            "Plugins: Loaded plugin '{}' ALLOW_MULTIINSTANCE differs between metadata ({}) and Python code ({})"
                            .format(
                                name, self.meta.get_bool('multi_instance'),
                                self.get_implementation().ALLOW_MULTIINSTANCE))
                    logger.debug(
                        "Plugins: Loaded plugin '{}' (class '{}') v{}: {}".
                        format(
                            name,
                            str(self.get_implementation().__class__.__name__),
                            self.meta.get_version(),
                            self.meta.get_mlstring('description')))
            else:
                logger.debug(
                    "Plugins: Loaded classic-plugin '{}' (class '{}')".format(
                        name,
                        str(self.get_implementation().__class__.__name__)))
            if instance != '':
                logger.debug("set plugin {0} instance to {1}".format(
                    name, instance))
                self.get_implementation()._set_instance_name(instance)
        else:
            logger.error(
                "Plugin '{}' initialization failed, plugin not loaded".format(
                    classpath.split('.')[1]))