comparison upmana/mercurial/templater.py @ 121:496dbf12a6cb alpha

Traipse Alpha 'OpenRPG' {091030-00} Traipse is a distribution of OpenRPG that is designed to be easy to setup and go. Traipse also makes it easy for developers to work on code without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the code. 'Ornery-Orc's main goal is to offer more advanced features and enhance the productivity of the user. Update Summary (Cleaning up for Beta): Adds Bookmarks (Alpha) with cool Smiley Star and Plus Symbol images! Changes made to the map for increased portability. SnowDog has changes planned in Core, though. Added an initial push to the BCG. Not much to see, just shows off how it is re-writing Main code. Fix to remote admin commands Minor fix to texted based server, works in /System/ folder Some Core changes to gametree to correctly disply Pretty Print, thanks David! Fix to Splitter Nodes not being created. Added images to Plugin Control panel for Autostart feature Fix to massive amounts of images loading; from Core fix to gsclient so with_statement imports Added 'boot' command to remote admin Prep work in Pass tool for remote admin rankings and different passwords, ei, Server, Admin, Moderator, etc. Remote Admin Commands more organized, more prep work. Added Confirmation window for sent nodes. Minor changes to allow for portability to an OpenSUSE linux OS (hopefully without breaking) {091028} Made changes to gametree to start working with Element Tree, mostly from Core Minor changes to Map to start working with Element Tree, from Core Preliminary changes to map efficiency, from FlexiRPG Miniatures Layer pop up box allows users to turn off Mini labels, from FlexiRPG Changes to main.py to start working with Element Tree {091029} Changes made to server to start working with Element Tree. Changes made to Meta Server Lib. Prepping test work for a multi meta network page. Minor bug fixed with mini to gametree Zoom Mouse plugin added. {091030} Getting ready for Beta. Server needs debugging so Alpha remains bugged. Plugin UI code cleaned. Auto start works with a graphic, pop-up asks to enable or disable plugin. Update Manager now has a partially working Status Bar. Status Bar captures terminal text, so Merc out put is visible. Manifest.xml file, will be renamed, is now much cleaner. Debug Console has a clear button and a Report Bug button. Prep work for a Term2Win class in Debug Console. Known: Current Alpha fails in Windows.
author sirebral
date Fri, 30 Oct 2009 22:21:40 -0500
parents
children
comparison
equal deleted inserted replaced
120:d86e762a994f 121:496dbf12a6cb
1 # templater.py - template expansion for output
2 #
3 # Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
4 #
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
7
8 from i18n import _
9 import re, sys, os
10 import util, config, templatefilters
11
12 path = ['templates', '../templates']
13 stringify = templatefilters.stringify
14
15 def parsestring(s, quoted=True):
16 '''parse a string using simple c-like syntax.
17 string must be in quotes if quoted is True.'''
18 if quoted:
19 if len(s) < 2 or s[0] != s[-1]:
20 raise SyntaxError(_('unmatched quotes'))
21 return s[1:-1].decode('string_escape')
22
23 return s.decode('string_escape')
24
25 class engine(object):
26 '''template expansion engine.
27
28 template expansion works like this. a map file contains key=value
29 pairs. if value is quoted, it is treated as string. otherwise, it
30 is treated as name of template file.
31
32 templater is asked to expand a key in map. it looks up key, and
33 looks for strings like this: {foo}. it expands {foo} by looking up
34 foo in map, and substituting it. expansion is recursive: it stops
35 when there is no more {foo} to replace.
36
37 expansion also allows formatting and filtering.
38
39 format uses key to expand each item in list. syntax is
40 {key%format}.
41
42 filter uses function to transform value. syntax is
43 {key|filter1|filter2|...}.'''
44
45 template_re = re.compile(r'{([\w\|%]+)}|#([\w\|%]+)#')
46
47 def __init__(self, loader, filters={}, defaults={}):
48 self.loader = loader
49 self.filters = filters
50 self.defaults = defaults
51 self.cache = {}
52
53 def process(self, t, map):
54 '''Perform expansion. t is name of map element to expand. map contains
55 added elements for use during expansion. Is a generator.'''
56 tmpl = self.loader(t)
57 iters = [self._process(tmpl, map)]
58 while iters:
59 try:
60 item = iters[0].next()
61 except StopIteration:
62 iters.pop(0)
63 continue
64 if isinstance(item, str):
65 yield item
66 elif item is None:
67 yield ''
68 elif hasattr(item, '__iter__'):
69 iters.insert(0, iter(item))
70 else:
71 yield str(item)
72
73 def _format(self, expr, get, map):
74 key, format = expr.split('%')
75 v = get(key)
76 if not hasattr(v, '__iter__'):
77 raise SyntaxError(_("error expanding '%s%%%s'") % (key, format))
78 lm = map.copy()
79 for i in v:
80 lm.update(i)
81 yield self.process(format, lm)
82
83 def _filter(self, expr, get, map):
84 if expr not in self.cache:
85 parts = expr.split('|')
86 val = parts[0]
87 try:
88 filters = [self.filters[f] for f in parts[1:]]
89 except KeyError, i:
90 raise SyntaxError(_("unknown filter '%s'") % i[0])
91 def apply(get):
92 x = get(val)
93 for f in filters:
94 x = f(x)
95 return x
96 self.cache[expr] = apply
97 return self.cache[expr](get)
98
99 def _process(self, tmpl, map):
100 '''Render a template. Returns a generator.'''
101
102 def get(key):
103 v = map.get(key)
104 if v is None:
105 v = self.defaults.get(key, '')
106 if hasattr(v, '__call__'):
107 v = v(**map)
108 return v
109
110 while tmpl:
111 m = self.template_re.search(tmpl)
112 if not m:
113 yield tmpl
114 break
115
116 start, end = m.span(0)
117 variants = m.groups()
118 expr = variants[0] or variants[1]
119
120 if start:
121 yield tmpl[:start]
122 tmpl = tmpl[end:]
123
124 if '%' in expr:
125 yield self._format(expr, get, map)
126 elif '|' in expr:
127 yield self._filter(expr, get, map)
128 else:
129 yield get(expr)
130
131 engines = {'default': engine}
132
133 class templater(object):
134
135 def __init__(self, mapfile, filters={}, defaults={}, cache={},
136 minchunk=1024, maxchunk=65536):
137 '''set up template engine.
138 mapfile is name of file to read map definitions from.
139 filters is dict of functions. each transforms a value into another.
140 defaults is dict of default map definitions.'''
141 self.mapfile = mapfile or 'template'
142 self.cache = cache.copy()
143 self.map = {}
144 self.base = (mapfile and os.path.dirname(mapfile)) or ''
145 self.filters = templatefilters.filters.copy()
146 self.filters.update(filters)
147 self.defaults = defaults
148 self.minchunk, self.maxchunk = minchunk, maxchunk
149 self.engines = {}
150
151 if not mapfile:
152 return
153 if not os.path.exists(mapfile):
154 raise util.Abort(_('style not found: %s') % mapfile)
155
156 conf = config.config()
157 conf.read(mapfile)
158
159 for key, val in conf[''].items():
160 if val[0] in "'\"":
161 try:
162 self.cache[key] = parsestring(val)
163 except SyntaxError, inst:
164 raise SyntaxError('%s: %s' %
165 (conf.source('', key), inst.args[0]))
166 else:
167 val = 'default', val
168 if ':' in val[1]:
169 val = val[1].split(':', 1)
170 self.map[key] = val[0], os.path.join(self.base, val[1])
171
172 def __contains__(self, key):
173 return key in self.cache or key in self.map
174
175 def load(self, t):
176 '''Get the template for the given template name. Use a local cache.'''
177 if not t in self.cache:
178 try:
179 self.cache[t] = open(self.map[t][1]).read()
180 except IOError, inst:
181 raise IOError(inst.args[0], _('template file %s: %s') %
182 (self.map[t][1], inst.args[1]))
183 return self.cache[t]
184
185 def __call__(self, t, **map):
186 ttype = t in self.map and self.map[t][0] or 'default'
187 proc = self.engines.get(ttype)
188 if proc is None:
189 proc = engines[ttype](self.load, self.filters, self.defaults)
190 self.engines[ttype] = proc
191
192 stream = proc.process(t, map)
193 if self.minchunk:
194 stream = util.increasingchunks(stream, min=self.minchunk,
195 max=self.maxchunk)
196 return stream
197
198 def templatepath(name=None):
199 '''return location of template file or directory (if no name).
200 returns None if not found.'''
201 normpaths = []
202
203 # executable version (py2exe) doesn't support __file__
204 if hasattr(sys, 'frozen'):
205 module = sys.executable
206 else:
207 module = __file__
208 for f in path:
209 if f.startswith('/'):
210 p = f
211 else:
212 fl = f.split('/')
213 p = os.path.join(os.path.dirname(module), *fl)
214 if name:
215 p = os.path.join(p, name)
216 if name and os.path.exists(p):
217 return os.path.normpath(p)
218 elif os.path.isdir(p):
219 normpaths.append(os.path.normpath(p))
220
221 return normpaths
222
223 def stylemap(style, paths=None):
224 """Return path to mapfile for a given style.
225
226 Searches mapfile in the following locations:
227 1. templatepath/style/map
228 2. templatepath/map-style
229 3. templatepath/map
230 """
231
232 if paths is None:
233 paths = templatepath()
234 elif isinstance(paths, str):
235 paths = [paths]
236
237 locations = style and [os.path.join(style, "map"), "map-" + style] or []
238 locations.append("map")
239 for path in paths:
240 for location in locations:
241 mapfile = os.path.join(path, location)
242 if os.path.isfile(mapfile):
243 return mapfile
244
245 raise RuntimeError("No hgweb templates found in %r" % paths)