# Copyright (c) 2004-2005 DoCoMo Euro-Labs GmbH (Munich, Germany). # Copyright (c) 2001-2005 LOGILAB S.A. (Paris, FRANCE). # # http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com # http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library 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 # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA """Library of functions to load /reload / save elements, actions, recipes and memory, used by the interpreter and some clients :version: $Revision:$ :author: Logilab :copyright: 2001-2005 LOGILAB S.A. (Paris, FRANCE) 2004-2005 DoCoMo Euro-Labs GmbH (Munich, Germany) :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr http://www.docomolab-euro.com/ -- mailto:tarlano@docomolab-euro.com """ __revision__ = "$Id: bibal.py 20 2004-04-15 14:43:51Z syt $" __docformat__ = "restructuredtext en" import sys import os from os.path import normpath, join, dirname, exists from logilab.common.modutils import load_module_from_name from narval import config from narval.public import AL_NS from narval.narvalrc import NarvalRC def _init_elements(elements, group, path, callback, name=None): """init freshly loaded action / recipe elements :type elements: list :param elements: list of elements to initialize :type group: str :param group: the group of the action / recipe :type load_date: str :param load_date: the date where the element has been loaded, as a string :type callback: function or method :param callback: callback to call to register each element, will be called with the element as argument :type name: str or None :param name: optional name of a particular element that should be searched in the given elements :rtype: narval.public.ALElement or None :return: the element matching if is specified and found """ target = None for elmt in elements: elmt.group = group elmt.from_file = path elmt.load_date = str(os.stat(path)[8]) callback(elmt) if name is not None and elmt.name == name: target = elmt return target # Load elements, actions, recipes and memory ################################### BASEDIR = dirname(__file__) def _python_load(registry, package_name, filter_module_func): """load definitions from python modules in a package :type registry: narval.reader.Registry :param registry: the interpreter's registry :type package_name: str :param registry: the name of the package to load :type filter_module_func: callable :param registry: a function to filter modules to load """ modpath = normpath(join(BASEDIR, package_name)) log(LOG_NOTICE, 'loading %s' % package_name.replace('_', ' ')) for fname in os.listdir(modpath): if not fname.endswith('.py') or fname == '__init__.py' or \ not filter_module_func(fname[:-3]): continue try: mod = load_module_from_name('narval.%s.%s' % (package_name, fname[:-3])) try: mod.register(registry) except AttributeError: registry.autoregister(mod.__name__) except: log(LOG_ERR, '[%s BROKEN]' % fname[:-3]) import traceback traceback.print_exc() log_traceback(LOG_DEBUG, sys.exc_info()) else: log(LOG_NOTICE, '[%s]' % fname[:-3]) def load_interfaces(registry): """load elements definitions from narval home directory (i.e. in the "elements" subdirectory) :type registry: narval.reader.Registry :param registry: the interpreter's registry """ _python_load(registry, 'interfaces', NarvalRC().load_interfaces_module) def load_elements(registry): """load elements definitions from narval home directory (i.e. in the "elements" subdirectory) :type registry: narval.reader.Registry :param registry: the interpreter's registry """ _python_load(registry, 'elements', NarvalRC().load_elements_module) def load_protocol_handlers(engine): """load protocol handlers from narval home directory (i.e. in the 'protocol_handlers' subdirectory) :type registry: `narval.engine.Narval` :param registry: the narval interpreter """ _python_load(engine, 'protocol_handlers', NarvalRC().load_protocol_handler_module) def load_modules(registry, callback): """load all modules registered in narval home (i.e. in the "modules" subdirectory) :type registry: narval.reader.Registry :param registry: the interpreter's registry :type callback: function or method :param callback: callback to call to register each action in modules, will be called with the action element as argument """ #modpath = normpath(join(BASEDIR, package_name)) #modpath = normpath(join(config.get_home(), 'modules')) #m_file, m_filename, m_desc = find_module('modules') package = load_module_from_name('narval.actions') nrc = NarvalRC() log(LOG_NOTICE, 'loading modules') for fname in os.listdir(package.__path__[0]): if not fname.endswith('.py') or fname == '__init__.py' or \ not nrc.load_actions_module(fname[:-3]): continue modname = fname[:-3] try: mod = load_module_from_name('narval.actions.%s' % modname) _init_elements(registry.from_string(mod.MOD_XML, 1), modname, mod.__file__.replace('.pyc', '.py'), callback) except: log(LOG_ERR, '[%s BROKEN]' % modname) log_traceback(LOG_ERR, sys.exc_info()) else: log(LOG_NOTICE, '[%s]' % modname) def reload_module(registry, callback, group, name, from_file): """reload action from module . Actually, all others actions in the module will also be reloaded :type registry: `narval.reader.Registry` :param registry: the interpreter's registry :type callback: function or method :param callback: callback to call to register each action in modules, will be called with the action element as argument :type group: str :param group: the name of the module to reload :type name: str :param name: the name of the action to reload :rtype: `narval.action.ActionElement` :return: the action which has been asked to be reloaded explicitly """ log(LOG_NOTICE, 'reloading module %s from %s', (group, from_file)) mod = load_module_from_name('narval.actions.%s' % group) reload(mod) return _init_elements(registry.from_string(mod.MOD_XML, 1), group, from_file, callback, name) def recipes_path(): """return a list of paths where recipes should be looked for""" devel_dir = join(dirname(__file__), 'share', 'recipes') if exists(devel_dir): result = (devel_dir,) else: result = (normpath(join(config.get_share(), 'recipes')),) home_dir = normpath(join(config.get_home(), 'recipes')) if home_dir not in result: result = (home_dir,) + result return result def load_recipes(registry, callback): """load all recipes registered in narval home (i.e. in the "recipes" subdirectory) :type registry: `narval.reader.Registry` :param registry: the interpreter's registry :type callback: function or method :param callback: callback to call to register each recipe in cookbooks, will be called with the action element as argument """ nrc = NarvalRC() # FIXME: handle overriden recipes ! for recipepath in recipes_path(): log(LOG_NOTICE, 'loading cookbooks from %s', recipepath) for fname in os.listdir(recipepath): if not fname.endswith('.xml') or not nrc.load_cookbook(fname[:-4]): continue try: path = join(recipepath, fname) _init_elements(registry.from_stream(open(path), 1), fname[:-4], path, callback) except: log_traceback(LOG_ERR, sys.exc_info()) else: log(LOG_NOTICE, '[%s]' % fname[:-4]) def reload_recipe(registry, callback, group, name, from_file): """reload recipe from cookbook . Actually, all others recipes in the cookbook will also be reloaded :type registry: `narval.reader.Registry` :param registry: the interpreter's registry :type callback: function or method :param callback: callback to call to register each recipe in cookbooks, will be called with the recipe element as argument :type group: str :param group: the name of the cookbook to reload :type name: str :param name: the name of the recipe to reload :rtype: `narval.recipe.RecipeElement` :return: the recipe which has been asked to be reloaded explicitly """ log(LOG_NOTICE, 'reloading cookbook %s from %s', (group, from_file)) return _init_elements(registry.from_stream(open(from_file), 1), group, from_file, callback, name) def save_recipes(memory, encoding='UTF-8'): """save recipes in memory :type memory: `narval.memory.Memory` :param memory: the narval's main memory :type encoding: str :param encoding: encoding to use in the cookbook files, default to UTF-8 :rtype: list :return: the list of errors which have occured during save """ recipepath = normpath(join(config.get_home(), 'recipes')) files = {} errors = [] for element in memory.get_recipes(): elements = files.setdefault(element.group, []) elements.append(element) for name, elements in files.items() : log(LOG_INFO, 'Saving cookbook %s', name) filename = join(recipepath, '%s.xml' % name) try: stream = open(filename, 'w') except IOError, ex: log(LOG_ERR, 'could not open file %s: %s', (filename, ex)) errors.append(filename) else: write = stream.write write("\n" % encoding) write("\n" % AL_NS) for elmt in elements: write(elmt.as_xml(encoding=encoding)) write("") stream.close() return errors def load_memory(registry, callback, filepath=None): """load the initial narval's main memory (i.e. in the "data/memory.xml" file, unless another path is specified) :type registry: narval.reader.Registry :param registry: the interpreter's registry :type callback: function or method :param callback: callback to call to register each element in the memory file, will be called with the element as argument :type filepath: str or None :param filepath: optional path of the memory file, will load $NARVAL_HOME/data/memory.xml if not specified """ log(LOG_NOTICE, 'loading memory') try: memorypath = filepath or normpath(join(config.get_home(), 'data', 'memory.xml')) for elmt in registry.from_stream(open(memorypath), 1): callback(elmt) except: log_traceback(LOG_ERR, sys.exc_info())