|
1 """ |
|
2 Template handler |
|
3 """ |
|
4 |
|
5 from mako import exceptions |
|
6 from mako.template import Template |
|
7 import mako.lookup |
|
8 |
|
9 from qmsk.web import http, helpers |
|
10 |
|
11 # path to template files |
|
12 TEMPLATE_DIR = "templates" |
|
13 |
|
14 # path to cached templates |
|
15 CACHE_DIR = "cache/templates" |
|
16 |
|
17 # template file extension |
|
18 TEMPLATE_EXT = "tmpl" |
|
19 |
|
20 class TemplateError (http.ResponseError) : |
|
21 """ |
|
22 Raised by the template module functions |
|
23 """ |
|
24 |
|
25 pass |
|
26 |
|
27 class TemplateLoader (mako.lookup.TemplateLookup) : |
|
28 """ |
|
29 Our own specialization of mako's TemplateLookup |
|
30 """ |
|
31 |
|
32 def __init__ (self, path, fileext=TEMPLATE_EXT, **env) : |
|
33 """ |
|
34 Initialize to load templates located at path, with the given file extension. |
|
35 |
|
36 The given **env list is supplied to every template render |
|
37 """ |
|
38 |
|
39 # store |
|
40 self.path = path |
|
41 self.fileext = fileext |
|
42 self.env = env |
|
43 |
|
44 # build the TemplateLookup |
|
45 super(TemplateLoader, self).__init__(directories=[path], module_directory=CACHE_DIR) |
|
46 |
|
47 @staticmethod |
|
48 def _render (tpl, env, params) : |
|
49 """ |
|
50 Render the given template with given env/params, returning the output as a unicode string, or raising a TemplateError |
|
51 """ |
|
52 |
|
53 # build the context from our superglobals, env, and params |
|
54 ctx = dict( |
|
55 h = helpers, |
|
56 ) |
|
57 ctx.update(env) |
|
58 ctx.update(params) |
|
59 |
|
60 try : |
|
61 return tpl.render_unicode(**ctx) |
|
62 |
|
63 # a template may render other templates |
|
64 except TemplateError : |
|
65 raise |
|
66 |
|
67 except : |
|
68 details = exceptions.text_error_template().render() |
|
69 |
|
70 raise TemplateError("Template render failed", status='500 Internal Server Error', details=details) |
|
71 |
|
72 |
|
73 def lookup (self, name) : |
|
74 """ |
|
75 Looks up a template based on the bare "name", which does not include the path or file extension |
|
76 """ |
|
77 |
|
78 try : |
|
79 return self.get_template("%s.%s" % (name, self.fileext)) |
|
80 |
|
81 except : |
|
82 raise TemplateError("Template broken: %r" % (name, ), status='500 Internal Server Error', details=exceptions.text_error_template().render()) |
|
83 |
|
84 def render (self, name, **params) : |
|
85 """ |
|
86 Render a template, using lookup() on the given name |
|
87 """ |
|
88 |
|
89 return self._render(self.lookup(name), self.env, params) |
|
90 |
|
91 def render_to_response (self, name, **params) : |
|
92 """ |
|
93 Render a template, returning a http.Response object |
|
94 """ |
|
95 |
|
96 return http.Response(self.render(name, **params)) |
|
97 |
|
98 @classmethod |
|
99 def load (cls, path) : |
|
100 """ |
|
101 Loads a template from a specific file |
|
102 """ |
|
103 |
|
104 try : |
|
105 return Template(filename=path, module_directory=CACHE_DIR) |
|
106 |
|
107 except : |
|
108 raise TemplateError("Template broken: %r" % (path, ), status='500 Internal Server Error', details=exceptions.text_error_template().render()) |
|
109 |
|
110 @classmethod |
|
111 def render_file (cls, path, **params) : |
|
112 """ |
|
113 Render a template, using load() on the given path. No global environment vars are defined for the render. |
|
114 """ |
|
115 |
|
116 return cls._render(cls.load(path), dict(), params) |
|
117 |
|
118 @classmethod |
|
119 def render_template (cls, template, **params) : |
|
120 """ |
|
121 Render the given template object. No global environment vars are defined for the render. |
|
122 """ |
|
123 |
|
124 return cls._render(template, dict(), params) |