Initial import to Tizen
[profile/ivi/python-twisted.git] / twisted / web / _stan.py
1 # -*- test-case-name: twisted.web.test.test_stan -*-
2 # Copyright (c) Twisted Matrix Laboratories.
3 # See LICENSE for details.
4
5 """
6 An s-expression-like syntax for expressing xml in pure python.
7
8 Stan tags allow you to build XML documents using Python.
9
10 Stan is a DOM, or Document Object Model, implemented using basic Python types
11 and functions called "flatteners". A flattener is a function that knows how to
12 turn an object of a specific type into something that is closer to an HTML
13 string. Stan differs from the W3C DOM by not being as cumbersome and heavy
14 weight. Since the object model is built using simple python types such as lists,
15 strings, and dictionaries, the API is simpler and constructing a DOM less
16 cumbersome.
17
18 @var voidElements: the names of HTML 'U{void
19     elements<http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#void-elements>}';
20     those which can't have contents and can therefore be self-closing in the
21     output.
22 """
23
24
25 class slot(object):
26     """
27     Marker for markup insertion in a template.
28
29     @type name: C{str}
30     @ivar name: The name of this slot.  The key which must be used in
31         L{Tag.fillSlots} to fill it.
32
33     @type children: C{list}
34     @ivar children: The L{Tag} objects included in this L{slot}'s template.
35
36     @type default: anything flattenable, or C{NoneType}
37     @ivar default: The default contents of this slot, if it is left unfilled.
38         If this is C{None}, an L{UnfilledSlot} will be raised, rather than
39         C{None} actually being used.
40
41     @type filename: C{str} or C{NoneType}
42     @ivar filename: The name of the XML file from which this tag was parsed.
43         If it was not parsed from an XML file, C{None}.
44
45     @type lineNumber: C{int} or C{NoneType}
46     @ivar lineNumber: The line number on which this tag was encountered in the
47         XML file from which it was parsed.  If it was not parsed from an XML
48         file, C{None}.
49
50     @type columnNumber: C{int} or C{NoneType}
51     @ivar columnNumber: The column number at which this tag was encountered in
52         the XML file from which it was parsed.  If it was not parsed from an
53         XML file, C{None}.
54     """
55
56     def __init__(self, name, default=None, filename=None, lineNumber=None,
57                  columnNumber=None):
58         self.name = name
59         self.children = []
60         self.default = default
61         self.filename = filename
62         self.lineNumber = lineNumber
63         self.columnNumber = columnNumber
64
65
66     def __repr__(self):
67         return "slot(%r)" % (self.name,)
68
69
70
71 class Tag(object):
72     """
73     A L{Tag} represents an XML tags with a tag name, attributes, and children.
74     A L{Tag} can be constructed using the special L{twisted.web.template.tags}
75     object, or it may be constructed directly with a tag name. L{Tag}s have a
76     special method, C{__call__}, which makes representing trees of XML natural
77     using pure python syntax.
78
79     @ivar tagName: The name of the represented element.  For a tag like
80         C{<div></div>}, this would be C{"div"}.
81     @type tagName: C{str}
82
83     @ivar attributes: The attributes of the element.
84     @type attributes: C{dict} mapping C{str} to renderable objects.
85
86     @ivar children: The child L{Tag}s of this C{Tag}.
87     @type children: C{list} of renderable objects.
88
89     @ivar render: The name of the render method to use for this L{Tag}.  This
90         name will be looked up at render time by the
91         L{twisted.web.template.Element} doing the rendering, via
92         L{twisted.web.template.Element.lookupRenderMethod}, to determine which
93         method to call.
94     @type render: C{str}
95
96     @type filename: C{str} or C{NoneType}
97     @ivar filename: The name of the XML file from which this tag was parsed.
98         If it was not parsed from an XML file, C{None}.
99
100     @type lineNumber: C{int} or C{NoneType}
101     @ivar lineNumber: The line number on which this tag was encountered in the
102         XML file from which it was parsed.  If it was not parsed from an XML
103         file, C{None}.
104
105     @type columnNumber: C{int} or C{NoneType}
106     @ivar columnNumber: The column number at which this tag was encountered in
107         the XML file from which it was parsed.  If it was not parsed from an
108         XML file, C{None}.
109
110     @type slotData: C{dict} or C{NoneType}
111     @ivar slotData: The data which can fill slots.  If present, a dictionary
112         mapping slot names to renderable values.  The values in this dict might
113         be anything that can be present as the child of a L{Tag}; strings,
114         lists, L{Tag}s, generators, etc.
115     """
116
117     slotData = None
118     filename = None
119     lineNumber = None
120     columnNumber = None
121
122     def __init__(self, tagName, attributes=None, children=None, render=None,
123                  filename=None, lineNumber=None, columnNumber=None):
124         self.tagName = tagName
125         self.render = render
126         if attributes is None:
127             self.attributes = {}
128         else:
129             self.attributes = attributes
130         if children is None:
131             self.children = []
132         else:
133             self.children = children
134         if filename is not None:
135             self.filename = filename
136         if lineNumber is not None:
137             self.lineNumber = lineNumber
138         if columnNumber is not None:
139             self.columnNumber = columnNumber
140
141
142     def fillSlots(self, **slots):
143         """
144         Remember the slots provided at this position in the DOM.
145
146         During the rendering of children of this node, slots with names in
147         C{slots} will be rendered as their corresponding values.
148
149         @return: C{self}. This enables the idiom C{return tag.fillSlots(...)} in
150             renderers.
151         """
152         if self.slotData is None:
153             self.slotData = {}
154         self.slotData.update(slots)
155         return self
156
157
158     def __call__(self, *children, **kw):
159         """
160         Add children and change attributes on this tag.
161
162         This is implemented using __call__ because it then allows the natural
163         syntax::
164
165           table(tr1, tr2, width="100%", height="50%", border="1")
166
167         Children may be other tag instances, strings, functions, or any other
168         object which has a registered flatten.
169
170         Attributes may be 'transparent' tag instances (so that
171         C{a(href=transparent(data="foo", render=myhrefrenderer))} works),
172         strings, functions, or any other object which has a registered
173         flattener.
174
175         If the attribute is a python keyword, such as 'class', you can add an
176         underscore to the name, like 'class_'.
177
178         There is one special keyword argument, 'render', which will be used as
179         the name of the renderer and saved as the 'render' attribute of this
180         instance, rather than the DOM 'render' attribute in the attributes
181         dictionary.
182         """
183         self.children.extend(children)
184
185         for k, v in kw.iteritems():
186             if k[-1] == '_':
187                 k = k[:-1]
188
189             if k == 'render':
190                 self.render = v
191             else:
192                 self.attributes[k] = v
193         return self
194
195
196     def _clone(self, obj, deep):
197         """
198         Clone an arbitrary object; used by L{Tag.clone}.
199
200         @param obj: an object with a clone method, a list or tuple, or something
201             which should be immutable.
202
203         @param deep: whether to continue cloning child objects; i.e. the
204             contents of lists, the sub-tags within a tag.
205
206         @return: a clone of C{obj}.
207         """
208         if hasattr(obj, 'clone'):
209             return obj.clone(deep)
210         elif isinstance(obj, (list, tuple)):
211             return [self._clone(x, deep) for x in obj]
212         else:
213             return obj
214
215
216     def clone(self, deep=True):
217         """
218         Return a clone of this tag. If deep is True, clone all of this tag's
219         children. Otherwise, just shallow copy the children list without copying
220         the children themselves.
221         """
222         if deep:
223             newchildren = [self._clone(x, True) for x in self.children]
224         else:
225             newchildren = self.children[:]
226         newattrs = self.attributes.copy()
227         for key in newattrs:
228             newattrs[key] = self._clone(newattrs[key], True)
229
230         newslotdata = None
231         if self.slotData:
232             newslotdata = self.slotData.copy()
233             for key in newslotdata:
234                 newslotdata[key] = self._clone(newslotdata[key], True)
235
236         newtag = Tag(
237             self.tagName,
238             attributes=newattrs,
239             children=newchildren,
240             render=self.render,
241             filename=self.filename,
242             lineNumber=self.lineNumber,
243             columnNumber=self.columnNumber)
244         newtag.slotData = newslotdata
245
246         return newtag
247
248
249     def clear(self):
250         """
251         Clear any existing children from this tag.
252         """
253         self.children = []
254         return self
255
256
257     def __repr__(self):
258         rstr = ''
259         if self.attributes:
260             rstr += ', attributes=%r' % self.attributes
261         if self.children:
262             rstr += ', children=%r' % self.children
263         return "Tag(%r%s)" % (self.tagName, rstr)
264
265
266
267 voidElements = ('img', 'br', 'hr', 'base', 'meta', 'link', 'param', 'area',
268                 'input', 'col', 'basefont', 'isindex', 'frame', 'command',
269                 'embed', 'keygen', 'source', 'track', 'wbs')
270
271
272 class CDATA(object):
273     """
274     A C{<![CDATA[]]>} block from a template.  Given a separate representation in
275     the DOM so that they may be round-tripped through rendering without losing
276     information.
277
278     @ivar data: The data between "C{<![CDATA[}" and "C{]]>}".
279     @type data: C{unicode}
280     """
281     def __init__(self, data):
282         self.data = data
283
284
285     def __repr__(self):
286         return 'CDATA(%r)' % (self.data,)
287
288
289
290 class Comment(object):
291     """
292     A C{<!-- -->} comment from a template.  Given a separate representation in
293     the DOM so that they may be round-tripped through rendering without losing
294     information.
295
296     @ivar data: The data between "C{<!--}" and "C{-->}".
297     @type data: C{unicode}
298     """
299
300     def __init__(self, data):
301         self.data = data
302
303
304     def __repr__(self):
305         return 'Comment(%r)' % (self.data,)
306
307
308
309 class CharRef(object):
310     """
311     A numeric character reference.  Given a separate representation in the DOM
312     so that non-ASCII characters may be output as pure ASCII.
313
314     @ivar ordinal: The ordinal value of the unicode character to which this is
315         object refers.
316     @type ordinal: C{int}
317
318     @since: 12.0
319     """
320     def __init__(self, ordinal):
321         self.ordinal = ordinal
322
323
324     def __repr__(self):
325         return "CharRef(%d)" % (self.ordinal,)