~divmod-dev/divmod.org/trunk

« back to all changes in this revision

Viewing changes to Nevow/nevow/flat/flatsax.py

  • Committer: Jean-Paul Calderone
  • Date: 2014-06-29 20:33:04 UTC
  • mfrom: (2749.1.1 remove-epsilon-1325289)
  • Revision ID: exarkun@twistedmatrix.com-20140629203304-gdkmbwl1suei4m97
mergeĀ lp:~exarkun/divmod.org/remove-epsilon-1325289

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (c) 2004-2009 Divmod.
2
 
# See LICENSE for details.
3
 
 
4
 
from xml.sax import make_parser, handler
5
 
import xml as pyxml
6
 
 
7
 
from nevow.stan import xml, Tag, directive, slot
8
 
import nevow
9
 
 
10
 
## Require PyXML 0.8.2 or later, or, if PyXML isn't installed
11
 
## python2.3 or later, because that includes approximately the
12
 
## same code (but doesn't share a version number *!@#$@!@#)
13
 
 
14
 
try:
15
 
    ## pyxml package has a version_info attribute
16
 
    bad_version = pyxml.version_info < (0,8,2)
17
 
    ## before 0.8.3, startDTD was passed the args in the wrong order
18
 
    bad_startdtd_args = pyxml.version_info < (0,8,3)
19
 
except:
20
 
    ## we're using core python xml library
21
 
    import sys
22
 
    bad_version = sys.version_info < (2,3)
23
 
    # python < 2.4 has the startDTD bug
24
 
    bad_startdtd_args = sys.version_info < (2,4)
25
 
 
26
 
 
27
 
class nscontext(object):
28
 
 
29
 
    def __init__(self, parent=None):
30
 
        self.parent = parent
31
 
        if parent is not None:
32
 
            self.nss = dict(parent.nss)
33
 
        else:
34
 
            self.nss = {'http://www.w3.org/XML/1998/namespace':'xml'}
35
 
 
36
 
    def get(self, k, d=None):
37
 
        return self.nss.get(k, d)
38
 
 
39
 
    def __setitem__(self, k, v):
40
 
        self.nss.__setitem__(k, v)
41
 
 
42
 
    def __getitem__(self, k):
43
 
        return self.nss.__getitem__(k)
44
 
 
45
 
 
46
 
class ToStan(handler.ContentHandler, handler.EntityResolver):
47
 
    directiveMapping = {
48
 
        'render': 'render',
49
 
        'data': 'data',
50
 
        'macro': 'macro',
51
 
    }
52
 
    attributeList = [
53
 
        'pattern', 'key',
54
 
    ]
55
 
 
56
 
    def __init__(self, ignoreDocType, ignoreComment, sourceFilename):
57
 
        self.ignoreDocType = ignoreDocType
58
 
        self.ignoreComment = ignoreComment
59
 
        self.sourceFilename = sourceFilename
60
 
        self.prefixMap = nscontext()
61
 
        self.inCDATA = False
62
 
 
63
 
 
64
 
    def setDocumentLocator(self, locator):
65
 
        self.locator = locator
66
 
 
67
 
    def resolveEntity(self, publicId, systemId):
68
 
        ## This doesn't seem to get called, which is good.
69
 
        raise Exception("resolveEntity should not be called. We don't use external DTDs.")
70
 
 
71
 
    def skippedEntity(self, name):
72
 
        self.current.append(xml("&%s;"%name))
73
 
 
74
 
    def startDocument(self):
75
 
        self.document = []
76
 
        self.current = self.document
77
 
        self.stack = []
78
 
        self.xmlnsAttrs = []
79
 
 
80
 
    def endDocument(self):
81
 
        pass
82
 
 
83
 
    def processingInstruction(self, target, data):
84
 
        self.current.append(xml("<?%s %s?>\n" % (target, data)))
85
 
 
86
 
    def startPrefixMapping(self, prefix, uri):
87
 
 
88
 
        self.prefixMap = nscontext(self.prefixMap)
89
 
        self.prefixMap[uri] = prefix
90
 
 
91
 
        # Ignore Nevow's namespace, we'll replace those during parsing.
92
 
        if uri == nevow.namespace:
93
 
            return
94
 
 
95
 
        # Add to a list that will be applied once we have the element.
96
 
        if prefix is None:
97
 
            self.xmlnsAttrs.append(('xmlns',uri))
98
 
        else:
99
 
            self.xmlnsAttrs.append(('xmlns:%s'%prefix,uri))
100
 
 
101
 
    def endPrefixMapping(self, prefix):
102
 
        self.prefixMap = self.prefixMap.parent
103
 
 
104
 
    def startElementNS(self, ns_and_name, qname, attrs):
105
 
 
106
 
        filename = self.sourceFilename
107
 
        lineNumber = self.locator.getLineNumber()
108
 
        columnNumber = self.locator.getColumnNumber()
109
 
 
110
 
        ns, name = ns_and_name
111
 
        if ns == nevow.namespace:
112
 
            if name == 'invisible':
113
 
                name = ''
114
 
            elif name == 'slot':
115
 
                try:
116
 
                    # Try to get the default value for the slot
117
 
                    default = attrs[(None, 'default')]
118
 
                except KeyError:
119
 
                    # If there wasn't one, then use None to indicate no
120
 
                    # default.
121
 
                    default = None
122
 
                el = slot(
123
 
                    attrs[(None, 'name')], default=default,
124
 
                    filename=filename, lineNumber=lineNumber,
125
 
                    columnNumber=columnNumber)
126
 
                self.stack.append(el)
127
 
                self.current.append(el)
128
 
                self.current = el.children
129
 
                return
130
 
 
131
 
        attrs = dict(attrs)
132
 
        specials = {}
133
 
        attributes = self.attributeList
134
 
        directives = self.directiveMapping
135
 
        for k, v in attrs.items():
136
 
            att_ns, nons = k
137
 
            if att_ns != nevow.namespace:
138
 
                continue
139
 
            if nons in directives:
140
 
                ## clean this up by making the names more consistent
141
 
                specials[directives[nons]] = directive(v)
142
 
                del attrs[k]
143
 
            if nons in attributes:
144
 
                specials[nons] = v
145
 
                del attrs[k]
146
 
 
147
 
        no_ns_attrs = {}
148
 
        for (attrNs, attrName), v in attrs.items():
149
 
            nsPrefix = self.prefixMap.get(attrNs)
150
 
            if nsPrefix is None:
151
 
                no_ns_attrs[attrName] = v
152
 
            else:
153
 
                no_ns_attrs['%s:%s'%(nsPrefix,attrName)] = v
154
 
 
155
 
        if ns == nevow.namespace and name == 'attr':
156
 
            if not self.stack:
157
 
                # TODO: define a better exception for this?
158
 
                raise AssertionError( '<nevow:attr> as top-level element' )
159
 
            if 'name' not in no_ns_attrs:
160
 
                # TODO: same here
161
 
                raise AssertionError( '<nevow:attr> requires a name attribute' )
162
 
            el = Tag('', specials=specials, filename=filename,
163
 
                     lineNumber=lineNumber, columnNumber=columnNumber)
164
 
            self.stack[-1].attributes[no_ns_attrs['name']] = el
165
 
            self.stack.append(el)
166
 
            self.current = el.children
167
 
            return
168
 
 
169
 
        # Apply any xmlns attributes
170
 
        if self.xmlnsAttrs:
171
 
            no_ns_attrs.update(dict(self.xmlnsAttrs))
172
 
            self.xmlnsAttrs = []
173
 
 
174
 
        # Add the prefix that was used in the parsed template for non-Nevow
175
 
        # namespaces (which Nevow will consume anyway).
176
 
        if ns != nevow.namespace and ns is not None:
177
 
            prefix = self.prefixMap[ns]
178
 
            if prefix is not None:
179
 
                name = '%s:%s' % (self.prefixMap[ns],name)
180
 
        el = Tag(
181
 
            name, attributes=dict(no_ns_attrs), specials=specials,
182
 
            filename=filename, lineNumber=lineNumber,
183
 
            columnNumber=columnNumber)
184
 
        self.stack.append(el)
185
 
        self.current.append(el)
186
 
        self.current = el.children
187
 
 
188
 
    def characters(self, ch):
189
 
        # CDATA characters should be passed through as is.
190
 
        if self.inCDATA:
191
 
            ch = xml(ch)
192
 
        self.current.append(ch)
193
 
 
194
 
    def endElementNS(self, name, qname):
195
 
        me = self.stack.pop()
196
 
        if self.stack:
197
 
            self.current = self.stack[-1].children
198
 
        else:
199
 
            self.current = self.document
200
 
 
201
 
    def startDTD(self, name, publicId, systemId):
202
 
        if self.ignoreDocType:
203
 
            return
204
 
        # Check for broken startDTD
205
 
        if bad_startdtd_args:
206
 
            systemId, publicId = publicId, systemId
207
 
        doctype = '<!DOCTYPE %s\n  PUBLIC "%s"\n  "%s">\n' % (name, publicId, systemId)
208
 
        self.current.append(xml(doctype))
209
 
 
210
 
    def endDTD(self, *args):
211
 
        pass
212
 
 
213
 
    def startCDATA(self):
214
 
        self.inCDATA = True
215
 
        self.current.append(xml('<![CDATA['))
216
 
 
217
 
    def endCDATA(self):
218
 
        self.inCDATA = False
219
 
        self.current.append(xml(']]>'))
220
 
 
221
 
    def comment(self, content):
222
 
        if self.ignoreComment:
223
 
            return
224
 
        self.current.append( (xml('<!--'),xml(content),xml('-->')) )
225
 
 
226
 
 
227
 
def parse(fl, ignoreDocType=False, ignoreComment=False):
228
 
    ## Earlier PyXMLs don't handle non-standard entities (e.g. &copy;)
229
 
    ## correctly. They will either give an error or simply ignore the
230
 
    ## entity producing bad output.
231
 
 
232
 
    if bad_version:
233
 
        raise Exception("Please use PyXML later than 0.8.2 or python later than 2.3. Earlier ones are too buggy.")
234
 
 
235
 
    parser = make_parser()
236
 
    parser.setFeature(handler.feature_validation, 0)
237
 
    parser.setFeature(handler.feature_namespaces, 1)
238
 
    parser.setFeature(handler.feature_external_ges, 0)
239
 
    parser.setFeature(handler.feature_external_pes, 0)
240
 
 
241
 
    s = ToStan(ignoreDocType, ignoreComment, getattr(fl, "name", None))
242
 
    parser.setContentHandler(s)
243
 
    parser.setEntityResolver(s)
244
 
    parser.setProperty(handler.property_lexical_handler, s)
245
 
 
246
 
    parser.parse(fl)
247
 
 
248
 
    return s.document
249
 
 
250
 
def parseString(t, ignoreDocType=False, ignoreComment=False):
251
 
    from cStringIO import StringIO
252
 
    return parse(StringIO(t), ignoreDocType, ignoreComment)