root/trunk/proj/w3c/xmlParserContext.py

Revision 362, 7.6 kB (checked in by sholloway, 5 years ago)

Implementation of #37: "CSS Errors with filename and line number"

Line 
1 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2 ##~ Copyright (C) 2002-2004  TechGame Networks, LLC.
3 ##~
4 ##~ This library is free software; you can redistribute it and/or
5 ##~ modify it under the terms of the BSD style License as found in the
6 ##~ LICENSE file included with this distribution.
7 ##~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8
9 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
10 #~ Imports
11 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12
13 from xml.parsers import expat as _expat
14
15 from TG.introspection.stack import traceSrcrefExec, traceSrcrefEval
16
17 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
18 #~ Parse Context and Parse Command
19 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
20
21 class ParseContextBase(object):
22     def getURI(self):
23         return self.getFilename()
24     def getEncoding(self):
25         return None
26     def getFilenameAndOffset(self):
27         return (self.getFilename(), self.getLineOffset())
28     def getFilename(self):
29         raise NotImplementedError('Subclass Responsibility: %r' % (self,))
30     def getLineOffset(self):
31         return 0
32
33 class ParseContext(ParseContextBase):
34     "A practical implementation of ParseContextBase"
35     encoding = None
36     uri = None
37     filename = None
38     lineOffset = 0
39
40     def getURI(self):
41         if self.uri is not None:
42             return self.uri
43         else:
44             return self.getFilename()
45     def setURI(self, uri):
46         self.uri = uri
47
48     def getEncoding(self):
49         return self.encoding
50     def setEncoding(self, encoding):
51         self.encoding = encoding
52
53     def getFilename(self):
54         return self.filename
55     def setFilename(self, filename):
56         self.filename = filename
57
58     def getLineOffset(self):
59         return self.lineOffset
60     def setLineOffset(self, lineOffset=0):
61         self.lineOffset = lineOffset
62
63     def getFilenameAndOffset(self):
64         return self.getFilename(), self.getLineOffset()
65     def setFilenameAndOffset(self, filename, lineOffset=0):
66         self.setFilename(filename)
67         self.setLineOffset(lineOffset)
68
69 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
70
71 class ParserCmdBase(object):
72     encoding = None
73     _parseContext = None
74     _sourceFilename = None
75     _sourceLineOffset = 0
76
77     def __init__(self, host, xmlSource, parserInterface, parseContext):
78         """Creates the Expat parser in a composed way. 
79
80         parserInterface:
81             'data': returns a callable that takes a string of the XML document
82             'file': returns a callable that takes a file-like interface to the XML document
83             'raw': returns the raw parser object
84         """
85         self.xmlSource = xmlSource
86
87         if (parseContext is None) and isinstance(xmlSource, ParseContextBase):
88             parseContext = xmlSource
89
90         self.setContext(parseContext)
91         self.parser = self._createParserOnHost(host, parserInterface)
92         self.configureParser(parseContext, parserInterface)
93
94     #~ Parse Context delegation ~~~~~~~~~~~~~~~~~~~~~~~~~~
95
96     def getContext(self):
97         return self._parseContext
98     def setContext(self, parseContext):
99         self._parseContext = parseContext
100         if parseContext is not None:
101             self.setEncoding(parseContext.getEncoding())
102             self.setSourceFilenameAndOffset(*parseContext.getFilenameAndOffset())
103
104     #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
105
106     def getURI(self):
107         parseContext = self.getContext()
108         if parseContext:
109             return parseContext.getURI()
110         else:
111             return None
112
113     #~ cached variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
114
115     def getSourceFilename(self):
116         return self._sourceFilename
117     def getSourceLineOffset(self):
118         return self._sourceLineOffset
119     def getSourceFilenameAndOffset(self):
120         return (self._sourceFilename, self._sourceLineOffset)
121     def setSourceFilenameAndOffset(self, filename, lineOffset=0):
122         self._sourceFilename = filename
123         self._sourceLineOffset = lineOffset
124
125     def getSourceLineNumber(self, offset=0):
126         return self._sourceLineOffset + self.getParserLineNumber() + offset
127     def getSourceFilenameAndLineNumber(self, offset=0):
128         return (self.getSourceFilename(), self.getSourceLineNumber(offset))
129
130     def getEncoding(self, default=None):
131         return self.encoding
132     def setEncoding(self, encoding):
133         self.encoding = encoding
134
135     #~ Parser supplied methods ~~~~~~~~~~~~~~~~~~~~~~~~~~
136
137     def configureParser(self, parseContext):
138         raise NotImplementedError('Subclass Responsibility: %r' % (self,))
139
140     def __call__(self, xmlSource):
141         raise NotImplementedError('Subclass Responsibility: %r' % (self,))
142
143     def getParserLineNumber(self):
144         raise NotImplementedError('Subclass Responsibility: %r' % (self,))
145
146 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
147 #~ Expat XML Parser Cmd
148 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
149
150 class AdvExpatError(_expat.ExpatError):
151     def __init__(self, expatError, parseCmd):
152         self.expatError = expatError
153         self.srcref = parseCmd.getSourceFilenameAndLineNumber()
154
155     def __str__(self):
156         errMsg = str(self.expatError)
157         errMsg =  errMsg[:errMsg.rfind(': line')]
158         return '%s: in File "%s" line %s, column %s' % (errMsg, self.srcref[0], self.srcref[1], self.expatError.offset)
159
160     def raiseFromSrc(self):
161         global traceSrcrefExec
162         traceSrcrefExec(self.srcref, 'raise xmlError', xmlError=self)
163
164 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
165
166 class ExpatParserCmd(ParserCmdBase):
167     def _createParserOnHost(self, host, parserInterface):
168         encoding = self.getEncoding()
169         parser = _expat.ParserCreate(encoding, host.seperator)
170         parser.returns_unicode = encoding and (encoding.lower() != 'ascii')
171
172         # hook the parse callbacks
173         parser.buffer_text = True
174         parser.StartElementHandler = self._dynamicCallTo(host._startElement)
175         parser.EndElementHandler = self._dynamicCallTo(host._endElement)
176         parser.CharacterDataHandler = self._dynamicCallTo(host._charData)
177         parser.CommentHandler = self._dynamicCallTo(host._commentData)
178         parser.StartNamespaceDeclHandler = self._dynamicCallTo(host._startNamespaceDeclHandler)
179         parser.EndNamespaceDeclHandler = self._dynamicCallTo(host._endNamespaceDeclHandler)
180
181         return parser
182
183     def _dynamicCallTo(self, callback):
184         if self.getContext() is None:
185             return callback
186         def dynStackCB(*args, **kw):
187             global traceSrcrefEval
188             srcref = self.getSourceFilenameAndLineNumber()
189             return traceSrcrefEval(srcref, 'expatCB(*args, **kw)', args=args, kw=kw, expatCB=callback)
190         return dynStackCB
191
192     def __call__(self):
193         try:
194             if self._parserInterface == 'file':
195                 return self.parser.ParseFile(self.xmlSource)
196             elif self._parserInterface == 'data':
197                 return self.parser.Parse(unicode(self.xmlSource), False)
198             elif self._parserInterface == 'raw':
199                 return self.parser(self.xmlSource)
200             else:
201                 raise ValueError('Invalid parserInterface: %r' % (self._parserInterface,))
202         except _expat.ExpatError, err:
203             AdvExpatError(err, self).raiseFromSrc()
204
205     def getParserLineNumber(self):
206         return self.parser.ErrorLineNumber
207
208     def getParserColumnNumber(self):
209         return self.parser.ErrorColumnNumber
210
211     #~ parseContext ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
212
213     def configureParser(self, parseContext, parserInterface):
214         self._parserInterface = parserInterface
215         uri = unicode(self.getURI() or u'')
216         self.parser.SetBase(uri)
217
Note: See TracBrowser for help on using the browser.