lookup.py 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. # lookup.py
  2. # Copyright (C) 2006, 2007, 2008, 2009, 2010 Michael Bayer
  3. # mike_mp@zzzcomputing.com
  4. #
  5. # This module is part of Mako and is released under
  6. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  7. import os, stat, posixpath, re
  8. from mako import exceptions, util
  9. from mako.template import Template
  10. try:
  11. import threading
  12. except:
  13. import dummy_threading as threading
  14. class TemplateCollection(object):
  15. def has_template(self, uri):
  16. try:
  17. self.get_template(uri)
  18. return True
  19. except exceptions.TemplateLookupException:
  20. return False
  21. def get_template(self, uri, relativeto=None):
  22. raise NotImplementedError()
  23. def filename_to_uri(self, uri, filename):
  24. """Convert the given filename to a uri relative to
  25. this TemplateCollection."""
  26. return uri
  27. def adjust_uri(self, uri, filename):
  28. """Adjust the given uri based on the calling filename.
  29. When this method is called from the runtime, the 'filename' parameter
  30. is taken directly to the 'filename' attribute of the calling template.
  31. Therefore a custom TemplateCollection subclass can place any string
  32. identifier desired in the "filename" parameter of the Template objects
  33. it constructs and have them come back here.
  34. """
  35. return uri
  36. class TemplateLookup(TemplateCollection):
  37. def __init__(self,
  38. directories=None,
  39. module_directory=None,
  40. filesystem_checks=True,
  41. collection_size=-1,
  42. format_exceptions=False,
  43. error_handler=None,
  44. disable_unicode=False,
  45. output_encoding=None,
  46. encoding_errors='strict',
  47. cache_type=None,
  48. cache_dir=None, cache_url=None,
  49. cache_enabled=True,
  50. modulename_callable=None,
  51. default_filters=None,
  52. buffer_filters=(),
  53. imports=None,
  54. input_encoding=None,
  55. preprocessor=None):
  56. self.directories = [posixpath.normpath(d) for d in
  57. util.to_list(directories, ())
  58. ]
  59. self.module_directory = module_directory
  60. self.modulename_callable = modulename_callable
  61. self.filesystem_checks = filesystem_checks
  62. self.collection_size = collection_size
  63. self.template_args = {
  64. 'format_exceptions':format_exceptions,
  65. 'error_handler':error_handler,
  66. 'disable_unicode':disable_unicode,
  67. 'output_encoding':output_encoding,
  68. 'encoding_errors':encoding_errors,
  69. 'input_encoding':input_encoding,
  70. 'module_directory':module_directory,
  71. 'cache_type':cache_type,
  72. 'cache_dir':cache_dir or module_directory,
  73. 'cache_url':cache_url,
  74. 'cache_enabled':cache_enabled,
  75. 'default_filters':default_filters,
  76. 'buffer_filters':buffer_filters,
  77. 'imports':imports,
  78. 'preprocessor':preprocessor}
  79. if collection_size == -1:
  80. self._collection = {}
  81. self._uri_cache = {}
  82. else:
  83. self._collection = util.LRUCache(collection_size)
  84. self._uri_cache = util.LRUCache(collection_size)
  85. self._mutex = threading.Lock()
  86. def get_template(self, uri):
  87. try:
  88. if self.filesystem_checks:
  89. return self._check(uri, self._collection[uri])
  90. else:
  91. return self._collection[uri]
  92. except KeyError:
  93. u = re.sub(r'^\/+', '', uri)
  94. for dir in self.directories:
  95. srcfile = posixpath.normpath(posixpath.join(dir, u))
  96. if os.path.isfile(srcfile):
  97. return self._load(srcfile, uri)
  98. else:
  99. raise exceptions.TopLevelLookupException(
  100. "Cant locate template for uri %r" % uri)
  101. def adjust_uri(self, uri, relativeto):
  102. """adjust the given uri based on the calling filename."""
  103. if uri[0] != '/':
  104. if relativeto is not None:
  105. return posixpath.join(posixpath.dirname(relativeto), uri)
  106. else:
  107. return '/' + uri
  108. else:
  109. return uri
  110. def filename_to_uri(self, filename):
  111. try:
  112. return self._uri_cache[filename]
  113. except KeyError:
  114. value = self._relativeize(filename)
  115. self._uri_cache[filename] = value
  116. return value
  117. def _relativeize(self, filename):
  118. """Return the portion of a filename that is 'relative'
  119. to the directories in this lookup.
  120. """
  121. filename = posixpath.normpath(filename)
  122. for dir in self.directories:
  123. if filename[0:len(dir)] == dir:
  124. return filename[len(dir):]
  125. else:
  126. return None
  127. def _load(self, filename, uri):
  128. self._mutex.acquire()
  129. try:
  130. try:
  131. # try returning from collection one
  132. # more time in case concurrent thread already loaded
  133. return self._collection[uri]
  134. except KeyError:
  135. pass
  136. try:
  137. if self.modulename_callable is not None:
  138. module_filename = self.modulename_callable(filename, uri)
  139. else:
  140. module_filename = None
  141. self._collection[uri] = template = Template(
  142. uri=uri,
  143. filename=posixpath.normpath(filename),
  144. lookup=self,
  145. module_filename=module_filename,
  146. **self.template_args)
  147. return template
  148. except:
  149. # if compilation fails etc, ensure
  150. # template is removed from collection,
  151. # re-raise
  152. self._collection.pop(uri, None)
  153. raise
  154. finally:
  155. self._mutex.release()
  156. def _check(self, uri, template):
  157. if template.filename is None:
  158. return template
  159. if not os.path.exists(template.filename):
  160. self._collection.pop(uri, None)
  161. raise exceptions.TemplateLookupException(
  162. "Cant locate template for uri %r" % uri)
  163. elif template.module._modified_time < \
  164. os.stat(template.filename)[stat.ST_MTIME]:
  165. self._collection.pop(uri, None)
  166. return self._load(template.filename, uri)
  167. else:
  168. return template
  169. def put_string(self, uri, text):
  170. self._collection[uri] = Template(
  171. text,
  172. lookup=self,
  173. uri=uri,
  174. **self.template_args)
  175. def put_template(self, uri, template):
  176. self._collection[uri] = template