30 raise NotImplementedError() |
30 raise NotImplementedError() |
31 |
31 |
32 def get_lineno(self, element): |
32 def get_lineno(self, element): |
33 raise NotImplementedError() |
33 raise NotImplementedError() |
34 |
34 |
|
35 def get_elem_from_lineno(self, root, lineno): |
|
36 raise NotImplementedError() |
|
37 |
35 class ElementTreeBackendWrapper(ElementTreeBackendWrapperBase): |
38 class ElementTreeBackendWrapper(ElementTreeBackendWrapperBase): |
36 |
39 |
37 class CustomTreeBuilder(ElementTree.TreeBuilder): |
40 class CustomTreeBuilder(ElementTree.TreeBuilder): |
38 """ |
41 """ |
39 Custom TreeBuilder for ElementTree that records line numbers |
42 Custom TreeBuilder for ElementTree that records line numbers |
40 of the elements. |
43 of the elements. |
41 """ |
44 """ |
42 def start(self, tag, attrs): |
45 def start(self, tag, attrs): |
43 elem = ElementTree.TreeBuilder.start(self, tag, attrs) |
46 elem = ElementTree.TreeBuilder.start(self, tag, attrs) |
44 lineno = self._xmltreebuilder._parser.CurrentLineNumber |
47 lineno = self._xmltreebuilder._parser.CurrentLineNumber |
45 #print "Tag: %s, line: %r" % (tag, lineno) |
48 # print "Tag: %s, line: %r" % (tag, lineno) |
46 elem.sourceline = lineno |
49 elem.sourceline = lineno |
47 return elem |
50 return elem |
48 |
51 |
49 def get_module(self): |
52 def get_module(self): |
50 return ElementTree |
53 return ElementTree |
53 try: |
56 try: |
54 treebuilder = self.CustomTreeBuilder() |
57 treebuilder = self.CustomTreeBuilder() |
55 parser = ElementTree.XMLTreeBuilder(target=treebuilder) |
58 parser = ElementTree.XMLTreeBuilder(target=treebuilder) |
56 treebuilder._xmltreebuilder = parser |
59 treebuilder._xmltreebuilder = parser |
57 parser.feed(text) |
60 parser.feed(text) |
58 return parser.close() |
61 self.root = parser.close() |
|
62 return self.root |
59 except expat.ExpatError, e: |
63 except expat.ExpatError, e: |
60 raise exceptions.XmlParseError( |
64 raise exceptions.XmlParseError( |
61 "XML parse error on line %d: %s" % (e.lineno, e), |
65 "XML parse error on line %d: %s" % (e.lineno, e), |
62 e.lineno, str(e)) |
66 e.lineno, str(e)) |
63 |
67 |
64 def tostring(self, etree, encoding=None): |
68 def tostring(self, etree, encoding=None): |
65 return ElementTree.tostring(etree, encoding) |
69 return ElementTree.tostring(etree, encoding) |
66 |
70 |
67 def get_lineno(self, element): |
71 def get_lineno(self, element): |
68 return element.sourceline |
72 try: |
69 |
73 return element.sourceline |
|
74 except AttributeError: |
|
75 return None |
|
76 |
|
77 def get_elem_from_lineno(self, root, lineno): |
|
78 for elem in root.getiterator(): |
|
79 if elem.sourceline == lineno: |
|
80 return elem |
|
81 return None |
70 |
82 |
71 class CElementTreeBackendWrapper(ElementTreeBackendWrapperBase): |
83 class CElementTreeBackendWrapper(ElementTreeBackendWrapperBase): |
72 def __init__(self): |
84 def __init__(self): |
73 try: |
85 try: |
74 from cElementTree import cElementTree |
86 from cElementTree import cElementTree |
80 def get_module(self): |
92 def get_module(self): |
81 return self.cElementTree |
93 return self.cElementTree |
82 |
94 |
83 def fromstring(self, text): |
95 def fromstring(self, text): |
84 try: |
96 try: |
85 return self.cElementTree.fromstring(text) |
97 self.root = self.cElementTree.fromstring(text) |
|
98 return self.root |
86 except SyntaxError, e: |
99 except SyntaxError, e: |
87 # cElementTree raises a SyntaxError, but does not set |
100 # cElementTree raises a SyntaxError, but does not set |
88 # its lineno attribute, so look for the line number |
101 # its lineno attribute, so look for the line number |
89 # in the exception text |
102 # in the exception text |
90 import re |
103 import re |
113 def get_module(self): |
129 def get_module(self): |
114 return self.lxml.etree |
130 return self.lxml.etree |
115 |
131 |
116 def fromstring(self, text): |
132 def fromstring(self, text): |
117 try: |
133 try: |
118 elem = self.lxml.etree.fromstring(text) |
134 self.root = self.lxml.etree.fromstring(text) |
119 |
135 self.remove_comments(self.root) |
120 # lxml parses also comments, but ConE does not expect those, |
136 return self.root |
121 # so remove them to prevent any errors on that account |
|
122 def remove_comments(elem): |
|
123 # Find the comments under this element |
|
124 comments = [] |
|
125 for x in elem: |
|
126 if isinstance(x, self.lxml.etree._Comment): |
|
127 comments.append(x) |
|
128 |
|
129 # Remove them |
|
130 for c in comments: |
|
131 elem.remove(c) |
|
132 |
|
133 # Recurse to sub-elements |
|
134 for subelem in elem: |
|
135 remove_comments(subelem) |
|
136 |
|
137 remove_comments(elem) |
|
138 |
|
139 return elem |
|
140 except self.lxml.etree.XMLSyntaxError, e: |
137 except self.lxml.etree.XMLSyntaxError, e: |
141 raise exceptions.XmlParseError( |
138 raise exceptions.XmlParseError( |
142 "XML parse error on line %d: %s" % (e.position[0], e), |
139 "XML parse error on line %d: %s" % (e.position[0], e), |
143 e.position[0], str(e)) |
140 e.position[0], str(e)) |
144 |
141 |
|
142 def remove_comments(self, elem): |
|
143 """ |
|
144 lxml parses also comments, but ConE does not expect those, |
|
145 so remove them to prevent any errors on that account |
|
146 """ |
|
147 # Find the comments under this element |
|
148 comments = [] |
|
149 for x in elem: |
|
150 if isinstance(x, self.lxml.etree._Comment): |
|
151 comments.append(x) |
|
152 |
|
153 # Remove them |
|
154 for c in comments: |
|
155 elem.remove(c) |
|
156 |
|
157 # Recurse to sub-elements |
|
158 for subelem in elem: |
|
159 self.remove_comments(subelem) |
|
160 |
145 def tostring(self, etree, encoding=None): |
161 def tostring(self, etree, encoding=None): |
146 return self.lxml.etree.tostring(etree, encoding=encoding) |
162 return self.lxml.etree.tostring(etree, encoding=encoding) |
147 |
163 |
148 def get_lineno(self, element): |
164 def get_lineno(self, element): |
149 return element.sourceline |
165 try: |
150 |
166 return element.sourceline |
|
167 except AttributeError: |
|
168 return None |
|
169 |
|
170 def get_elem_from_lineno(self, root, lineno): |
|
171 for elem in root.getiterator(): |
|
172 if elem.sourceline == lineno: |
|
173 return elem |
|
174 return None |
|
175 |
151 # ============================================================================ |
176 # ============================================================================ |
152 # |
177 # |
153 # ============================================================================ |
178 # ============================================================================ |
154 |
179 |
155 class ElementTreeWrapper(object): |
180 class ElementTreeWrapper(object): |
165 BACKEND_LXML = 'lxml' |
190 BACKEND_LXML = 'lxml' |
166 |
191 |
167 # Import order for the default back-end. The list is traversed |
192 # Import order for the default back-end. The list is traversed |
168 # top-down and the first back-end whose importing is successful is |
193 # top-down and the first back-end whose importing is successful is |
169 # used as the default back-end |
194 # used as the default back-end |
170 DEFAULT_BACKEND_IMPORT_ORDER = [BACKEND_C_ELEMENT_TREE, |
195 DEFAULT_BACKEND_IMPORT_ORDER = [BACKEND_LXML, |
|
196 BACKEND_C_ELEMENT_TREE, |
171 BACKEND_ELEMENT_TREE] |
197 BACKEND_ELEMENT_TREE] |
172 |
198 |
173 _backend_mapping = {BACKEND_ELEMENT_TREE: ElementTreeBackendWrapper, |
199 _backend_mapping = {BACKEND_ELEMENT_TREE: ElementTreeBackendWrapper, |
174 BACKEND_C_ELEMENT_TREE: CElementTreeBackendWrapper, |
200 BACKEND_C_ELEMENT_TREE: CElementTreeBackendWrapper, |
175 BACKEND_LXML: LxmlBackendWrapper} |
201 BACKEND_LXML: LxmlBackendWrapper} |
227 Note that for the cElementTree parser this will always return |
253 Note that for the cElementTree parser this will always return |
228 None, since that parser does not support line numbers. |
254 None, since that parser does not support line numbers. |
229 """ |
255 """ |
230 return self._get_backend().get_lineno(element) |
256 return self._get_backend().get_lineno(element) |
231 |
257 |
|
258 def get_elem_from_lineno(self, root, lineno): |
|
259 """ |
|
260 Return the element from the given line number of the given XML element. |
|
261 |
|
262 @param root: the root element to search from |
|
263 @param lineno: the line number to search for |
|
264 |
|
265 Note that for the cElementTree parser this will always return |
|
266 None, since that parser does not support line numbers. |
|
267 """ |
|
268 return self._get_backend().get_elem_from_lineno(root, lineno) |
|
269 |
232 def __getattribute__(self, attrname): |
270 def __getattribute__(self, attrname): |
233 try: |
271 try: |
234 # Try to get the attribute from this object (the top-level wrapper) |
272 # Try to get the attribute from this object (the top-level wrapper) |
235 return object.__getattribute__(self, attrname) |
273 return object.__getattribute__(self, attrname) |
236 except AttributeError: |
274 except AttributeError: |