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
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]))