comparison mercurial/templateutil.py @ 37273:83e1bbd48991

templater: define interface for objects requiring unwraphybrid() Prepares for introducing another hybrid-like data type. show() takes context as an argument so a wrapper class may render its items by pre-configured template: def show(self, context, mapping): return (context.expand(self._tmpl, mapping + lm) for lm in self._mappings)
author Yuya Nishihara <yuya@tcha.org>
date Sat, 17 Mar 2018 20:52:50 +0900
parents 7d3bc1d4e871
children 8c84dc8264dc
comparison
equal deleted inserted replaced
37272:7d3bc1d4e871 37273:83e1bbd48991
5 # This software may be used and distributed according to the terms of the 5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version. 6 # GNU General Public License version 2 or any later version.
7 7
8 from __future__ import absolute_import 8 from __future__ import absolute_import
9 9
10 import abc
10 import types 11 import types
11 12
12 from .i18n import _ 13 from .i18n import _
13 from . import ( 14 from . import (
14 error, 15 error,
24 pass 25 pass
25 26
26 class TemplateNotFound(error.Abort): 27 class TemplateNotFound(error.Abort):
27 pass 28 pass
28 29
30 class wrapped(object):
31 """Object requiring extra conversion prior to displaying or processing
32 as value"""
33
34 __metaclass__ = abc.ABCMeta
35
36 @abc.abstractmethod
37 def show(self, context, mapping):
38 """Return a bytes or (possibly nested) generator of bytes representing
39 the underlying object
40
41 A pre-configured template may be rendered if the underlying object is
42 not printable.
43 """
44
29 # stub for representing a date type; may be a real date type that can 45 # stub for representing a date type; may be a real date type that can
30 # provide a readable string value 46 # provide a readable string value
31 class date(object): 47 class date(object):
32 pass 48 pass
33 49
34 class hybrid(object): 50 class hybrid(wrapped):
35 """Wrapper for list or dict to support legacy template 51 """Wrapper for list or dict to support legacy template
36 52
37 This class allows us to handle both: 53 This class allows us to handle both:
38 - "{files}" (legacy command-line-specific list hack) and 54 - "{files}" (legacy command-line-specific list hack) and
39 - "{files % '{file}\n'}" (hgweb-style with inlining and function support) 55 - "{files % '{file}\n'}" (hgweb-style with inlining and function support)
58 yield self.joinfmt(x) 74 yield self.joinfmt(x)
59 def itermaps(self): 75 def itermaps(self):
60 makemap = self._makemap 76 makemap = self._makemap
61 for x in self._values: 77 for x in self._values:
62 yield makemap(x) 78 yield makemap(x)
79
80 def show(self, context, mapping):
81 # TODO: switch gen to (context, mapping) API?
82 gen = self.gen
83 if callable(gen):
84 return gen()
85 return gen
86
63 def __contains__(self, x): 87 def __contains__(self, x):
64 return x in self._values 88 return x in self._values
65 def __getitem__(self, key): 89 def __getitem__(self, key):
66 return self._values[key] 90 return self._values[key]
67 def __len__(self): 91 def __len__(self):
72 if name not in (r'get', r'items', r'iteritems', r'iterkeys', 96 if name not in (r'get', r'items', r'iteritems', r'iterkeys',
73 r'itervalues', r'keys', r'values'): 97 r'itervalues', r'keys', r'values'):
74 raise AttributeError(name) 98 raise AttributeError(name)
75 return getattr(self._values, name) 99 return getattr(self._values, name)
76 100
77 class mappable(object): 101 class mappable(wrapped):
78 """Wrapper for non-list/dict object to support map operation 102 """Wrapper for non-list/dict object to support map operation
79 103
80 This class allows us to handle both: 104 This class allows us to handle both:
81 - "{manifest}" 105 - "{manifest}"
82 - "{manifest % '{rev}:{node}'}" 106 - "{manifest % '{rev}:{node}'}"
100 def tomap(self): 124 def tomap(self):
101 return self._makemap(self._key) 125 return self._makemap(self._key)
102 126
103 def itermaps(self): 127 def itermaps(self):
104 yield self.tomap() 128 yield self.tomap()
129
130 def show(self, context, mapping):
131 # TODO: switch gen to (context, mapping) API?
132 gen = self.gen
133 if callable(gen):
134 return gen()
135 return gen
105 136
106 def hybriddict(data, key='key', value='value', fmt=None, gen=None): 137 def hybriddict(data, key='key', value='value', fmt=None, gen=None):
107 """Wrap data to support both dict-like and string-like operations""" 138 """Wrap data to support both dict-like and string-like operations"""
108 prefmt = pycompat.identity 139 prefmt = pycompat.identity
109 if fmt is None: 140 if fmt is None:
121 return hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % prefmt(x)) 152 return hybrid(gen, data, lambda x: {name: x}, lambda x: fmt % prefmt(x))
122 153
123 def unwraphybrid(context, mapping, thing): 154 def unwraphybrid(context, mapping, thing):
124 """Return an object which can be stringified possibly by using a legacy 155 """Return an object which can be stringified possibly by using a legacy
125 template""" 156 template"""
126 gen = getattr(thing, 'gen', None) 157 if not isinstance(thing, wrapped):
127 if gen is None:
128 return thing 158 return thing
129 if callable(gen): 159 return thing.show(context, mapping)
130 return gen()
131 return gen
132 160
133 def unwrapvalue(thing): 161 def unwrapvalue(thing):
134 """Move the inner value object out of the wrapper""" 162 """Move the inner value object out of the wrapper"""
135 if not util.safehasattr(thing, '_value'): 163 if not util.safehasattr(thing, '_value'):
136 return thing 164 return thing