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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

import os 

from . import core 

 

from .util import memoize, relpath 

 

from markupsafe import Markup 

import six 

from six.moves import map 

from six.moves import zip 

 

# Just shorthand 

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

 

engine_name_cache = {} 

 

_default_rendering_extension_lookup = { 

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

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

    # just for backwards compatibility with tw2 2.0.0 

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

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

    'chameleon': ['pt'], 

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

} 

 

 

def get_rendering_extensions_lookup(mw): 

    if mw is None: 

        rl = core.request_local() 

        mw = rl.get('middleware') 

        if mw is None: 

            return _default_rendering_extension_lookup 

    return mw.config.rendering_extension_lookup 

 

 

@memoize 

def get_engine_name(template_name, mw=None): 

    if template_name in engine_name_cache: 

        return engine_name_cache[template_name] 

 

    if template_name and ':' in template_name: 

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

        engine_name_cache[template_name] = engine_name 

        return engine_name 

 

    try: 

        if mw is None: 

            rl = core.request_local() 

            mw = rl['middleware'] 

        pref_rend_eng = mw.config.preferred_rendering_engines 

    except (KeyError, AttributeError): 

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

 

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

    for engine_name in pref_rend_eng: 

        try: 

            get_source(engine_name, template_name, mw=mw) 

            engine_name_cache[template_name] = engine_name 

            return engine_name 

        except IOError: 

            pass 

 

    if not mw.config.strict_engine_selection: 

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

        for engine_name in pref_rend_eng: 

            try: 

                get_source(engine_name, template_name, mw=mw) 

                engine_name_cache[template_name] = engine_name 

                return engine_name 

            except IOError: 

                pass 

 

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

 

 

@memoize 

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

    rendering_extension_lookup = get_rendering_extensions_lookup(mw) 

    template = _strip_engine_name(template, mw) 

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

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

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

 

    for extension in rendering_extension_lookup[engine_name]: 

        abs_filename = parent_dir + SEP + filename + EXTSEP + extension 

        if os.path.exists(abs_filename): 

            return abs_filename 

 

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

 

 

def _strip_engine_name(template, mw=None): 

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

    rendering_extension_lookup = get_rendering_extensions_lookup(mw) 

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

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

 

    return template 

 

 

 

 

@memoize 

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

    if inline: 

        return template 

 

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

        filename = _strip_engine_name(template, mw=mw) 

    else: 

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

 

    # TODO -- use a context manager here once we drop support for py2.5. 

    f = open(filename, 'r') 

 

    try: 

        source = f.read() 

    finally: 

        f.close() 

 

    return source 

 

 

@memoize 

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

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

 

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

 

    directory = None 

    if filename and not inline: 

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

            filename = _get_dotted_filename(engine_name, filename) 

 

        directory = os.path.dirname(filename) 

 

    if engine_name == 'mako': 

        import mako.template 

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

                    default_filters=['escape_silent']) 

 

        if directory: 

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

            from mako.lookup import TemplateLookup 

            args['lookup'] = TemplateLookup( 

                directories=[directory]) 

 

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

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

 

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

        import genshi.template 

        args = dict( 

            source=src, 

        ) 

 

        if directory: 

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

                genshi.template.loader.directory(directory), 

            ]) 

 

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

        return lambda kwargs: Markup( 

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

        ) 

 

    elif engine_name == 'jinja': 

        import jinja2 

        from .jinja_util import htmlbools 

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

        env.filters['htmlbools'] = htmlbools 

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

        tmpl.filename = filename 

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

 

    elif engine_name == 'kajiki': 

        import kajiki 

        tmpl = kajiki.XMLTemplate(six.u(src), filename=filename, 

                                  cdata_scripts=False) 

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

 

    elif engine_name == 'chameleon': 

        import chameleon 

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

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

 

    raise NotImplementedError("Unhandled engine") 

 

 

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

    """ Highest level function, here for convenience. 

 

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

    """ 

 

    # Determine the engine name 

    if not inline: 

        engine_name = get_engine_name(template_name, mw) 

    else: 

        engine_name = inline 

 

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

        get_source._flush() 

        get_render_callable._flush() 

 

    # Load the template source 

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

 

    # Establish the render function 

    callback = get_render_callable( 

        engine_name, displays_on, source, template_name, inline) 

 

    # Do it 

    return callback(kwargs)