mercurial/templater.py
changeset 8218 e61cb2813d2a
parent 8198 cf9accffd0b3
child 8223 02145b700fe4
equal deleted inserted replaced
8217:d895158fe8af 8218:e61cb2813d2a
    19             raise SyntaxError(_('unmatched quotes'))
    19             raise SyntaxError(_('unmatched quotes'))
    20         return s[1:-1].decode('string_escape')
    20         return s[1:-1].decode('string_escape')
    21 
    21 
    22     return s.decode('string_escape')
    22     return s.decode('string_escape')
    23 
    23 
    24 class templater(object):
    24 class engine(object):
    25     '''template expansion engine.
    25     '''template expansion engine.
    26 
    26 
    27     template expansion works like this. a map file contains key=value
    27     template expansion works like this. a map file contains key=value
    28     pairs. if value is quoted, it is treated as string. otherwise, it
    28     pairs. if value is quoted, it is treated as string. otherwise, it
    29     is treated as name of template file.
    29     is treated as name of template file.
    41     filter uses function to transform value. syntax is
    41     filter uses function to transform value. syntax is
    42     {key|filter1|filter2|...}.'''
    42     {key|filter1|filter2|...}.'''
    43 
    43 
    44     template_re = re.compile(r"(?:(?:#(?=[\w\|%]+#))|(?:{(?=[\w\|%]+})))"
    44     template_re = re.compile(r"(?:(?:#(?=[\w\|%]+#))|(?:{(?=[\w\|%]+})))"
    45                              r"(\w+)(?:(?:%(\w+))|((?:\|\w+)*))[#}]")
    45                              r"(\w+)(?:(?:%(\w+))|((?:\|\w+)*))[#}]")
       
    46 
       
    47     def __init__(self, loader, filters={}, defaults={}):
       
    48         self.loader = loader
       
    49         self.filters = filters
       
    50         self.defaults = defaults
       
    51 
       
    52     def process(self, t, map):
       
    53         '''Perform expansion. t is name of map element to expand. map contains
       
    54         added elements for use during expansion. Is a generator.'''
       
    55         tmpl = self.loader(t)
       
    56         iters = [self._process(tmpl, map)]
       
    57         while iters:
       
    58             try:
       
    59                 item = iters[0].next()
       
    60             except StopIteration:
       
    61                 iters.pop(0)
       
    62                 continue
       
    63             if isinstance(item, str):
       
    64                 yield item
       
    65             elif item is None:
       
    66                 yield ''
       
    67             elif hasattr(item, '__iter__'):
       
    68                 iters.insert(0, iter(item))
       
    69             else:
       
    70                 yield str(item)
       
    71 
       
    72     def _process(self, tmpl, map):
       
    73         '''Render a template. Returns a generator.'''
       
    74         while tmpl:
       
    75             m = self.template_re.search(tmpl)
       
    76             if not m:
       
    77                 yield tmpl
       
    78                 break
       
    79 
       
    80             start, end = m.span(0)
       
    81             key, format, fl = m.groups()
       
    82 
       
    83             if start:
       
    84                 yield tmpl[:start]
       
    85             tmpl = tmpl[end:]
       
    86 
       
    87             if key in map:
       
    88                 v = map[key]
       
    89             else:
       
    90                 v = self.defaults.get(key, "")
       
    91             if callable(v):
       
    92                 v = v(**map)
       
    93             if format:
       
    94                 if not hasattr(v, '__iter__'):
       
    95                     raise SyntaxError(_("Error expanding '%s%%%s'")
       
    96                                       % (key, format))
       
    97                 lm = map.copy()
       
    98                 for i in v:
       
    99                     lm.update(i)
       
   100                     yield self.process(format, lm)
       
   101             else:
       
   102                 if fl:
       
   103                     for f in fl.split("|")[1:]:
       
   104                         v = self.filters[f](v)
       
   105                 yield v
       
   106 
       
   107 class templater(object):
    46 
   108 
    47     def __init__(self, mapfile, filters={}, defaults={}, cache={},
   109     def __init__(self, mapfile, filters={}, defaults={}, cache={},
    48                  minchunk=1024, maxchunk=65536):
   110                  minchunk=1024, maxchunk=65536):
    49         '''set up template engine.
   111         '''set up template engine.
    50         mapfile is name of file to read map definitions from.
   112         mapfile is name of file to read map definitions from.
    77                 self.map[key] = os.path.join(self.base, val)
   139                 self.map[key] = os.path.join(self.base, val)
    78 
   140 
    79     def __contains__(self, key):
   141     def __contains__(self, key):
    80         return key in self.cache or key in self.map
   142         return key in self.cache or key in self.map
    81 
   143 
    82     def _template(self, t):
   144     def load(self, t):
    83         '''Get the template for the given template name. Use a local cache.'''
   145         '''Get the template for the given template name. Use a local cache.'''
    84         if not t in self.cache:
   146         if not t in self.cache:
    85             try:
   147             try:
    86                 self.cache[t] = file(self.map[t]).read()
   148                 self.cache[t] = file(self.map[t]).read()
    87             except IOError, inst:
   149             except IOError, inst:
    88                 raise IOError(inst.args[0], _('template file %s: %s') %
   150                 raise IOError(inst.args[0], _('template file %s: %s') %
    89                               (self.map[t], inst.args[1]))
   151                               (self.map[t], inst.args[1]))
    90         return self.cache[t]
   152         return self.cache[t]
    91 
   153 
    92     def _process(self, tmpl, map):
       
    93         '''Render a template. Returns a generator.'''
       
    94         while tmpl:
       
    95             m = self.template_re.search(tmpl)
       
    96             if not m:
       
    97                 yield tmpl
       
    98                 break
       
    99 
       
   100             start, end = m.span(0)
       
   101             key, format, fl = m.groups()
       
   102 
       
   103             if start:
       
   104                 yield tmpl[:start]
       
   105             tmpl = tmpl[end:]
       
   106 
       
   107             if key in map:
       
   108                 v = map[key]
       
   109             else:
       
   110                 v = self.defaults.get(key, "")
       
   111             if callable(v):
       
   112                 v = v(**map)
       
   113             if format:
       
   114                 if not hasattr(v, '__iter__'):
       
   115                     raise SyntaxError(_("Error expanding '%s%%%s'")
       
   116                                       % (key, format))
       
   117                 lm = map.copy()
       
   118                 for i in v:
       
   119                     lm.update(i)
       
   120                     t = self._template(format)
       
   121                     yield self._process(t, lm)
       
   122             else:
       
   123                 if fl:
       
   124                     for f in fl.split("|")[1:]:
       
   125                         v = self.filters[f](v)
       
   126                 yield v
       
   127 
       
   128     def __call__(self, t, **map):
   154     def __call__(self, t, **map):
   129         stream = self.expand(t, **map)
   155         proc = engine(self.load, self.filters, self.defaults)
       
   156         stream = proc.process(t, map)
   130         if self.minchunk:
   157         if self.minchunk:
   131             stream = util.increasingchunks(stream, min=self.minchunk,
   158             stream = util.increasingchunks(stream, min=self.minchunk,
   132                                            max=self.maxchunk)
   159                                            max=self.maxchunk)
   133         return stream
   160         return stream
   134 
       
   135     def expand(self, t, **map):
       
   136         '''Perform expansion. t is name of map element to expand. map contains
       
   137         added elements for use during expansion. Is a generator.'''
       
   138         tmpl = self._template(t)
       
   139         iters = [self._process(tmpl, map)]
       
   140         while iters:
       
   141             try:
       
   142                 item = iters[0].next()
       
   143             except StopIteration:
       
   144                 iters.pop(0)
       
   145                 continue
       
   146             if isinstance(item, str):
       
   147                 yield item
       
   148             elif item is None:
       
   149                 yield ''
       
   150             elif hasattr(item, '__iter__'):
       
   151                 iters.insert(0, iter(item))
       
   152             else:
       
   153                 yield str(item)
       
   154 
   161 
   155 def templatepath(name=None):
   162 def templatepath(name=None):
   156     '''return location of template file or directory (if no name).
   163     '''return location of template file or directory (if no name).
   157     returns None if not found.'''
   164     returns None if not found.'''
   158     normpaths = []
   165     normpaths = []