import os import re import sys from StringIO import StringIO import logging import meld3 class MeldTemplateFile(object): _v_last_read = 0 _v_element = None content_type = 'text/html' def __init__(self, filename, _prefix=None, content_type=None, transform=None): path = get_path_from_prefix(_prefix) self.filename = os.path.join(path, filename) if not os.path.isfile(self.filename): raise ValueError("No such file", self.filename) if content_type is not None: self.content_type = content_type self.transform = transform def read_file(self): __traceback_info__ = self.filename mode = 'rb' f = open(self.filename, mode) try: text = f.read(XML_PREFIX_MAX_LENGTH) except: f.close() raise type_ = sniff_type(text) if type_ == "text/xml": text += f.read() encoding = None else: # For HTML, we really want the file read in text mode: f.close() mode = 'r' f = open(self.filename, mode) text = f.read() text, type_, encoding = prepare_html(text) f.close() return text, type_, encoding, mode def _cook(self): if self._v_last_read and not __debug__: return __traceback_info__ = self.filename try: mtime = os.path.getmtime(self.filename) except OSError: mtime = 0 if self._v_element is not None and mtime == self._v_last_read: return text, type_, encoding, mode = self.read_file() if type == 'text/html': parser = meld3.parse_htmlstring args = (text, encoding) else: parser = meld3.parse_xmlstring args = (text,) self._v_errors = () try: element = parser(*args) self._v_element = element except: self._v_errors = ["Compilation failed", "%s: %s" % sys.exc_info()[:2]] self._v_cooked = 1 if self._v_errors: logging.error('MeldTemplateFile: Error in template %s: %s', self.filename, '\n'.join(self._v_errors)) return self._v_last_read = mtime def __call__(self, context, request): self._cook() element = self._v_element.clone() if self.transform is not None: self.transform(element, context, request) io = StringIO() s = element.write_html(io) io.seek(0) s = io.read() return s def __getstate__(self): raise TypeError("non-picklable object") def transform(self): # default transform pass DEFAULT_ENCODING = "utf-8" meta_pattern = re.compile( r'\s*\s*', re.IGNORECASE) XML_PREFIXES = [ "/ stripping # be in PageTemplate.__call__()? text = meta_pattern.sub("", text) else: type_ = None encoding = DEFAULT_ENCODING return unicode(text, encoding), type_, encoding def get_path_from_prefix(_prefix): if isinstance(_prefix, str): path = _prefix else: if _prefix is None: _prefix = sys._getframe(2).f_globals path = package_home(_prefix) return path def package_home(gdict): filename = gdict["__file__"] return os.path.dirname(filename)