1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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
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 """
46
49
52
53
55 """
56 Error during parsing of XML.
57
58 args[0]: str
59 """
60
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
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
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
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
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
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