Source code for wheezy.template.loader

"""
"""

import os
import os.path
import stat

from time import time


class FileLoader(object):
[docs] """ Loads templates from file system. ``directories`` - search path of directories to scan for template. ``encoding`` - decode template content per encoding. """ def __init__(self, directories, encoding='UTF-8'): searchpath = [] for path in directories: abspath = os.path.abspath(path) assert os.path.exists(abspath) assert os.path.isdir(abspath) searchpath.append(abspath) self.searchpath = searchpath self.encoding = encoding def list_names(self):
[docs] """ Return a list of names relative to directories. Ignores any files and directories that start with dot. """ names = [] for path in self.searchpath: pathlen = len(path) + 1 for dirpath, dirnames, filenames in os.walk(path): for i in [i for i, name in enumerate(dirnames) if name.startswith('.')]: del dirnames[i] for filename in filenames: if filename.startswith('.'): continue name = os.path.join(dirpath, filename)[pathlen:] name = name.replace('\\', '/') names.append(name) return tuple(sorted(names)) def get_fullname(self, name):
[docs] """ Returns a full path by a template name. """ for path in self.searchpath: filename = os.path.join(path, name) if not os.path.exists(filename): continue if not os.path.isfile(filename): continue return filename else: None def load(self, name):
[docs] """ Loads a template by name from file system. """ filename = self.get_fullname(name) if filename: f = open(filename, 'rb') try: return f.read().decode(self.encoding) finally: f.close() return None class DictLoader(object):
[docs] """ Loads templates from python dictionary. ``templates`` - a dict where key corresponds to template name and value to template content. """ def __init__(self, templates): self.templates = templates def list_names(self):
[docs] """ List all keys from internal dict. """ return tuple(sorted(self.templates.keys())) def load(self, name):
[docs] """ Returns template by name. """ if name not in self.templates: return None return self.templates[name] class ChainLoader(object):
[docs] """ Loads templates from ``loaders`` until first succeed. """ def __init__(self, loaders): self.loaders = loaders def list_names(self):
[docs] """ Returns as list of names from all loaders. """ names = set() for loader in self.loaders: names |= set(loader.list_names()) return tuple(sorted(names)) def load(self, name):
[docs] """ Returns template by name from the first loader that succeed. """ for loader in self.loaders: source = loader.load(name) if source is not None: return source return None class PreprocessLoader(object):
[docs] """ Performs preprocessing of loaded template. """ def __init__(self, engine, ctx=None): self.engine = engine self.ctx = ctx or {} def list_names(self): return self.engine.loader.list_names() def load(self, name): return self.engine.render(name, self.ctx, {}, {}) def autoreload(engine, enabled=True):
[docs] """ Auto reload template if changes are detected in file. Limitation: master (inherited), imported and preprocessed templates. It is recommended to use application server that supports file reload instead. """ if not enabled: return engine return AutoReloadProxy(engine) # region: internal details class AutoReloadProxy(object):
def __init__(self, engine): from warnings import warn self.engine = engine self.names = {} warn('autoreload limitation: master (inherited), imported ' 'and preprocessed templates. It is recommended to use ' 'application server that supports file reload instead.', stacklevel=3) def get_template(self, name): if self.file_changed(name): self.remove(name) return self.engine.get_template(name) def render(self, name, ctx, local_defs, super_defs): if self.file_changed(name): self.remove(name) return self.engine.render(name, ctx, local_defs, super_defs) def remove(self, name): self.engine.remove(name) # region: internal details def __getattr__(self, name): return getattr(self.engine, name) def file_changed(self, name): try: last_known_stamp = self.names[name] current_time = int(time()) if current_time - last_known_stamp <= 2: return False except KeyError: last_known_stamp = 0 abspath = self.engine.loader.get_fullname(name) if not abspath: return False last_modified_stamp = os.stat(abspath)[stat.ST_MTIME] if last_modified_stamp <= last_known_stamp: return False self.names[name] = last_modified_stamp return True