2 # $Id: Filters.py,v 1.28 2006/06/16 20:15:24 hierro Exp $
3 """Filters for the #filter directive; output filters Cheetah's $placeholders .
5 Filters may now be used standalone, for debugging or for use outside Cheetah.
6 Class DummyTemplate, instance _dummyTemplateObj and class NoDefault exist only
7 for this, to provide a default argument for the filter constructors (which
8 would otherwise require a real template object).
10 The default filter is now RawOrEncodedUnicode. Please use this as a base class instead of Filter because it handles non-ASCII characters better.
13 ================================================================================
14 Author: Tavis Rudd <tavis@damnsimple.com>
15 Version: $Revision: 1.28 $
16 Start Date: 2001/08/01
17 Last Revision Date: $Date: 2006/06/16 20:15:24 $
19 __author__
= "Tavis Rudd <tavis@damnsimple.com>"
20 __revision__
= "$Revision: 1.28 $"[11:-2]
22 from StringIO
import StringIO
# not cStringIO because of unicode support
24 # Additional entities WebSafe knows how to transform. No need to include
25 # '<', '>' or '&' since those will have been done already.
26 webSafeEntities
= {' ': ' ', '"': '"'}
28 class Error(Exception):
36 """Fake template class to allow filters to be used standalone.
38 This is provides only the level of Template compatibility required by the
39 standard filters. Namely, the get-settings interface works but there are
40 no settings. Other aspects of Template are not implemented.
42 def setting(self
, name
, default
=NoDefault
):
43 if default
is NoDefault
:
51 _dummyTemplateObj
= DummyTemplate()
54 ##################################################
58 """A baseclass for the Cheetah Filters."""
60 def __init__(self
, templateObj
=_dummyTemplateObj
):
61 """Setup a ref to the templateObj. Subclasses should call this method.
63 if hasattr(templateObj
, 'setting'):
64 self
.setting
= templateObj
.setting
66 self
.setting
= lambda k
: None
68 if hasattr(templateObj
, 'settings'):
69 self
.settings
= templateObj
.settings
71 self
.settings
= lambda: {}
73 def generateAutoArgs(self
):
75 """This hook allows the filters to generate an arg-list that will be
76 appended to the arg-list of a $placeholder tag when it is being
77 translated into Python code during the template compilation process. See
78 the 'Pager' filter class for an example."""
82 def filter(self
, val
, **kw
):
84 """Reimplement this method if you want more advanced filterting."""
89 ##################################################
93 class ReplaceNone(Filter
):
94 def filter(self
, val
, **kw
):
96 """Replace None with an empty string. Reimplement this method if you
97 want more advanced filterting."""
103 class EncodeUnicode(Filter
):
104 def filter(self
, val
,
106 str=str, type=type, unicodeType
=type(u
''),
108 """Encode Unicode strings, by default in UTF-8.
110 >>> import Cheetah.Template
111 >>> t = Cheetah.Template.Template('''
113 ... ${myvar, encoding='utf16'}
114 ... ''', searchList=[{'myvar': u'Asni\xe8res'}],
115 ... filter='EncodeUnicode')
118 if type(val
)==unicodeType
:
119 filtered
= val
.encode(encoding
)
126 class RawOrEncodedUnicode(Filter
):
127 def filter(self
, val
,
130 str=str, type=type, unicodeType
=type(u
''),
132 """Pass Unicode strings through unmolested, unless an encoding is specified.
134 if type(val
)==unicodeType
:
136 filtered
= val
.encode(encoding
)
146 class MaxLen(RawOrEncodedUnicode
):
147 def filter(self
, val
, **kw
):
148 """Replace None with '' and cut off at maxlen."""
150 output
= super(MaxLen
, self
).filter(val
, **kw
)
151 if kw
.has_key('maxlen') and len(output
) > kw
['maxlen']:
152 return output
[:kw
['maxlen']]
157 class Pager(RawOrEncodedUnicode
):
158 def __init__(self
, templateObj
=_dummyTemplateObj
):
159 Filter
.__init
__(self
, templateObj
)
162 def buildQString(self
,varsDict
, updateDict
):
163 finalDict
= varsDict
.copy()
164 finalDict
.update(updateDict
)
166 for key
, val
in finalDict
.items():
167 qString
+= str(key
) + '=' + str(val
) + '&'
170 def generateAutoArgs(self
):
171 ID
= str(self
._IDcounter
)
173 return ', trans=trans, ID=' + ID
175 def filter(self
, val
, **kw
):
176 """Replace None with '' and cut off at maxlen."""
177 output
= super(Pager
, self
).filter(val
, **kw
)
178 if kw
.has_key('trans') and kw
['trans']:
180 marker
= kw
.get('marker', '<split>')
181 req
= kw
['trans'].request()
182 URI
= req
.environ()['SCRIPT_NAME'] + req
.environ()['PATH_INFO']
183 queryVar
= 'pager' + str(ID
) + '_page'
184 fields
= req
.fields()
185 page
= int(fields
.get( queryVar
, 1))
186 pages
= output
.split(marker
)
187 output
= pages
[page
-1]
190 output
+='<A HREF="' + URI
+ self
.buildQString(fields
, {queryVar
:max(page
-1,1)}) + \
191 '">Previous Page</A> '
192 if page
< len(pages
):
193 output
+= '<A HREF="' + URI
+ self
.buildQString(
196 min(page
+1,len(pages
))}) + \
204 class WebSafe(RawOrEncodedUnicode
):
205 """Escape HTML entities in $placeholders.
207 def filter(self
, val
, **kw
):
208 s
= super(WebSafe
, self
).filter(val
, **kw
)
209 # These substitutions are copied from cgi.escape().
210 s
= s
.replace("&", "&") # Must be done first!
211 s
= s
.replace("<", "<")
212 s
= s
.replace(">", ">")
213 # Process the additional transformations if any.
214 if kw
.has_key('also'):
216 entities
= webSafeEntities
# Global variable.
218 if entities
.has_key(k
):
228 class Strip(RawOrEncodedUnicode
):
229 """Strip leading/trailing whitespace but preserve newlines.
231 This filter goes through the value line by line, removing leading and
232 trailing whitespace on each line. It does not strip newlines, so every
233 input line corresponds to one output line, with its trailing newline intact.
235 We do not use val.split('\n') because that would squeeze out consecutive
236 blank lines. Instead, we search for each newline individually. This
237 makes us unable to use the fast C .split method, but it makes the filter
238 much more widely useful.
240 This filter is intended to be usable both with the #filter directive and
241 with the proposed #sed directive (which has not been ratified yet.)
243 def filter(self
, val
, **kw
):
244 s
= super(Strip
, self
).filter(val
, **kw
)
246 start
= 0 # The current line will be s[start:end].
247 while 1: # Loop through each line.
248 end
= s
.find('\n', start
) # Find next newline.
249 if end
== -1: # If no more newlines.
251 chunk
= s
[start
:end
].strip()
255 # Write the unfinished portion after the last newline, if any.
256 chunk
= s
[start
:].strip()
258 return "".join(result
)
261 class StripSqueeze(RawOrEncodedUnicode
):
262 """Canonicalizes every chunk of whitespace to a single space.
264 Strips leading/trailing whitespace. Removes all newlines, so multi-line
265 input is joined into one ling line with NO trailing newline.
267 def filter(self
, val
, **kw
):
268 s
= super(StripSqueeze
, self
).filter(val
, **kw
)
272 ##################################################
273 ## MAIN ROUTINE -- testing
277 s2
= " asdf \n\t 1 2 3\n"
278 print "WebSafe INPUT:", `s1`
279 print " WebSafe:", `
WebSafe().filter(s1
)`
282 print " Strip INPUT:", `s2`
283 print " Strip:", `
Strip().filter(s2
)`
284 print "StripSqueeze:", `
StripSqueeze().filter(s2
)`
286 print "Unicode:", `
EncodeUnicode().filter(u
'aoeu12345\u1234')`
288 if __name__
== "__main__": test()
290 # vim: shiftwidth=4 tabstop=4 expandtab