Changeset 25

Show
Ignore:
Timestamp:
19/08/04 12:54:38 (4 years ago)
Author:
steve
Message:

#include

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/airspeed.py

    r24 r25  
    66except ImportError: import StringIO 
    77 
    8 __all__ = ['TemplateSyntaxError', 'Template'] 
     8__all__ = ['Template', 'TemplateError', 'TemplateSyntaxError', 'FileLoader'] 
    99 
    1010 
     
    1818        self.root_element = None 
    1919 
    20     def merge(self, namespace): 
     20    def merge(self, namespace, loader=None): 
    2121        output = StringIO.StringIO() 
    22         self.merge_to(namespace, output
     22        self.merge_to(namespace, output, loader
    2323        return output.getvalue() 
    2424 
    25     def merge_to(self, namespace, fileobj): 
     25    def merge_to(self, namespace, fileobj, loader=None): 
     26        if loader is None: loader = NullLoader() 
    2627        if not self.root_element: 
    2728            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 
     31class TemplateError(Exception): 
     32    pass 
     33 
     34class TemplateSyntaxError(TemplateError): 
    3235    def __init__(self, element, expected): 
    3336        self.element = element 
     
    4952        caret_pos = self.column 
    5053        return [error_line, ' ' * (caret_pos - 1) + '^'] 
     54 
     55 
     56class 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 
     64class 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() 
    5174 
    5275 
     
    155178        self.text = self.ESCAPED_CHAR.sub(unescape, text) 
    156179 
    157     def evaluate(self, namespace, stream): 
     180    def evaluate(self, namespace, stream, loader): 
    158181        stream.write(self.text) 
    159182 
     
    270293        if self.braces: self.require_match(self.CLOSING_BRACE, '}') 
    271294 
    272     def evaluate(self, namespace, stream): 
     295    def evaluate(self, namespace, stream, loader): 
    273296        value = self.expression.calculate(namespace) 
    274297        if value is None: 
     
    288311 
    289312class Null: 
    290     def evaluate(self, namespace, stream): pass 
     313    def evaluate(self, namespace, stream, loader): pass 
    291314 
    292315 
     
    375398        self.require_next_element(End, '#else, #elseif or #end') 
    376399 
    377     def evaluate(self, namespace, stream): 
     400    def evaluate(self, namespace, stream, loader): 
    378401        if self.condition.calculate(namespace): 
    379             self.block.evaluate(namespace, stream
     402            self.block.evaluate(namespace, stream, loader
    380403        else: 
    381404            for elseif in self.elseifs: 
    382405                if elseif.calculate(namespace): 
    383                     elseif.evaluate(namespace, stream
     406                    elseif.evaluate(namespace, stream, loader
    384407                    return 
    385             self.else_block.evaluate(namespace, stream
     408            self.else_block.evaluate(namespace, stream, loader
    386409 
    387410 
     
    421444        self.require_next_element(End, 'block') 
    422445 
    423     def evaluate(self, namespace, stream): 
     446    def evaluate(self, namespace, stream, loader): 
    424447        macro_key = '#' + self.macro_name.lower() 
    425448        if macro_key in namespace: 
     
    427450        namespace[macro_key] = self 
    428451 
    429     def execute_macro(self, namespace, stream, arg_value_elements): 
     452    def execute_macro(self, namespace, stream, arg_value_elements, loader): 
    430453        if len(arg_value_elements) != len(self.arg_names): 
    431454            raise Exception("expected %d arguments, got %d" % (len(self.arg_names), len(arg_value_elements))) 
     
    433456        for arg_name, arg_value in zip(self.arg_names, arg_value_elements): 
    434457            macro_namespace[arg_name] = arg_value.calculate(namespace) 
    435         self.block.evaluate(macro_namespace, stream
     458        self.block.evaluate(macro_namespace, stream, loader
    436459 
    437460 
     
    455478        self.require_match(self.CLOSE_PAREN, 'argument value or )') 
    456479 
    457     def evaluate(self, namespace, stream): 
     480    def evaluate(self, namespace, stream, loader): 
    458481        try: macro = namespace['#' + self.macro_name] 
    459482        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 
     486class 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) 
    461499 
    462500 
     
    468506        self.assignment = self.require_next_element(Assignment, 'assignment') 
    469507 
    470     def evaluate(self, namespace, stream): 
     508    def evaluate(self, namespace, stream, loader): 
    471509        self.assignment.calculate(namespace) 
    472510 
     
    484522        self.require_next_element(End, '#end') 
    485523 
    486     def evaluate(self, namespace, stream): 
     524    def evaluate(self, namespace, stream, loader): 
    487525        iterable = self.value.calculate(namespace) 
    488526        counter = 1 
     
    491529            namespace['velocityCount'] = counter 
    492530            namespace[self.loop_var_name] = item 
    493             self.block.evaluate(namespace, stream
     531            self.block.evaluate(namespace, stream, loader
    494532            counter += 1 
    495533 
     
    501539            raise self.syntax_error('block element') 
    502540 
    503     def evaluate(self, namespace, stream): 
     541    def evaluate(self, namespace, stream, loader): 
    504542        namespace = LocalNamespace(namespace) 
    505         self.block.evaluate(namespace, stream
     543        self.block.evaluate(namespace, stream, loader
    506544 
    507545 
     
    510548        self.children = [] 
    511549        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))) 
    513551            except NoMatch: break 
    514552 
    515     def evaluate(self, namespace, stream): 
     553    def evaluate(self, namespace, stream, loader): 
    516554        for child in self.children: 
    517             child.evaluate(namespace, stream
     555            child.evaluate(namespace, stream, loader
  • trunk/airspeed_test.py

    r24 r25  
    326326        template = airspeed.Template('#macro ( hello)hi#end#macro(hello)again#end') 
    327327        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()) 
    328339 
    329340# 
     
    342353#  Q. What is scope of #set ($customer.Name = 'john')  ??? 
    343354#  Scope of #set across if/elseif/else? 
     355#  Scope of namespace for #include etc 
    344356# 
    345357