Changeset 954
- Timestamp:
- 09/10/08 22:53:09 (10 months ago)
- Location:
- trunk
- Files:
-
- 8 modified
-
. (modified) (1 prop)
-
genshi/filters/i18n.py (modified) (9 diffs)
-
genshi/filters/tests/i18n.py (modified) (11 diffs)
-
genshi/template/base.py (modified) (7 diffs)
-
genshi/template/markup.py (modified) (6 diffs)
-
genshi/template/tests/directives.py (modified) (3 diffs)
-
genshi/template/tests/markup.py (modified) (4 diffs)
-
genshi/template/text.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk
-
trunk/genshi/filters/i18n.py
r932 r954 24 24 from genshi.core import Attrs, Namespace, QName, START, END, TEXT, START_NS, \ 25 25 END_NS, XML_NAMESPACE, _ensure 26 from genshi.template.base import Template, EXPR, SUB 26 from genshi.template.base import DirectiveFactory, EXPR, SUB, _apply_directives 27 from genshi.template.directives import Directive 27 28 from genshi.template.markup import MarkupTemplate, EXEC 28 29 … … 33 34 34 35 35 class Translator(object): 36 class CommentDirective(Directive): 37 38 __slots__ = [] 39 40 @classmethod 41 def attach(cls, template, stream, value, namespaces, pos): 42 return None, stream 43 44 45 class MsgDirective(Directive): 46 47 __slots__ = ['params'] 48 49 def __init__(self, value, template, hints=None, namespaces=None, 50 lineno=-1, offset=-1): 51 Directive.__init__(self, None, template, namespaces, lineno, offset) 52 self.params = [name.strip() for name in value.split(',')] 53 54 def __call__(self, stream, directives, ctxt, **vars): 55 msgbuf = MessageBuffer(self.params) 56 57 stream = iter(stream) 58 yield stream.next() # the outer start tag 59 previous = stream.next() 60 for event in stream: 61 msgbuf.append(*previous) 62 previous = event 63 64 gettext = ctxt.get('_i18n.gettext') 65 for event in msgbuf.translate(gettext(msgbuf.format())): 66 yield event 67 68 yield previous # the outer end tag 69 70 71 class Translator(DirectiveFactory): 36 72 """Can extract and translate localizable strings from markup streams and 37 73 templates. … … 86 122 """ 87 123 124 directives = [ 125 ('comment', CommentDirective), 126 ('msg', MsgDirective) 127 ] 128 88 129 IGNORE_TAGS = frozenset([ 89 130 QName('script'), QName('http://www.w3.org/1999/xhtml}script'), … … 92 133 INCLUDE_ATTRS = frozenset(['abbr', 'alt', 'label', 'prompt', 'standby', 93 134 'summary', 'title']) 135 NAMESPACE = I18N_NAMESPACE 94 136 95 137 def __init__(self, translate=NullTranslations(), ignore_tags=IGNORE_TAGS, … … 114 156 self.extract_text = extract_text 115 157 116 def __call__(self, stream, ctxt=None, search_text=True , msgbuf=None):158 def __call__(self, stream, ctxt=None, search_text=True): 117 159 """Translate any localizable strings in the given stream. 118 160 … … 127 169 :param search_text: whether text nodes should be translated (used 128 170 internally) 129 :param msgbuf: a `MessageBuffer` object or `None` (used internally)130 171 :return: the localized stream 131 172 """ 132 173 ignore_tags = self.ignore_tags 133 174 include_attrs = self.include_attrs 175 skip = 0 176 xml_lang = XML_NAMESPACE['lang'] 177 134 178 if type(self.translate) is FunctionType: 135 179 gettext = self.translate 136 180 else: 137 181 gettext = self.translate.ugettext 138 if not self.extract_text: 182 if ctxt: 183 ctxt['_i18n.gettext'] = gettext 184 185 extract_text = self.extract_text 186 if not extract_text: 139 187 search_text = False 140 141 ns_prefixes = []142 skip = 0143 i18n_comment = I18N_NAMESPACE['comment']144 i18n_msg = I18N_NAMESPACE['msg']145 xml_lang = XML_NAMESPACE['lang']146 188 147 189 for kind, data, pos in stream: … … 169 211 for name, value in attrs: 170 212 newval = value 171 if search_text and isinstance(value, basestring):213 if extract_text and isinstance(value, basestring): 172 214 if name in include_attrs: 173 215 newval = gettext(value) … … 183 225 attrs = Attrs(new_attrs) 184 226 185 if msgbuf:186 msgbuf.append(kind, data, pos)187 continue188 elif i18n_msg in attrs:189 params = attrs.get(i18n_msg)190 if params and type(params) is list: # event tuple191 params = params[0][1]192 msgbuf = MessageBuffer(params)193 attrs -= (i18n_comment, i18n_msg)194 195 227 yield kind, (tag, attrs), pos 196 228 197 229 elif search_text and kind is TEXT: 198 if not msgbuf: 199 text = data.strip() 200 if text: 201 data = data.replace(text, unicode(gettext(text))) 202 yield kind, data, pos 203 else: 204 msgbuf.append(kind, data, pos) 205 206 elif msgbuf and kind is EXPR: 207 msgbuf.append(kind, data, pos) 208 209 elif not skip and msgbuf and kind is END: 210 msgbuf.append(kind, data, pos) 211 if not msgbuf.depth: 212 for event in msgbuf.translate(gettext(msgbuf.format())): 213 yield event 214 msgbuf = None 215 yield kind, data, pos 230 text = data.strip() 231 if text: 232 data = data.replace(text, unicode(gettext(text))) 233 yield kind, data, pos 216 234 217 235 elif kind is SUB: 218 subkind, substream = data 219 new_substream = list(self(substream, ctxt, msgbuf=msgbuf)) 220 yield kind, (subkind, new_substream), pos 221 222 elif kind is START_NS and data[1] == I18N_NAMESPACE: 223 ns_prefixes.append(data[0]) 224 225 elif kind is END_NS and data in ns_prefixes: 226 ns_prefixes.remove(data) 236 directives, substream = data 237 # If this is an i18n:msg directive, no need to translate text 238 # nodes here 239 is_msg = filter(None, [isinstance(d, MsgDirective) 240 for d in directives]) 241 substream = list(self(substream, ctxt, 242 search_text=not is_msg)) 243 yield kind, (directives, substream), pos 227 244 228 245 else: … … 373 390 belonging to the message was found 374 391 """ 375 self.params = [name.strip() for name in params.split(',')] 392 if isinstance(params, basestring): 393 params = [name.strip() for name in params.split(',')] 394 self.params = params 376 395 self.comment = comment 377 396 self.lineno = lineno -
trunk/genshi/filters/tests/i18n.py
r932 r954 175 175 </html>""") 176 176 gettext = lambda s: u"Für Details siehe bitte [1:Hilfe]." 177 tmpl.filters.insert(0, Translator(gettext)) 177 translator = Translator(gettext) 178 tmpl.filters.insert(0, translator) 179 tmpl.add_directives(Translator.NAMESPACE, translator) 178 180 self.assertEqual("""<html> 179 181 <p>Für Details siehe bitte <a href="help.html">Hilfe</a>.</p> … … 201 203 </html>""") 202 204 gettext = lambda s: u"Für Details siehe bitte [1:[2:Hilfeseite]]." 203 tmpl.filters.insert(0, Translator(gettext)) 205 translator = Translator(gettext) 206 tmpl.filters.insert(0, translator) 207 tmpl.add_directives(Translator.NAMESPACE, translator) 204 208 self.assertEqual("""<html> 205 209 <p>Für Details siehe bitte <a href="help.html"><em>Hilfeseite</em></a>.</p> … … 226 230 </html>""") 227 231 gettext = lambda s: u"[1:] Einträge pro Seite anzeigen." 228 tmpl.filters.insert(0, Translator(gettext)) 232 translator = Translator(gettext) 233 tmpl.filters.insert(0, translator) 234 tmpl.add_directives(Translator.NAMESPACE, translator) 229 235 self.assertEqual("""<html> 230 236 <p><input type="text" name="num"/> Einträge pro Seite anzeigen.</p> … … 251 257 </html>""") 252 258 gettext = lambda s: u"Für [2:Details] siehe bitte [1:Hilfe]." 253 tmpl.filters.insert(0, Translator(gettext)) 259 translator = Translator(gettext) 260 tmpl.filters.insert(0, translator) 261 tmpl.add_directives(Translator.NAMESPACE, translator) 254 262 self.assertEqual("""<html> 255 263 <p>Für <em>Details</em> siehe bitte <a href="help.html">Hilfe</a>.</p> … … 277 285 </html>""") 278 286 gettext = lambda s: u"[1:] Einträge pro Seite, beginnend auf Seite [2:]." 279 tmpl.filters.insert(0, Translator(gettext)) 287 translator = Translator(gettext) 288 tmpl.filters.insert(0, translator) 289 tmpl.add_directives(Translator.NAMESPACE, translator) 280 290 self.assertEqual("""<html> 281 291 <p><input type="text" name="num"/> Eintr\xc3\xa4ge pro Seite, beginnend auf Seite <input type="text" name="num"/>.</p> … … 302 312 </html>""") 303 313 gettext = lambda s: u"Hallo, %(name)s!" 304 tmpl.filters.insert(0, Translator(gettext)) 314 translator = Translator(gettext) 315 tmpl.filters.insert(0, translator) 316 tmpl.add_directives(Translator.NAMESPACE, translator) 305 317 self.assertEqual("""<html> 306 318 <p>Hallo, Jim!</p> … … 315 327 </html>""") 316 328 gettext = lambda s: u"%(name)s, sei gegrüßt!" 317 tmpl.filters.insert(0, Translator(gettext)) 329 translator = Translator(gettext) 330 tmpl.filters.insert(0, translator) 331 tmpl.add_directives(Translator.NAMESPACE, translator) 318 332 self.assertEqual("""<html> 319 333 <p>Jim, sei gegrüßt!</p> … … 328 342 </html>""") 329 343 gettext = lambda s: u"Sei gegrüßt, [1:Alter]!" 330 tmpl.filters.insert(0, Translator(gettext)) 344 translator = Translator(gettext) 345 tmpl.filters.insert(0, translator) 346 tmpl.add_directives(Translator.NAMESPACE, translator) 331 347 self.assertEqual("""<html> 332 348 <p>Sei gegrüßt, <a href="#42">Alter</a>!</p> … … 353 369 </html>""") 354 370 gettext = lambda s: u"%(name)s schrieb dies um %(time)s" 355 tmpl.filters.insert(0, Translator(gettext)) 371 translator = Translator(gettext) 372 tmpl.filters.insert(0, translator) 373 tmpl.add_directives(Translator.NAMESPACE, translator) 356 374 entry = { 357 375 'author': 'Jim', … … 404 422 </html>""") 405 423 gettext = lambda s: u"Voh" 406 tmpl.filters.insert(0, Translator(gettext)) 424 translator = Translator(gettext) 425 tmpl.filters.insert(0, translator) 426 tmpl.add_directives(Translator.NAMESPACE, translator) 407 427 self.assertEqual("""<html> 408 428 <p>Voh</p> 409 429 </html>""", tmpl.generate().render()) 410 430 431 def test_extract_i18n_msg_with_attr(self): 432 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" 433 xmlns:i18n="http://genshi.edgewall.org/i18n"> 434 <p i18n:msg="" title="Foo bar">Foo</p> 435 </html>""") 436 translator = Translator() 437 messages = list(translator.extract(tmpl.stream)) 438 self.assertEqual(2, len(messages)) 439 self.assertEqual((3, None, u'Foo bar', []), messages[0]) 440 self.assertEqual((3, None, u'Foo', []), messages[1]) 441 442 def test_translate_i18n_msg_with_attr(self): 443 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" 444 xmlns:i18n="http://genshi.edgewall.org/i18n"> 445 <p i18n:msg="" title="Foo bar">Foo</p> 446 </html>""") 447 gettext = lambda s: u"Voh" 448 translator = Translator(DummyTranslations({ 449 'Foo': u'Voh', 450 'Foo bar': u'Voh bär' 451 })) 452 tmpl.filters.insert(0, translator) 453 tmpl.add_directives(Translator.NAMESPACE, translator) 454 self.assertEqual("""<html> 455 <p title="Voh bär">Voh</p> 456 </html>""", tmpl.generate().render()) 457 411 458 def test_translate_with_translations_object(self): 412 459 tmpl = MarkupTemplate("""<html xmlns:py="http://genshi.edgewall.org/" … … 414 461 <p i18n:msg="" i18n:comment="As in foo bar">Foo</p> 415 462 </html>""") 416 translations = DummyTranslations({'Foo': 'Voh'}) 417 tmpl.filters.insert(0, Translator(translations)) 463 translator = Translator(DummyTranslations({'Foo': 'Voh'})) 464 tmpl.filters.insert(0, translator) 465 tmpl.add_directives(Translator.NAMESPACE, translator) 418 466 self.assertEqual("""<html> 419 467 <p>Voh</p> -
trunk/genshi/template/base.py
r876 r954 27 27 from genshi.input import ParseError 28 28 29 __all__ = ['Context', ' Template', 'TemplateError', 'TemplateRuntimeError',30 'Template SyntaxError', 'BadDirectiveError']29 __all__ = ['Context', 'DirectiveFactory', 'Template', 'TemplateError', 30 'TemplateRuntimeError', 'TemplateSyntaxError', 'BadDirectiveError'] 31 31 __docformat__ = 'restructuredtext en' 32 32 … … 302 302 303 303 304 class TemplateMeta(type):305 """Meta class for templates."""304 class DirectiveFactoryMeta(type): 305 """Meta class for directive factories.""" 306 306 307 307 def __new__(cls, name, bases, d): … … 313 313 314 314 315 class Template(object): 315 class DirectiveFactory(object): 316 """Base for classes that provide a set of template directives. 317 318 :since: version 0.6 319 """ 320 __metaclass__ = DirectiveFactoryMeta 321 322 directives = [] 323 """A list of `(name, cls)` tuples that define the set of directives 324 provided by this factory. 325 """ 326 327 def compare_directives(self): 328 """Return a function that takes two directive classes and compares 329 them to determine their relative ordering. 330 """ 331 def _get_index(cls): 332 if cls in self._dir_order: 333 return self._dir_order.index(cls) 334 return 0 335 return lambda a, b: cmp(_get_index(a[0]), _get_index(b[0])) 336 337 def get_directive(self, name): 338 """Return the directive class for the given name. 339 340 :param name: the directive name as used in the template 341 :return: the directive class 342 :see: `Directive` 343 """ 344 return self._dir_by_name.get(name) 345 346 347 class Template(DirectiveFactory): 316 348 """Abstract template base class. 317 349 … … 319 351 specify the syntax of templates. 320 352 """ 321 __metaclass__ = TemplateMeta322 353 323 354 EXEC = StreamEventKind('EXEC') … … 364 395 self.allow_exec = allow_exec 365 396 self._init_filters() 397 self._prepared = False 366 398 367 399 if isinstance(source, basestring): … … 370 402 source = source 371 403 try: 372 self. stream = list(self._prepare(self._parse(source, encoding)))404 self._stream = self._parse(source, encoding) 373 405 except ParseError, e: 374 406 raise TemplateSyntaxError(e.msg, self.filepath, e.lineno, e.offset) … … 390 422 if self.loader: 391 423 self.filters.append(self._include) 424 425 def _get_stream(self): 426 if not self._prepared: 427 self._stream = list(self._prepare(self._stream)) 428 self._prepared = True 429 return self._stream 430 stream = property(_get_stream) 392 431 393 432 def _parse(self, source, encoding): -
trunk/genshi/template/markup.py
r914 r954 43 43 """ 44 44 45 DIRECTIVE_NAMESPACE = Namespace('http://genshi.edgewall.org/')46 XINCLUDE_NAMESPACE = Namespace('http://www.w3.org/2001/XInclude')45 DIRECTIVE_NAMESPACE = 'http://genshi.edgewall.org/' 46 XINCLUDE_NAMESPACE = 'http://www.w3.org/2001/XInclude' 47 47 48 48 directives = [('def', DefDirective), … … 61 61 _number_conv = Markup 62 62 63 def __init__(self, source, filepath=None, filename=None, loader=None, 64 encoding=None, lookup='strict', allow_exec=True): 65 Template.__init__(self, source, filepath=filepath, filename=filename, 66 loader=loader, encoding=encoding, lookup=lookup, 67 allow_exec=allow_exec) 68 self.add_directives(self.DIRECTIVE_NAMESPACE, self) 69 63 70 def _init_filters(self): 64 71 Template._init_filters(self) … … 71 78 72 79 def _parse(self, source, encoding): 73 streams = [[]] # stacked lists of events of the "compiled" template74 dirmap = {} # temporary mapping of directives to elements75 ns_prefix = {}76 depth = 077 fallbacks = []78 includes = []79 80 80 if not isinstance(source, Stream): 81 81 source = XMLParser(source, filename=self.filename, 82 82 encoding=encoding) 83 stream = [] 83 84 84 85 for kind, data, pos in source: 85 stream = streams[-1] 86 87 if kind is START_NS: 88 # Strip out the namespace declaration for template directives 89 prefix, uri = data 90 ns_prefix[prefix] = uri 91 if uri not in (self.DIRECTIVE_NAMESPACE, 92 self.XINCLUDE_NAMESPACE): 86 87 if kind is TEXT: 88 for kind, data, pos in interpolate(data, self.filepath, pos[1], 89 pos[2], lookup=self.lookup): 93 90 stream.append((kind, data, pos)) 94 91 95 elif kind is END_NS: 96 uri = ns_prefix.pop(data, None) 97 if uri and uri not in (self.DIRECTIVE_NAMESPACE, 98 self.XINCLUDE_NAMESPACE): 92 elif kind is PI and data[0] == 'python': 93 if not self.allow_exec: 94 raise TemplateSyntaxError('Python code blocks not allowed', 95 self.filepath, *pos[1:]) 96 try: 97 suite = Suite(data[1], self.filepath, pos[1], 98 lookup=self.lookup) 99 except SyntaxError, err: 100 raise TemplateSyntaxError(err, self.filepath, 101 pos[1] + (err.lineno or 1) - 1, 102 pos[2] + (err.offset or 0)) 103 stream.append((EXEC, suite, pos)) 104 105 elif kind is COMMENT: 106 if not data.lstrip().startswith('!'): 99 107 stream.append((kind, data, pos)) 100 108 101 elif kind is START: 102 # Record any directive attributes in start tags 109 else: 110 stream.append((kind, data, pos)) 111 112 return stream 113 114 def _extract_directives(self, stream, namespace, factory): 115 depth = 0 116 dirmap = {} # temporary mapping of directives to elements 117 new_stream = [] 118 ns_prefix = {} # namespace prefixes in use 119 120 for kind, data, pos in stream: 121 122 if kind is START: 103 123 tag, attrs = data 104 124 directives = [] 105 125 strip = False 106 126 107 if tag in self.DIRECTIVE_NAMESPACE:108 cls = self._dir_by_name.get(tag.localname)127 if tag.namespace == namespace: 128 cls = factory.get_directive(tag.localname) 109 129 if cls is None: 110 raise BadDirectiveError(tag.localname, self.filepath,111 pos[1])130 raise BadDirectiveError(tag.localname, 131 self.filepath, pos[1]) 112 132 args = dict([(name.localname, value) for name, value 113 133 in attrs if not name.namespace]) … … 117 137 new_attrs = [] 118 138 for name, value in attrs: 119 if name in self.DIRECTIVE_NAMESPACE:120 cls = self._dir_by_name.get(name.localname)139 if name.namespace == namespace: 140 cls = factory.get_directive(name.localname) 121 141 if cls is None: 122 142 raise BadDirectiveError(name.localname, 123 143 self.filepath, pos[1]) 124 directives.append((cls, value, ns_prefix.copy(), pos)) 144 if type(value) is list and len(value) == 1: 145 value = value[0][1] 146 directives.append((cls, value, ns_prefix.copy(), 147 pos)) 125 148 else: 126 if value:127 value = list(interpolate(value, self.filepath,128 pos[1], pos[2],129 lookup=self.lookup))130 if len(value) == 1 and value[0][0] is TEXT:131 value = value[0][1]132 else:133 value = [(TEXT, u'', pos)]134 149 new_attrs.append((name, value)) 135 150 new_attrs = Attrs(new_attrs) 136 151 137 152 if directives: 138 index = self._dir_order.index 139 directives.sort(lambda a, b: cmp(index(a[0]), index(b[0]))) 140 dirmap[(depth, tag)] = (directives, len(stream), strip) 141 142 if tag in self.XINCLUDE_NAMESPACE: 153 directives.sort(self.compare_directives()) 154 dirmap[(depth, tag)] = (directives, len(new_stream), 155 strip) 156 157 new_stream.append((kind, (tag, new_attrs), pos)) 158 depth += 1 159 160 elif kind is END: 161 depth -= 1 162 new_stream.append((kind, data, pos)) 163 164 # If there have have directive attributes with the 165 # corresponding start tag, move the events inbetween into 166 # a "subprogram" 167 if (depth, data) in dirmap: 168 directives, offset, strip = dirmap.pop((depth, data)) 169 substream = new_stream[offset:] 170 if strip: 171 substream = substream[1:-1] 172 new_stream[offset:] = [ 173 (SUB, (directives, substream), pos) 174 ] 175 176 elif kind is SUB: 177 directives, substream = data 178 substream = self._extract_directives(substream, namespace, 179 factory) 180 181 if len(substream) == 1 and substream[0][0] is SUB: 182 added_directives, substream = substream[0][1] 183 directives += added_directives 184 185 new_stream.append((kind, (directives, substream), pos)) 186 187 elif kind is START_NS: 188 # Strip out the namespace declaration for template 189 # directives 190 prefix, uri = data 191 ns_prefix[prefix] = uri 192 if uri != namespace: 193 new_stream.append((kind, data, pos)) 194 195 elif kind is END_NS: 196 uri = ns_prefix.pop(data, None) 197 if uri and uri != namespace: 198 new_stream.append((kind, data, pos)) 199 200 else: 201 new_stream.append((kind, data, pos)) 202 203 return new_stream 204 205 def _extract_includes(self, stream): 206 streams = [[]] # stacked lists of events of the "compiled" template 207 prefixes = {} 208 fallbacks = [] 209 includes = [] 210 xinclude_ns = Namespace(self.XINCLUDE_NAMESPACE) 211 212 for kind, data, pos in stream: 213 stream = streams[-1] 214 215 if kind is START: 216 # Record any directive attributes in start tags 217 tag, attrs = data 218 if tag in xinclude_ns: 143 219 if tag.localname == 'include': 144 include_href = new_attrs.get('href')220 include_href = attrs.get('href') 145 221 if not include_href: 146 222 raise TemplateSyntaxError('Include misses required ' 147 223 'attribute "href"', 148 224 self.filepath, *pos[1:]) 149 includes.append((include_href, new_attrs.get('parse')))225 includes.append((include_href, attrs.get('parse'))) 150 226 streams.append([]) 151 227 elif tag.localname == 'fallback': 152 228 streams.append([]) 153 229 fallbacks.append(streams[-1]) 154 155 230 else: 156 stream.append((kind, (tag, new_attrs), pos)) 157 158 depth += 1 231 stream.append((kind, (tag, attrs), pos)) 159 232 160 233 elif kind is END: 161 depth -= 1 162 163 if fallbacks and data == self.XINCLUDE_NAMESPACE['fallback']: 234 if fallbacks and data == xinclude_ns['fallback']: 164 235 assert streams.pop() is fallbacks[-1] 165 elif data == self.XINCLUDE_NAMESPACE['include']:236 elif data == xinclude_ns['include']: 166 237 fallback = None 167 238 if len(fallbacks) == len(includes): … … 184 255 stream.append((kind, data, pos)) 185 256 186 # If there have have directive attributes with the corresponding 187 # start tag, move the events inbetween into a "subprogram" 188 if (depth, data) in dirmap: 189 directives, start_offset, strip = dirmap.pop((depth, data)) 190 substream = stream[start_offset:] 191 if strip: 192 substream = substream[1:-1] 193 stream[start_offset:] = [(SUB, (directives, substream), 194 pos)] 195 196 elif kind is PI and data[0] == 'python': 197 if not self.allow_exec: 198 raise TemplateSyntaxError('Python code blocks not allowed', 199 self.filepath, *pos[1:]) 200 try: 201 suite = Suite(data[1], self.filepath, pos[1], 202 lookup=self.lookup) 203 except SyntaxError, err: 204 raise TemplateSyntaxError(err, self.filepath, 205 pos[1] + (err.lineno or 1) - 1, 206 pos[2] + (err.offset or 0)) 207 stream.append((EXEC, suite, pos)) 208 209 elif kind is TEXT: 210 for kind, data, pos in interpolate(data, self.filepath, pos[1], 211 pos[2], lookup=self.lookup): 212 stream.append((kind, data, pos)) 213 214 elif kind is COMMENT: 215 if not data.lstrip().startswith('!'): 216 stream.append((kind, data, pos)) 257 elif kind is START_NS and data[1] == xinclude_ns: 258 # Strip out the XInclude namespace 259 prefixes[data[0]] = data[1] 260 261 elif kind is END_NS and data in prefixes: 262 prefixes.pop(data) 217 263 218 264 else: … … 221 267 assert len(streams) == 1 222 268 return streams[0] 269 270 def _interpolate_attrs(self, stream): 271 for kind, data, pos in stream: 272 273 if kind is START: 274 # Record any directive attributes in start tags 275 tag, attrs = data 276 new_attrs = [] 277 for name, value in attrs: 278 if value: 279 value = list(interpolate(value, self.filepath, pos[1], 280 pos[2], lookup=self.lookup)) 281 if len(value) == 1 and value[0][0] is TEXT: 282 value = value[0][1] 283 else: 284 value = [(TEXT, u'', pos)] 285 new_attrs.append((name, value)) 286 data = tag, Attrs(new_attrs) 287 288 yield kind, data, pos 289 290 def _prepare(self, stream): 291 return Template._prepare(self, 292 self._extract_includes(self._interpolate_attrs(stream)) 293 ) 294 295 def add_directives(self, namespace, factory): 296 """Register a custom `DirectiveFactory` for a given namespace. 297 298 :param namespace: the namespace URI 299 :type namespace: `basestring` 300 :param factory: the directive factory to register 301 :type factory: `DirectiveFactory` 302 :since: version 0.6 303 """ 304 assert not self._prepared, 'Too late for adding directives, ' \ 305 'template already prepared' 306 self._stream = self._extract_directives(self._stream, namespace, 307 factory) 223 308 224 309 def _match(self, stream, ctxt, start=0, end=None, **vars): -
trunk/genshi/template/tests/directives.py
r876 r954 505 505 try: 506 506 MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/"> 507 <py:for each="">508 empty509 </py:for>510 </doc>""", filename='test.html')507 <py:for each=""> 508 empty 509 </py:for> 510 </doc>""", filename='test.html').generate() 511 511 self.fail('ExpectedTemplateSyntaxError') 512 512 except TemplateSyntaxError, e: … … 964 964 def test_as_element(self): 965 965 try: 966 tmpl =MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">966 MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/"> 967 967 <py:content foo="">Foo</py:content> 968 </doc>""", filename='test.html') 968 </doc>""", filename='test.html').generate() 969 969 self.fail('Expected TemplateSyntaxError') 970 970 except TemplateSyntaxError, e: … … 982 982 """ 983 983 try: 984 tmpl =MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/">984 MarkupTemplate("""<doc xmlns:py="http://genshi.edgewall.org/"> 985 985 <elem py:replace="">Foo</elem> 986 </doc>""", filename='test.html') 986 </doc>""", filename='test.html').generate() 987 987 self.fail('Expected TemplateSyntaxError') 988 988 except TemplateSyntaxError, e: -
trunk/genshi/template/tests/markup.py
r897 r954 87 87 xml = """<p xmlns:py="http://genshi.edgewall.org/" py:if="bar'" />""" 88 88 try: 89 tmpl = MarkupTemplate(xml, filename='test.html') 90 self.fail('Expected SyntaxError')89 tmpl = MarkupTemplate(xml, filename='test.html').generate() 90 self.fail('Expected TemplateSyntaxError') 91 91 except TemplateSyntaxError, e: 92 92 self.assertEqual('test.html', e.filename) … … 99 99 try: 100 100 tmpl = MarkupTemplate(xml, filename='test.html') 101 self.fail('Expected SyntaxError')101 self.fail('Expected TemplateSyntaxError') 102 102 except TemplateSyntaxError, e: 103 103 self.assertEqual('test.html', e.filename) … … 112 112 try: 113 113 tmpl = MarkupTemplate(xml, filename='test.html') 114 self.fail('Expected SyntaxError')114 self.fail('Expected TemplateSyntaxError') 115 115 except TemplateSyntaxError, e: 116 116 self.assertEqual('test.html', e.filename) … … 131 131 def test_text_noescape_quotes(self): 132 132 """ 133 Verify that outputting context data in text nodes doesn't escape quotes. 133 Verify that outputting context data in text nodes doesn't escape 134 quotes. 134 135 """ 135 136 tmpl = MarkupTemplate("""<div xmlns:py="http://genshi.edgewall.org/"> -
trunk/genshi/template/text.py
r835 r954 217 217 218 218 elif command: 219 cls = self. _dir_by_name.get(command)219 cls = self.get_directive(command) 220 220 if cls is None: 221 221 raise BadDirectiveError(command) … … 313 313 stream.append((INCLUDE, (value.strip(), None, []), pos)) 314 314 elif command != '#': 315 cls = self. _dir_by_name.get(command)315 cls = self.get_directive(command) 316 316 if cls is None: 317 317 raise BadDirectiveError(command)
