# Copyright (c) 2002-2004 LOGILAB S.A. (Paris, FRANCE). # http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 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 General Public License for more details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. """ module to easily translate xml file to dictionary according to simples rules """ __revision__ = "$Id: xmlconf.py,v 1.4 2003/12/10 15:42:09 syt Exp $" from xml.sax import ContentHandler, make_parser ## standard handler for configuration files ################################### 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 dictionary: * _ROOT , the root element name * _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. * _SUB, dictionary designing the deeper level key. for instance, with _SUB == {'entry':'name', 'state':None} the handler, when it receives a startElement with name == 'entry', will append to the handler stack stack[-1].setdefault(attrs.get(_SUB[name]),{}) where stack[-1] is the last elmeent on the stack. As _MASTER, when it receives a startElement with name=='state', it will append stack[-1].setdefault(name,{}) to the handler stack. * _KEY, tuple designing last level key * _LIST, tuple designing multiple last level key, same as above, but value will be append to a list. * _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 preference object dictionary ("prefs") For example see narval.NarvalRC or narval.gui.PrefModel """ def __init__(self, pref_o=None): ContentHandler.__init__(self) if pref_o is not None: self.initState(pref_o) def initState(self, pref_o): """initialize the sax handler""" 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): """See SAX ContentHandler interface definition """ self.last_key = None if self._MASTER.has_key(name): key = self._MASTER[name] if key: value = attrs.get(key) self._stack.append(self._stack[-1].setdefault(value, {})) 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): """See SAX ContentHandler interface definition """ if self._ALL.has_key(name): self._stack.pop() def characters(self, ch): """See SAX ContentHandler interface definition """ key = self.last_key if key: ch = ' '.join(ch.split()) if len(ch) > 0: if self._CB.has_key(key): ch = self._CB[key](ch) if key in self._LIST: self._stack[-1].append(ch) else: self._stack[-1][key] = ch ## Reader #################################################################### class PrefReader: """xml config sax based reader""" def __init__(self, handler_class, pref_o): self._p = make_parser() self._h = handler_class(pref_o) self._p.setContentHandler(self._h) #from os.path import normpath, join #cat_path = normcase(normpath(join(config.get_home(),'dtd/CATALOG'))) def fromFile(self, filename): """ read config from file to pref_o """ f = open(filename) self._p.parse(f) f.close() def fromString(self, string): """ read config from string to pref_o """ from cStringIO import StringIO buf = StringIO(string) self._p.parse(buf) buf.close()