Package flumotion :: Package common :: Module fxml
[hide private]

Source Code for Module flumotion.common.fxml

  1  # -*- Mode: Python; test-case-name: flumotion.test.test_registry -*- 
  2  # vi:si:et:sw=4:sts=4:ts=4 
  3  # 
  4  # Flumotion - a streaming media server 
  5  # Copyright (C) 2006 Fluendo, S.L. (www.fluendo.com). 
  6  # All rights reserved. 
  7   
  8  # This file may be distributed and/or modified under the terms of 
  9  # the GNU General Public License version 2 as published by 
 10  # the Free Software Foundation. 
 11  # This file is distributed without any warranty; without even the implied 
 12  # warranty of merchantability or fitness for a particular purpose. 
 13  # See "LICENSE.GPL" in the source distribution for more information. 
 14   
 15  # Licensees having purchased or holding a valid Flumotion Advanced 
 16  # Streaming Server license may use this file in accordance with the 
 17  # Flumotion Advanced Streaming Server Commercial License Agreement. 
 18  # See "LICENSE.Flumotion" in the source distribution for more information. 
 19   
 20  # Headers in this file shall remain intact. 
 21   
 22  """ 
 23  Common routines to parsing XML. 
 24   
 25  Flumotion deals with two basic kinds of XML: config and registry. They 
 26  correspond to data and schema, more or less. This file defines some base 
 27  parsing routines shared between both kinds of XML. 
 28  """ 
 29   
 30  import sets 
 31   
 32  from xml.dom import minidom, Node 
 33  from xml.parsers import expat 
 34   
 35  from flumotion.common import log, common 
 36   
37 -class Box:
38 """ 39 Object designed to wrap, or "box", any value. Useful mostly in the 40 context of the table-driven XML parser, so that a handler that wants 41 to set a scalar value can do so, getting around the limitations of 42 Python's lexical scoping. 43 """
44 - def __init__(self, val=None):
45 self.set(val)
46
47 - def set(self, val):
48 self.val = val
49
50 - def unbox(self):
51 return self.val
52 53
54 -class ParserError(Exception):
55 """ 56 Error during parsing of XML. 57 58 args[0]: str 59 """
60
61 -class Parser(log.Loggable):
62 """ 63 XML parser base class. 64 65 I add some helper functions for specialized XML parsers, mostly the 66 parseFromTable method. 67 68 I am here so that the config parser and the registry parser can 69 share code. 70 """ 71
72 - def getRoot(self, file):
73 """ 74 Return the root of the XML tree for the the string or filename 75 passed as an argument. Raises fxml.ParserError if the XML could 76 not be parsed. 77 78 @param file: An open file object, or the name of a file. Note 79 that if you pass a file object, this function will leave the 80 file open. 81 @type file: File object; can be a duck file like StringIO. 82 Alternately, the path of a file on disk. 83 """ 84 self.debug('Parsing XML from %r', file) 85 try: 86 return minidom.parse(file) 87 except expat.ExpatError, e: 88 raise ParserError('Error parsing XML from %r: %s' % ( 89 file, log.getExceptionMessage(e)))
90
91 - def checkAttributes(self, node, required=None, optional=None):
92 """ 93 Checks that a given XML node has all of the required attributes, 94 and no unknown attributes. Raises fxml.ParserError if unknown 95 or missing attributes are detected. 96 97 @param node: An XML DOM node. 98 @type node: L{xml.dom.Node} 99 @param required: Set of required attributes, or None. 100 @type required: Sequence (list, tuple, ...) of strings. 101 @param optional: Set of optional attributes, or None. 102 @type optional: Sequence (list, tuple, ...) of strings. 103 """ 104 attrs = sets.Set(node.attributes.keys()) 105 required = sets.Set(required or ()) 106 optional = sets.Set(optional or ()) 107 for x in attrs - required.union(optional): 108 raise ParserError("Unknown attribute in <%s>: %s" 109 % (node.nodeName, x)) 110 for x in required - attrs: 111 raise ParserError("Missing attribute in <%s>: %s" 112 % (node.nodeName, x))
113
114 - def parseAttributes(self, node, required=None, optional=None):
115 """ 116 Checks the validity of the attributes on an XML node, via 117 Parser.checkAttributes, then parses them out and returns them 118 all as a tuple. 119 120 @param node: An XML DOM node. 121 @type node: L{xml.dom.Node} 122 @param required: Set of required attributes, or None. 123 @type required: Sequence (list, tuple, ...) of strings. 124 @param optional: Set of optional attributes, or None. 125 @type optional: Sequence (list, tuple, ...) of strings. 126 127 @returns: List of all attributes as a tuple. The first element 128 of the returned tuple will be the value of the first required 129 attribute, the second the value of the second required 130 attribute, and so on. The optional attributes follow, with None 131 as the value if the optional attribute was not present. 132 @rtype: tuple of string or None, as long as the combined length 133 of the required and optional attributes. 134 """ 135 self.checkAttributes(node, required, optional) 136 out = [] 137 for k in (required or ()) + (optional or ()): 138 if node.hasAttribute(k): 139 # expat always gives us unicode; we always want str 140 a = node.getAttribute(k) 141 if a: 142 out.append(str(a)) 143 else: 144 out.append(None) 145 else: 146 out.append(None) 147 return out
148
149 - def parseFromTable(self, parent, parsers):
150 """ 151 A data-driven verifying XML parser. Raises fxml.ParserError if 152 an unexpected child node is encountered. 153 154 @param parent: An XML node whose child nodes you are interested 155 in parsing. 156 @type parent: L{xml.dom.Node} 157 @param parsers: A parse table defining how to parse the child 158 nodes. The keys are the possible child nodes, and the value is a 159 two-tuple of how to parse them consisting of a parser and a 160 value handler. The parser is a one-argument function that will 161 be called with the child node as an argument, and the handler is 162 a one-argument function that will be called with the result of 163 calling the parser. 164 @type parsers: dict of string -> (function, function) 165 """ 166 for child in parent.childNodes: 167 if (child.nodeType == Node.TEXT_NODE or 168 child.nodeType == Node.COMMENT_NODE): 169 continue 170 try: 171 parser, handler = parsers[child.nodeName] 172 except KeyError: 173 raise ParserError("unexpected node in <%s>: %s" 174 % (parent.nodeName, child)) 175 handler(parser(child))
176