|  | #!/usr/bin/env python3 | 
|  | # Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file | 
|  | # for details. All rights reserved. Use of this source code is governed by a | 
|  | # BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | # Template loader and preprocessor. | 
|  | # | 
|  | # Preprocessor language: | 
|  | # | 
|  | #   //$ Comment line removed by preprocessor | 
|  | #   $if VAR | 
|  | #   $else | 
|  | #   $endif | 
|  | # | 
|  | # VAR must be defined in the conditions dictionary. | 
|  |  | 
|  | import os | 
|  |  | 
|  |  | 
|  | class TemplateLoader(object): | 
|  | """Loads template files from a path.""" | 
|  |  | 
|  | def __init__(self, root, subpaths, conditions={}): | 
|  | """Initializes loader. | 
|  |  | 
|  | Args: | 
|  | root - a string, the directory under which the templates are stored. | 
|  | subpaths - a list of strings, subpaths of root in search order. | 
|  | conditions - a dictionary from strings to booleans.  Any conditional | 
|  | expression must be a key in the map. | 
|  | """ | 
|  | self._root = root | 
|  | self._subpaths = subpaths | 
|  | self._conditions = conditions | 
|  | self._cache = {} | 
|  |  | 
|  | def TryLoad(self, name, more_conditions={}): | 
|  | """Returns content of template file as a string, or None of not found.""" | 
|  | conditions = dict(self._conditions, **more_conditions) | 
|  | cache_key = (name, tuple(sorted(conditions.items()))) | 
|  | if cache_key in self._cache: | 
|  | return self._cache[cache_key] | 
|  |  | 
|  | for subpath in self._subpaths: | 
|  | template_file = os.path.join(self._root, subpath, name) | 
|  | if os.path.exists(template_file): | 
|  | template = ''.join(open(template_file).readlines()) | 
|  | template = self._Preprocess(template, template_file, conditions) | 
|  | self._cache[cache_key] = template | 
|  | return template | 
|  |  | 
|  | return None | 
|  |  | 
|  | def Load(self, name, more_conditions={}): | 
|  | """Returns contents of template file as a string, or raises an exception.""" | 
|  | template = self.TryLoad(name, more_conditions) | 
|  | if template is not None:  # Can be empty string | 
|  | return template | 
|  | raise Exception("Could not find template '%s' on %s / %s" % | 
|  | (name, self._root, self._subpaths)) | 
|  |  | 
|  | def _Preprocess(self, template, filename, conditions): | 
|  |  | 
|  | def error(lineno, message): | 
|  | raise Exception('%s:%s: %s' % (filename, lineno, message)) | 
|  |  | 
|  | lines = template.splitlines(True) | 
|  | out = [] | 
|  |  | 
|  | condition_stack = [] | 
|  | active = True | 
|  | seen_else = False | 
|  |  | 
|  | for (lineno, full_line) in enumerate(lines): | 
|  | line = full_line.strip() | 
|  |  | 
|  | if line.startswith('$'): | 
|  | words = line.split() | 
|  | directive = words[0] | 
|  |  | 
|  | if directive == '$if': | 
|  | if len(words) != 2: | 
|  | error(lineno, '$if does not have single variable') | 
|  | variable = words[1] | 
|  | if variable in conditions: | 
|  | condition_stack.append((active, seen_else)) | 
|  | active = active and conditions[variable] | 
|  | seen_else = False | 
|  | else: | 
|  | error(lineno, "Unknown $if variable '%s'" % variable) | 
|  |  | 
|  | elif directive == '$else': | 
|  | if not condition_stack: | 
|  | error(lineno, '$else without $if') | 
|  | if seen_else: | 
|  | raise error(lineno, 'Double $else') | 
|  | seen_else = True | 
|  | (parentactive, | 
|  | _) = condition_stack[len(condition_stack) - 1] | 
|  | active = not active and parentactive | 
|  |  | 
|  | elif directive == '$endif': | 
|  | if not condition_stack: | 
|  | error(lineno, '$endif without $if') | 
|  | (active, seen_else) = condition_stack.pop() | 
|  |  | 
|  | else: | 
|  | # Something else, like '$!MEMBERS' | 
|  | if active: | 
|  | out.append(full_line) | 
|  | elif line.startswith('//$'): | 
|  | pass  # Ignore pre-processor comment. | 
|  |  | 
|  | else: | 
|  | if active: | 
|  | out.append(full_line) | 
|  | continue | 
|  |  | 
|  | if condition_stack: | 
|  | error(len(lines), 'Unterminated $if') | 
|  |  | 
|  | return ''.join(out) |