# 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 """Narval RC file : allow control of narval startup, i.e. which cookbooks, modules, elements and protocol handlers will be loaded :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: narvalrc.py 20 2004-04-15 14:43:51Z syt $" __docformat__ = "restructuredtext en" from os.path import normpath, join from cStringIO import StringIO from xml.sax import ContentHandler, make_parser from narval.config import get_home from narval.utils import Singleton class DictSaxHandlerMixin(ContentHandler): """easily construct xml config file parsing by inheriting from this class The subclass shoud define 3 dictionaries, 2 tuples and a string variable which explain the document structure. This handler is initialized with an object which can be handled as a dictionary, and will contain configuration information at the end of parsing. required attributes: :type _MASTER: dict :cvar _MASTER: dictionary designing the first level key for instance, with _MASTER == {'part1':'name', 'part2':None} the handler, when it receives a startElement with name == 'part1', will append to the handler stack pref_o.prefs[attrs.get(_MASTER[name])], where pref_o is the preference object given to __init__ and attrs the attributes dictionary given to startElement. On the other hand, when it receives a startElement with name=='part2', it will append pref_o.prefs[name] to the handler stack. :type _KEY: tuple or list :cvar _KEY: tuple designing last level key :type _LIST: tuple or list :cvar _LIST: tuple designing multiple last level key, same as above, but value will be append to a list :type _CB: dict :cvar _CB: dictionary where key are element of _KEY or _LIST and value function to call when retreiving the element value. All first level key should be predefined in the preferences dictionary """ def __init__(self, pref_o=None): ContentHandler.__init__(self) self._stack = None self._ALL = None self.last_key = None if pref_o is not None: self.init_state(pref_o) def init_state(self, pref_o): """init handler state using the given preference object :type pref_o: dict :param pref_o: the object which will handle the preferences information """ self._stack = [pref_o] self._ALL = {} self.last_key = None for l in (self._MASTER.keys(), self._S_LIST, self._LIST): for e in l: self._ALL[e] = 1 def startElement(self, name, attrs): """SAX callback: start a new xml node :type name: unicode :param name: the tag name :type attrs: dict :param attrs: the node's attribute values, indexed by attribute's name """ self.last_key = None ## if self._MASTER.has_key(name): key = self._MASTER[name] if key: self._stack.append(self._stack[-1].setdefault(attrs.get(key),{})) else: self._stack.append(self._stack[-1].setdefault(name, {})) elif name in self._S_LIST: l = self._stack[-1].setdefault(name, []) l.append({}) self._stack.append(l[-1]) elif name in self._LIST: self.last_key = name self._stack.append(self._stack[-1].setdefault(name, [])) ## if name in self._KEY: self.last_key = name def endElement(self, name): """SAX callback: close a xml node :type name: unicode :param name: the tag name """ if self._ALL.has_key(name): self._stack.pop() def characters(self, content): """SAX callback: get some characters :type content: unicode :param content: the non empty string to hold """ if self.last_key: content = ' '.join(content.split()) if content: key = self.last_key if self._CB.has_key(key): content = self._CB[key](content) if key in self._LIST: self._stack[-1].append(content) else: self._stack[-1][key] = content class PrefReader: """the preference reader : associate a SAX handler to a preference object to be able to load configuration from string / files """ def __init__(self, handler_class, pref_o): self._p = make_parser() self._h = handler_class(pref_o) self._p.setContentHandler(self._h) def from_file(self, path): """read preferences from file :type path: str :param path: path to the preference file """ self._p.parse(open(path)) def from_string(self, string): """read preferences from string :type string: str :param string: XML document containing configuration information """ self._p.parse(StringIO(string)) # configuration object ######################################################### class NarvalRCSaxHandler(DictSaxHandlerMixin): """special SAX handler for narval rc file""" _MASTER = {'enable':None, 'disable':None} _S_LIST = () _LIST = ('interfaces', 'elements', 'actions' , 'cookbook', 'protocol-handler') _KEY = ('encoding', 'package', 'class') _CB = {} class NarvalRC(Singleton): """singleton class for narval rc file information handling, implementing the dictionary interface to access / set configuration parameters """ def __init__(self, filename=None): super(NarvalRC, self).__init__() self._prefs = {'encoding': 'UTF-8', 'disable' : {'actions': [], 'elements' : [], 'protocol-handler' : [], 'interfaces' : [], 'cookbook' : []}, 'enable' : {'actions': [], 'interfaces' : [], 'protocol-handler' : [], 'elements' : [], 'cookbook' : []}, } self.black_list = self._prefs['disable'] self.white_list = self._prefs['enable'] if filename is None: filename = normpath(join(get_home(), 'data', 'narvalrc.xml')) reader = PrefReader(NarvalRCSaxHandler, self) self.mode = 'allow_all' try: reader.from_file(filename) for v in self.white_list.values(): if v: self.mode = 'deny_all' break except IOError: log(LOG_NOTICE, 'no configuration file found, everything will be loaded') def __getitem__(self, key): return self._prefs[key] def __setitem__(self, key, val): self._prefs[key] = val def setdefault(self, key, val): """set the default value for the given key if necessary and return the actual value :type key: unicode or str :param key: dictionary key :type val: unicode :param val: key's default value :rtype: unicode or str :return: the actual key's value """ return self._prefs.setdefault(key, val) def _load(self, category, obj): """return true if the object from the given category should be loaded :type category: str :param category: name of the category ('interfaces', 'elements', 'actions', 'cookbook' or 'protocol-handler') :type obj: str :param obj: name of the object :rtype: bool :return: a flag indicating whether the cookbook should be loaded """ if self.mode == "allow_all": return not obj in self.black_list[category] return obj in self.white_list[category] def load_cookbook(self, cookbook): """return true if the cookbook should be loaded :type cookbook: str :param cookbook: name of the cookbook :rtype: bool :return: a flag indicating whether the cookbook should be loaded """ return self._load('cookbook', cookbook) def load_actions_module(self, module): """return true if the action module should be loaded :type module: str :param module: name of the module :rtype: bool :return: a flag indicating whether the module should be loaded """ return self._load('actions', module) def load_elements_module(self, module): """return true if the elements module should be loaded :type module: str :param module: name of the module :rtype: bool :return: a flag indicating whether the module should be loaded """ return self._load('elements', module) def load_interfaces_module(self, module): """return true if the interfaces module should be loaded :type module: str :param module: name of the module :rtype: bool :return: a flag indicating whether the module should be loaded """ return self._load('interfaces', module) def load_protocol_handler_module(self, module): """return true if the protocol handler module should be loaded :type module: str :param module: name of the module :rtype: bool :return: a flag indicating whether the module should be loaded """ return self._load('protocol-handler', module)