Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1import os 

2from . import core 

3 

4from .util import memoize, relpath 

5 

6from markupsafe import Markup 

7import six 

8from six.moves import map 

9from six.moves import zip 

10 

11# Just shorthand 

12SEP, ALTSEP, EXTSEP = os.path.sep, os.path.altsep, os.path.extsep 

13 

14engine_name_cache = {} 

15 

16_default_rendering_extension_lookup = { 

17 'mako': ['mak', 'mako'], 

18 'genshi': ['genshi', 'html'], 

19 # just for backwards compatibility with tw2 2.0.0 

20 'genshi_abs': ['genshi', 'html'], 

21 'jinja': ['jinja', 'html'], 

22 'chameleon': ['pt'], 

23 'kajiki': ['kajiki', 'html'], 

24} 

25 

26 

27def get_rendering_extensions_lookup(mw): 

28 if mw is None: 

29 rl = core.request_local() 

30 mw = rl.get('middleware') 

31 if mw is None: 

32 return _default_rendering_extension_lookup 

33 return mw.config.rendering_extension_lookup 

34 

35 

36@memoize 

37def get_engine_name(template_name, mw=None): 

38 if template_name in engine_name_cache: 

39 return engine_name_cache[template_name] 

40 

41 if template_name and ':' in template_name: 

42 engine_name = template_name.split(':', 1)[0] 

43 engine_name_cache[template_name] = engine_name 

44 return engine_name 

45 

46 try: 

47 if mw is None: 

48 rl = core.request_local() 

49 mw = rl['middleware'] 

50 pref_rend_eng = mw.config.preferred_rendering_engines 

51 except (KeyError, AttributeError): 

52 pref_rend_eng = ['mako', 'genshi', 'jinja', 'chameleon', 'kajiki'] 

53 

54 # find the first file in the preffered engines available for templating 

55 for engine_name in pref_rend_eng: 

56 try: 

57 get_source(engine_name, template_name, mw=mw) 

58 engine_name_cache[template_name] = engine_name 

59 return engine_name 

60 except IOError: 

61 pass 

62 

63 if not mw.config.strict_engine_selection: 

64 pref_rend_eng = ['mako', 'genshi', 'jinja', 'chameleon', 'kajiki'] 

65 for engine_name in pref_rend_eng: 

66 try: 

67 get_source(engine_name, template_name, mw=mw) 

68 engine_name_cache[template_name] = engine_name 

69 return engine_name 

70 except IOError: 

71 pass 

72 

73 raise ValueError("Could not find engine name for %s" % template_name) 

74 

75 

76@memoize 

77def _get_dotted_filename(engine_name, template, mw=None): 

78 rendering_extension_lookup = get_rendering_extensions_lookup(mw) 

79 template = _strip_engine_name(template, mw) 

80 location, filename = template.rsplit('.', 1) 

81 module = __import__(location, globals(), locals(), ['*']) 

82 parent_dir = SEP.join(module.__file__.split(SEP)[:-1]) 

83 

84 for extension in rendering_extension_lookup[engine_name]: 

85 abs_filename = parent_dir + SEP + filename + EXTSEP + extension 

86 if os.path.exists(abs_filename): 

87 return abs_filename 

88 

89 raise IOError("Couldn't find source for %r" % template) 

90 

91 

92def _strip_engine_name(template, mw=None): 

93 """ Strip off the leading engine name from the template if it exists. """ 

94 rendering_extension_lookup = get_rendering_extensions_lookup(mw) 

95 if any(map(template.lstrip().startswith, rendering_extension_lookup)): 

96 return template.split(':', 1)[1] 

97 

98 return template 

99 

100 

101 

102 

103@memoize 

104def get_source(engine_name, template, inline=False, mw=None): 

105 if inline: 

106 return template 

107 

108 if SEP in template or (ALTSEP and ALTSEP in template): 

109 filename = _strip_engine_name(template, mw=mw) 

110 else: 

111 filename = _get_dotted_filename(engine_name, template, mw=mw) 

112 

113 with open(filename, 'rb') as f: 

114 return f.read().decode('utf-8') 

115 

116 

117@memoize 

118def get_render_callable(engine_name, displays_on, src, filename=None, inline=False): 

119 """ Returns a function that takes a template source and kwargs. """ 

120 

121 # See the discussion here re: `displays_on` -- http://bit.ly/JRqbRw 

122 

123 directory = None 

124 if filename and not inline: 

125 if SEP not in filename and (not ALTSEP or ALTSEP not in filename): 

126 filename = _get_dotted_filename(engine_name, filename) 

127 

128 directory = os.path.dirname(filename) 

129 

130 if engine_name == 'mako': 

131 import mako.template 

132 args = dict(text=src, imports=["from markupsafe import escape_silent"], 

133 default_filters=['escape_silent']) 

134 

135 if directory: 

136 args['filename'] = relpath(filename, directory) 

137 from mako.lookup import TemplateLookup 

138 args['lookup'] = TemplateLookup( 

139 directories=[directory]) 

140 

141 tmpl = mako.template.Template(**args) 

142 return lambda kwargs: Markup(tmpl.render_unicode(**kwargs)) 

143 

144 elif engine_name in ('genshi', 'genshi_abs'): 

145 import genshi.template 

146 args = dict( 

147 source=src, 

148 ) 

149 

150 if directory: 

151 args['loader'] = genshi.template.TemplateLoader([ 

152 genshi.template.loader.directory(directory), 

153 ]) 

154 

155 tmpl = genshi.template.MarkupTemplate(**args) 

156 return lambda kwargs: Markup( 

157 ''.join(tmpl.generate(**kwargs).serialize('xhtml')) 

158 ) 

159 

160 elif engine_name == 'jinja': 

161 import jinja2 

162 from .jinja_util import htmlbools 

163 env = jinja2.environment.Environment(autoescape=True) 

164 env.filters['htmlbools'] = htmlbools 

165 tmpl = env.from_string(src, template_class=jinja2.Template) 

166 tmpl.filename = filename 

167 return lambda kwargs: Markup(tmpl.render(**kwargs)) 

168 

169 elif engine_name == 'kajiki': 

170 import kajiki 

171 tmpl = kajiki.XMLTemplate(src, filename=filename, 

172 cdata_scripts=False) 

173 return lambda kwargs: Markup(tmpl(kwargs).render()) 

174 

175 elif engine_name == 'chameleon': 

176 import chameleon 

177 tmpl = chameleon.PageTemplate(src, filename=filename) 

178 return lambda kwargs: Markup(tmpl.render(**kwargs).strip()) 

179 

180 raise NotImplementedError("Unhandled engine") 

181 

182 

183def render(template_name, displays_on, kwargs, inline=False, mw=None): 

184 """ Highest level function, here for convenience. 

185 

186 Makes use of *all* other functions in this module. 

187 """ 

188 

189 # Determine the engine name 

190 if not inline: 

191 engine_name = get_engine_name(template_name, mw) 

192 else: 

193 engine_name = inline 

194 

195 if mw is not None and mw.config.auto_reload_templates: 

196 get_source._flush() 

197 get_render_callable._flush() 

198 

199 # Load the template source 

200 source = get_source(engine_name, template_name, inline, mw) 

201 

202 # Establish the render function 

203 callback = get_render_callable( 

204 engine_name, displays_on, source, template_name, inline) 

205 

206 # Do it 

207 return callback(kwargs)