1"Template support for Cheetah"
2
3import sys, os, imp
4
5from Cheetah import Compiler
6import pkg_resources
7
8def _recompile_template(package, basename, tfile, classname):
9    tmpl = pkg_resources.resource_string(package, "%s.tmpl" % basename)
10    c = Compiler.Compiler(source=tmpl, mainClassName='GenTemplate')
11    code = str(c)
12    mod = imp.new_module(classname)
13    ns = dict()
14    exec(code, ns)
15    tempclass = ns.get("GenTemplate",
16                       ns.get('DynamicallyCompiledCheetahTemplate'))
17    assert tempclass
18    tempclass.__name__ = basename
19    setattr(mod, basename, tempclass)
20    sys.modules[classname] = mod
21    return mod
22
23class TurboCheetah:
24    extension = "tmpl"
25
26    def __init__(self, extra_vars_func=None, options=None):
27        if options is None:
28            options = dict()
29        self.get_extra_vars = extra_vars_func
30        self.options = options
31        self.compiledTemplates = {}
32        self.search_path = []
33
34    def load_template(self, template=None,
35                      template_string=None, template_file=None,
36                      loadingSite=False):
37        """Searches for a template along the Python path.
38
39        Template files must end in ".tmpl" and be in legitimate packages.
40        """
41        given = len([_f for _f in (template, template_string, template_file) if _f])
42        if given > 1:
43            raise TypeError(
44                "You may give only one of template, template_string, and "
45                "template_file")
46        if not given:
47            raise TypeError(
48                "You must give one of template, template_string, or "
49                "template_file")
50        if template:
51            return self.load_template_module(template)
52        elif template_string:
53            return self.load_template_string(template_string)
54        elif template_file:
55            return self.load_template_file(template_file)
56
57    def load_template_module(self, classname):
58
59        ct = self.compiledTemplates
60
61        divider = classname.rfind(".")
62        if divider > -1:
63            package = classname[0:divider]
64            basename = classname[divider+1:]
65        else:
66            raise ValueError("All templates must be in a package")
67
68        if not self.options.get("cheetah.precompiled", False):
69            tfile = pkg_resources.resource_filename(package,
70                                                    "%s.%s" %
71                                                    (basename,
72                                                    self.extension))
73            if classname in ct:
74                mtime = os.stat(tfile).st_mtime
75                if ct[classname] != mtime:
76                    ct[classname] = mtime
77                    del sys.modules[classname]
78                    mod = _recompile_template(package, basename,
79                                              tfile, classname)
80                else:
81                    mod = __import__(classname, dict(), dict(), [basename])
82            else:
83                ct[classname] = os.stat(tfile).st_mtime
84                mod = _recompile_template(package, basename,
85                                          tfile, classname)
86        else:
87            mod = __import__(classname, dict(), dict(), [basename])
88        tempclass = getattr(mod, basename)
89        return tempclass
90
91    def load_template_string(self, content):
92        raise NotImplementedError
93
94    def load_template_file(self, filename):
95        raise NotImplementedError
96
97    def render(self, info, format="html", fragment=False, template=None,
98               template_string=None, template_file=None):
99        tclass = self.load_template(
100            template=template, template_string=template_string,
101            template_file=template_file)
102        if self.get_extra_vars:
103            extra = self.get_extra_vars()
104        else:
105            extra = {}
106        tempobj = tclass(searchList=[info, extra])
107        if fragment:
108            return tempobj.fragment()
109        else:
110            return tempobj.respond()
111