# -*- coding: utf-8 -*-
"""
jinja template loader
"""
from jinja.base import Lexer, Parser
from jinja.exceptions import TemplateDoesNotExist, TemplateSyntaxError, \
TemplateCharsetError
import os
from md5 import md5
try:
import cPickle as pickle
except ImportError:
import pickle
try:
from pkg_resources import resource_exists, resource_string
except ImportError:
resource_exists = resource_string = None
__all__ = ['FileSystemLoader', 'CachedFileSystemLoader', 'StringLoader',
'EggLoader', 'ChoiceLoader']
class BaseLoader(object):
def load(self, name, parent=None):
"""This method isn't allowed to cache the data"""
raise NotImplementedError()
def load_and_compile(self, name, lib=None, parent=None):
"""Get's called when the template requires an nodelist
to render on."""
template = self.load(name, parent)
lexer = Lexer(template)
parser = Parser(lexer.tokenize(), self, lib)
return parser.parse()
def load_and_compile_uncached(self, name, lib=None, parent=None):
"""Get's called for the extends tag to get a fresh
nodelist to manipulate."""
return self.load_and_compile(name, lib, parent)
class FileSystemLoader(BaseLoader):
"""
Loads templates from the filesystem::
from jinja import FileSystemLoader
loader = FileSystemLoader('/template/search/path')
"""
def __init__(self, path, suffix='.html', charset='utf-8'):
self.path = path
self.suffix = suffix
self.charset = charset
def load(self, name, parent=None):
name = os.sep.join([p for p in name.split(os.sep) if p and p[0] != '.'])
fn = os.path.join(self.path, name) + self.suffix
if os.path.exists(fn):
contents = file(fn).read()
else:
raise TemplateDoesNotExist(name)
try:
return contents.decode(self.charset)
except UnicodeDecodeError, e:
raise TemplateCharsetError("Could not decode template '%s'" % fn, e)
class CachedFileSystemLoader(FileSystemLoader):
"""
Same as ``FileSystemLoader`` but caches the parsed nodelist in a binary
cPickle dump.
"""
def __init__(self, path, suffix='.html', cache_dir=None, charset='utf-8'):
super(CachedFileSystemLoader, self).__init__(path, suffix, charset)
if cache_dir is None:
self.cache_dir = path
self.prefix = True
else:
self.cache_dir = cache_dir
self.prefix = False
def load_and_compile(self, name, lib=None, parent=None):
if self.prefix:
prefix = '.'
else:
prefix = ''
hash_ = md5('%s/%s' % (self.path, name)).hexdigest()
cache_name = os.path.join(self.cache_dir, prefix + hash_) + '.cache'
template_name = os.path.join(self.path, name) + self.suffix
if not os.path.exists(cache_name) or \
os.path.getmtime(cache_name) < os.path.getmtime(template_name):
nodelist = FileSystemLoader.load_and_compile(self, name, lib, parent)
try:
pickle.dump(nodelist, file(cache_name, 'wb'), protocol=2)
except IOError:
pass
else:
try:
nodelist = pickle.load(file(cache_name, 'rb'))
except IOError:
nodelist = FileSystemLoader.load_and_compile(self, name, lib.
parent)
return nodelist
def load_and_compile_uncached(self, name, lib=None, parent=None):
super(CachedFileSystemLoader, self).load_and_compile(name, lib, parent)
class StringLoader(BaseLoader):
"""
A non thread safe version of a loader getting their templates
from strings. If you want a thread safe behaviour you have to
create a new loader for each template::
from jinja import Template, StringLoader
t = Template('''my template here''', StringLoader())
"""
def __init__(self, charset='utf-8'):
self.charset = charset
self.template = False
def load(self, tpl, parent=None):
if isinstance(tpl, unicode):
return tpl
try:
return unicode(tpl, self.charset)
except UnicodeDecodeError, e:
raise TemplateCharsetError('Could not decode template', e)
def load_and_compile(self, name, lib=None, parent=None):
if self.template:
raise TemplateSyntaxError('StringLoader doesn\'t allow '
'template inheritance')
self.template = True
rv = super(StringLoader, self).load_and_compile(name, lib, parent)
self.template = False
return rv
class EggLoader(FileSystemLoader):
"""
Loads templates from an egg::
from jinja import EggLoader
loader = EggLoader('MyEgg', 'internal/path/to/templates')
"""
# contributed by Jon Rosebaugh
def __init__(self, package, path, suffix='.html', charset='utf-8'):
if resource_exists is resource_string is None:
raise RuntimeError('pkg_resources not found')
super(EggLoader, self).__init__(path, suffix, charset)
self.package = package
def load(self, name, parent=None):
name = '/'.join([p for p in name.split('/') if p and p[0] != '.'])
name = '/'.join([self.path, name]) + self.suffix
if resource_exists(self.package, name):
contents = resource_string(self.package, name)
else:
raise TemplateDoesNotExist(name)
try:
return contents.decode(self.charset)
except UnicodeDecodeError, e:
raise TemplateCharsetError("Could not decode template "
"'%s'" % name, e)
class ChoiceLoader(object):
"""
Takes a number of loader instances.
The ``load`` and ``load_and_compile`` method try to to call the
functions of all given loaders::
from jinja import ChoiceLoader, FileSystemLoader, EggLoader
loader = ChoiceLoader(
FileSystemLoader('/path/to/my/templates'),
EggLoader('MyEgg', 'internal/path/to/templates')
)
"""
def __init__(self, *loaders):
self.loaders = loaders
def load(self, name, parent=None):
for loader in self.loaders:
try:
return loader.load(name, parent=None)
except TemplateDoesNotExist:
continue
raise TemplateDoesNotExist(name)
def load_and_compile(self, name, lib=None, parent=None):
for loader in self.loaders:
try:
return loader.load_and_compile(name, lib, parent)
except TemplateDoesNotExist:
continue
raise TemplateDoesNotExist(name)
def load_and_compile_uncached(self, name, lib=None, parent=None):
for loader in self.loaders:
try:
return loader.load_and_compile_uncached(name, lib, parent)
except TemplateDoesNotExist:
continue
raise TemplateDoesNotExist(name)
syntax highlighted by Code2HTML, v. 0.9.1