Changeset 25
- Timestamp:
- 19/08/04 12:54:38 (4 years ago)
- Files:
-
- trunk/airspeed.py (modified) (16 diffs)
- trunk/airspeed_test.py (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/airspeed.py
r24 r25 6 6 except ImportError: import StringIO 7 7 8 __all__ = ['Template SyntaxError', 'Template']8 __all__ = ['Template', 'TemplateError', 'TemplateSyntaxError', 'FileLoader'] 9 9 10 10 … … 18 18 self.root_element = None 19 19 20 def merge(self, namespace ):20 def merge(self, namespace, loader=None): 21 21 output = StringIO.StringIO() 22 self.merge_to(namespace, output )22 self.merge_to(namespace, output, loader) 23 23 return output.getvalue() 24 24 25 def merge_to(self, namespace, fileobj): 25 def merge_to(self, namespace, fileobj, loader=None): 26 if loader is None: loader = NullLoader() 26 27 if not self.root_element: 27 28 self.root_element = TemplateBody(self.content) 28 self.root_element.evaluate(namespace, fileobj) 29 30 31 class TemplateSyntaxError(Exception): 29 self.root_element.evaluate(namespace, fileobj, loader) 30 31 class TemplateError(Exception): 32 pass 33 34 class TemplateSyntaxError(TemplateError): 32 35 def __init__(self, element, expected): 33 36 self.element = element … … 49 52 caret_pos = self.column 50 53 return [error_line, ' ' * (caret_pos - 1) + '^'] 54 55 56 class NullLoader: 57 def merge_text(self, name, stream): 58 raise self.load_template(name) 59 60 def load_template(self, name): 61 raise TemplateError("no loader available for '%s'" % name) 62 63 64 class FileLoader: 65 def merge_text(self, name, stream): 66 f = open(name) 67 try: stream.write(f.read()) 68 finally: f.close() 69 70 def load_template(self, name): 71 f = open(name) 72 try: return Template(f.read()) 73 finally: f.close() 51 74 52 75 … … 155 178 self.text = self.ESCAPED_CHAR.sub(unescape, text) 156 179 157 def evaluate(self, namespace, stream ):180 def evaluate(self, namespace, stream, loader): 158 181 stream.write(self.text) 159 182 … … 270 293 if self.braces: self.require_match(self.CLOSING_BRACE, '}') 271 294 272 def evaluate(self, namespace, stream ):295 def evaluate(self, namespace, stream, loader): 273 296 value = self.expression.calculate(namespace) 274 297 if value is None: … … 288 311 289 312 class Null: 290 def evaluate(self, namespace, stream ): pass313 def evaluate(self, namespace, stream, loader): pass 291 314 292 315 … … 375 398 self.require_next_element(End, '#else, #elseif or #end') 376 399 377 def evaluate(self, namespace, stream ):400 def evaluate(self, namespace, stream, loader): 378 401 if self.condition.calculate(namespace): 379 self.block.evaluate(namespace, stream )402 self.block.evaluate(namespace, stream, loader) 380 403 else: 381 404 for elseif in self.elseifs: 382 405 if elseif.calculate(namespace): 383 elseif.evaluate(namespace, stream )406 elseif.evaluate(namespace, stream, loader) 384 407 return 385 self.else_block.evaluate(namespace, stream )408 self.else_block.evaluate(namespace, stream, loader) 386 409 387 410 … … 421 444 self.require_next_element(End, 'block') 422 445 423 def evaluate(self, namespace, stream ):446 def evaluate(self, namespace, stream, loader): 424 447 macro_key = '#' + self.macro_name.lower() 425 448 if macro_key in namespace: … … 427 450 namespace[macro_key] = self 428 451 429 def execute_macro(self, namespace, stream, arg_value_elements ):452 def execute_macro(self, namespace, stream, arg_value_elements, loader): 430 453 if len(arg_value_elements) != len(self.arg_names): 431 454 raise Exception("expected %d arguments, got %d" % (len(self.arg_names), len(arg_value_elements))) … … 433 456 for arg_name, arg_value in zip(self.arg_names, arg_value_elements): 434 457 macro_namespace[arg_name] = arg_value.calculate(namespace) 435 self.block.evaluate(macro_namespace, stream )458 self.block.evaluate(macro_namespace, stream, loader) 436 459 437 460 … … 455 478 self.require_match(self.CLOSE_PAREN, 'argument value or )') 456 479 457 def evaluate(self, namespace, stream ):480 def evaluate(self, namespace, stream, loader): 458 481 try: macro = namespace['#' + self.macro_name] 459 482 except KeyError: raise Exception('no such macro: ' + self.macro_name) 460 macro.execute_macro(namespace, stream, self.args) 483 macro.execute_macro(namespace, stream, self.args, loader) 484 485 486 class IncludeDirective(_Element): 487 START = re.compile(r'#include\b(.*)', re.S + re.I) 488 OPEN_PAREN = re.compile(r'[ \t]*\(\s*(.*)$', re.S) 489 CLOSE_PAREN = re.compile(r'[ \t]*\)(.*)$', re.S) 490 491 def parse(self): 492 self.identity_match(self.START) 493 self.require_match(self.OPEN_PAREN, '(') 494 self.name = self.require_next_element((StringLiteral, SimpleReference), 'template name') 495 self.require_match(self.CLOSE_PAREN, ')') 496 497 def evaluate(self, namespace, stream, loader): 498 template = loader.merge_text(self.name.calculate(namespace), stream) 461 499 462 500 … … 468 506 self.assignment = self.require_next_element(Assignment, 'assignment') 469 507 470 def evaluate(self, namespace, stream ):508 def evaluate(self, namespace, stream, loader): 471 509 self.assignment.calculate(namespace) 472 510 … … 484 522 self.require_next_element(End, '#end') 485 523 486 def evaluate(self, namespace, stream ):524 def evaluate(self, namespace, stream, loader): 487 525 iterable = self.value.calculate(namespace) 488 526 counter = 1 … … 491 529 namespace['velocityCount'] = counter 492 530 namespace[self.loop_var_name] = item 493 self.block.evaluate(namespace, stream )531 self.block.evaluate(namespace, stream, loader) 494 532 counter += 1 495 533 … … 501 539 raise self.syntax_error('block element') 502 540 503 def evaluate(self, namespace, stream ):541 def evaluate(self, namespace, stream, loader): 504 542 namespace = LocalNamespace(namespace) 505 self.block.evaluate(namespace, stream )543 self.block.evaluate(namespace, stream, loader) 506 544 507 545 … … 510 548 self.children = [] 511 549 while True: 512 try: self.children.append(self.next_element((Text, Placeholder, Comment, IfDirective, SetDirective, ForeachDirective, MacroDefinition, MacroCall)))550 try: self.children.append(self.next_element((Text, Placeholder, Comment, IfDirective, SetDirective, ForeachDirective, IncludeDirective, MacroDefinition, MacroCall))) 513 551 except NoMatch: break 514 552 515 def evaluate(self, namespace, stream ):553 def evaluate(self, namespace, stream, loader): 516 554 for child in self.children: 517 child.evaluate(namespace, stream )555 child.evaluate(namespace, stream, loader) trunk/airspeed_test.py
r24 r25 326 326 template = airspeed.Template('#macro ( hello)hi#end#macro(hello)again#end') 327 327 self.assertRaises(Exception, template.merge, {}) ## Should this be TemplateSyntaxError? 328 329 def test_include_directive_gives_error_if_no_loader_provided(self): 330 template = airspeed.Template('#include ("foo.tmpl")') 331 self.assertRaises(airspeed.TemplateError, template.merge, {}) 332 333 def test_include_directive_yield_loader_error_if_included_content_not_found(self): 334 class BrokenLoader: 335 def merge_text(self, name, stream): 336 raise IOError(name) 337 template = airspeed.Template('#include ("foo.tmpl")') 338 self.assertRaises(IOError, template.merge, {}, loader=BrokenLoader()) 328 339 329 340 # … … 342 353 # Q. What is scope of #set ($customer.Name = 'john') ??? 343 354 # Scope of #set across if/elseif/else? 355 # Scope of namespace for #include etc 344 356 # 345 357
