| Index: tools/json_schema_compiler/code.py
 | 
| diff --git a/tools/json_schema_compiler/code.py b/tools/json_schema_compiler/code.py
 | 
| index c19029a1ef0a2b3ba5135cbfc57dd9b12b0d08cb..e9ade79a6ff6ff9b9072301939fd45a7d77f0caf 100644
 | 
| --- a/tools/json_schema_compiler/code.py
 | 
| +++ b/tools/json_schema_compiler/code.py
 | 
| @@ -2,6 +2,8 @@
 | 
|  # Use of this source code is governed by a BSD-style license that can be
 | 
|  # found in the LICENSE file.
 | 
|  
 | 
| +import itertools
 | 
| +
 | 
|  class Code(object):
 | 
|    """A convenience object for constructing code.
 | 
|  
 | 
| @@ -10,14 +12,19 @@ class Code(object):
 | 
|    """
 | 
|    def __init__(self, indent_size=2, comment_length=80):
 | 
|      self._code = []
 | 
| +    self._substitute = []
 | 
|      self._indent_level = 0
 | 
|      self._indent_size = indent_size
 | 
|      self._comment_length = comment_length
 | 
|  
 | 
| -  def Append(self, line=''):
 | 
| +  def Append(self, line='', substitute=True):
 | 
|      """Appends a line of code at the current indent level or just a newline if
 | 
|      line is not specified. Trailing whitespace is stripped.
 | 
| +
 | 
| +    substitute: indicated whether this line should be affected by
 | 
| +    code.Substitute().
 | 
|      """
 | 
| +    self._substitute.append(substitute)
 | 
|      self._code.append(((' ' * self._indent_level) + line).rstrip())
 | 
|      return self
 | 
|  
 | 
| @@ -37,12 +44,16 @@ class Code(object):
 | 
|      if not isinstance(obj, Code):
 | 
|        raise TypeError(type(obj))
 | 
|      assert self is not obj
 | 
| -    for line in obj._code:
 | 
| +    for substitute, line in itertools.izip(obj._substitute, obj._code):
 | 
|        try:
 | 
|          # line % () will fail if any substitution tokens are left in line
 | 
| -        self._code.append(((' ' * self._indent_level) + line % ()).rstrip())
 | 
| +        if substitute:
 | 
| +          line %= ()
 | 
|        except TypeError:
 | 
|          raise TypeError('Unsubstituted value when concatting\n' + line)
 | 
| +      except ValueError:
 | 
| +        raise ValueError('Stray % character when concatting\n' + line)
 | 
| +      self.Append(line, substitute)
 | 
|  
 | 
|      return self
 | 
|  
 | 
| @@ -66,13 +77,13 @@ class Code(object):
 | 
|      self.Append(line)
 | 
|      return self
 | 
|  
 | 
| -  # TODO(calamity): Make comment its own class or something and Render at
 | 
| -  # self.Render() time
 | 
|    def Comment(self, comment):
 | 
|      """Adds the given string as a comment.
 | 
|  
 | 
|      Will split the comment if it's too long. Use mainly for variable length
 | 
|      comments. Otherwise just use code.Append('// ...') for comments.
 | 
| +
 | 
| +    Unaffected by code.Substitute().
 | 
|      """
 | 
|      comment_symbol = '// '
 | 
|      max_len = self._comment_length - self._indent_level - len(comment_symbol)
 | 
| @@ -84,8 +95,8 @@ class Code(object):
 | 
|          comment = comment[last_space + 1:]
 | 
|        else:
 | 
|          comment = comment[max_len:]
 | 
| -      self.Append(comment_symbol + line)
 | 
| -    self.Append(comment_symbol + comment)
 | 
| +      self.Append(comment_symbol + line, substitute=False)
 | 
| +    self.Append(comment_symbol + comment, substitute=False)
 | 
|      return self
 | 
|  
 | 
|    def Substitute(self, d):
 | 
| @@ -100,12 +111,13 @@ class Code(object):
 | 
|      if not isinstance(d, dict):
 | 
|        raise TypeError('Passed argument is not a dictionary: ' + d)
 | 
|      for i, line in enumerate(self._code):
 | 
| -      # Only need to check %s because arg is a dict and python will allow
 | 
| -      # '%s %(named)s' but just about nothing else
 | 
| -      if '%s' in self._code[i] or '%r' in self._code[i]:
 | 
| -        raise TypeError('"%s" or "%r" found in substitution. '
 | 
| -                        'Named arguments only. Use "%" to escape')
 | 
| -      self._code[i] = line % d
 | 
| +      if self._substitute[i]:
 | 
| +        # Only need to check %s because arg is a dict and python will allow
 | 
| +        # '%s %(named)s' but just about nothing else
 | 
| +        if '%s' in self._code[i] or '%r' in self._code[i]:
 | 
| +          raise TypeError('"%s" or "%r" found in substitution. '
 | 
| +                          'Named arguments only. Use "%" to escape')
 | 
| +        self._code[i] = line % d
 | 
|      return self
 | 
|  
 | 
|    def Render(self):
 | 
| 
 |