PageRenderTime 49ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/python/Lib/site-packages/mako-0.7.3-py2.7.egg/mako/lookup.py

https://gitlab.com/pmuontains/Odoo
Python | 354 lines | 333 code | 5 blank | 16 comment | 5 complexity | f6d6f12f8eada65286349ba8fa1afed3 MD5 | raw file
  1. # mako/lookup.py
  2. # Copyright (C) 2006-2012 the Mako authors and contributors <see AUTHORS file>
  3. #
  4. # This module is part of Mako and is released under
  5. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  6. import os, stat, posixpath, re
  7. from mako import exceptions, util
  8. from mako.template import Template
  9. try:
  10. import threading
  11. except:
  12. import dummy_threading as threading
  13. class TemplateCollection(object):
  14. """Represent a collection of :class:`.Template` objects,
  15. identifiable via URI.
  16. A :class:`.TemplateCollection` is linked to the usage of
  17. all template tags that address other templates, such
  18. as ``<%include>``, ``<%namespace>``, and ``<%inherit>``.
  19. The ``file`` attribute of each of those tags refers
  20. to a string URI that is passed to that :class:`.Template`
  21. object's :class:`.TemplateCollection` for resolution.
  22. :class:`.TemplateCollection` is an abstract class,
  23. with the usual default implementation being :class:`.TemplateLookup`.
  24. """
  25. def has_template(self, uri):
  26. """Return ``True`` if this :class:`.TemplateLookup` is
  27. capable of returning a :class:`.Template` object for the
  28. given ``uri``.
  29. :param uri: String URI of the template to be resolved.
  30. """
  31. try:
  32. self.get_template(uri)
  33. return True
  34. except exceptions.TemplateLookupException:
  35. return False
  36. def get_template(self, uri, relativeto=None):
  37. """Return a :class:`.Template` object corresponding to the given
  38. ``uri``.
  39. The default implementation raises
  40. :class:`.NotImplementedError`. Implementations should
  41. raise :class:`.TemplateLookupException` if the given ``uri``
  42. cannot be resolved.
  43. :param uri: String URI of the template to be resolved.
  44. :param relativeto: if present, the given ``uri`` is assumed to
  45. be relative to this URI.
  46. """
  47. raise NotImplementedError()
  48. def filename_to_uri(self, uri, filename):
  49. """Convert the given ``filename`` to a URI relative to
  50. this :class:`.TemplateCollection`."""
  51. return uri
  52. def adjust_uri(self, uri, filename):
  53. """Adjust the given ``uri`` based on the calling ``filename``.
  54. When this method is called from the runtime, the
  55. ``filename`` parameter is taken directly to the ``filename``
  56. attribute of the calling template. Therefore a custom
  57. :class:`.TemplateCollection` subclass can place any string
  58. identifier desired in the ``filename`` parameter of the
  59. :class:`.Template` objects it constructs and have them come back
  60. here.
  61. """
  62. return uri
  63. class TemplateLookup(TemplateCollection):
  64. """Represent a collection of templates that locates template source files
  65. from the local filesystem.
  66. The primary argument is the ``directories`` argument, the list of
  67. directories to search:
  68. .. sourcecode:: python
  69. lookup = TemplateLookup(["/path/to/templates"])
  70. some_template = lookup.get_template("/index.html")
  71. The :class:`.TemplateLookup` can also be given :class:`.Template` objects
  72. programatically using :meth:`.put_string` or :meth:`.put_template`:
  73. .. sourcecode:: python
  74. lookup = TemplateLookup()
  75. lookup.put_string("base.html", '''
  76. <html><body>${self.next()}</body></html>
  77. ''')
  78. lookup.put_string("hello.html", '''
  79. <%include file='base.html'/>
  80. Hello, world !
  81. ''')
  82. :param directories: A list of directory names which will be
  83. searched for a particular template URI. The URI is appended
  84. to each directory and the filesystem checked.
  85. :param collection_size: Approximate size of the collection used
  86. to store templates. If left at its default of ``-1``, the size
  87. is unbounded, and a plain Python dictionary is used to
  88. relate URI strings to :class:`.Template` instances.
  89. Otherwise, a least-recently-used cache object is used which
  90. will maintain the size of the collection approximately to
  91. the number given.
  92. :param filesystem_checks: When at its default value of ``True``,
  93. each call to :meth:`.TemplateLookup.get_template()` will
  94. compare the filesystem last modified time to the time in
  95. which an existing :class:`.Template` object was created.
  96. This allows the :class:`.TemplateLookup` to regenerate a
  97. new :class:`.Template` whenever the original source has
  98. been updated. Set this to ``False`` for a very minor
  99. performance increase.
  100. :param modulename_callable: A callable which, when present,
  101. is passed the path of the source file as well as the
  102. requested URI, and then returns the full path of the
  103. generated Python module file. This is used to inject
  104. alternate schemes for Python module location. If left at
  105. its default of ``None``, the built in system of generation
  106. based on ``module_directory`` plus ``uri`` is used.
  107. All other keyword parameters available for
  108. :class:`.Template` are mirrored here. When new
  109. :class:`.Template` objects are created, the keywords
  110. established with this :class:`.TemplateLookup` are passed on
  111. to each new :class:`.Template`.
  112. """
  113. def __init__(self,
  114. directories=None,
  115. module_directory=None,
  116. filesystem_checks=True,
  117. collection_size=-1,
  118. format_exceptions=False,
  119. error_handler=None,
  120. disable_unicode=False,
  121. bytestring_passthrough=False,
  122. output_encoding=None,
  123. encoding_errors='strict',
  124. cache_args=None,
  125. cache_impl='beaker',
  126. cache_enabled=True,
  127. cache_type=None,
  128. cache_dir=None,
  129. cache_url=None,
  130. modulename_callable=None,
  131. module_writer=None,
  132. default_filters=None,
  133. buffer_filters=(),
  134. strict_undefined=False,
  135. imports=None,
  136. enable_loop=True,
  137. input_encoding=None,
  138. preprocessor=None):
  139. self.directories = [posixpath.normpath(d) for d in
  140. util.to_list(directories, ())
  141. ]
  142. self.module_directory = module_directory
  143. self.modulename_callable = modulename_callable
  144. self.filesystem_checks = filesystem_checks
  145. self.collection_size = collection_size
  146. if cache_args is None:
  147. cache_args = {}
  148. # transfer deprecated cache_* args
  149. if cache_dir:
  150. cache_args.setdefault('dir', cache_dir)
  151. if cache_url:
  152. cache_args.setdefault('url', cache_url)
  153. if cache_type:
  154. cache_args.setdefault('type', cache_type)
  155. self.template_args = {
  156. 'format_exceptions':format_exceptions,
  157. 'error_handler':error_handler,
  158. 'disable_unicode':disable_unicode,
  159. 'bytestring_passthrough':bytestring_passthrough,
  160. 'output_encoding':output_encoding,
  161. 'cache_impl':cache_impl,
  162. 'encoding_errors':encoding_errors,
  163. 'input_encoding':input_encoding,
  164. 'module_directory':module_directory,
  165. 'module_writer':module_writer,
  166. 'cache_args':cache_args,
  167. 'cache_enabled':cache_enabled,
  168. 'default_filters':default_filters,
  169. 'buffer_filters':buffer_filters,
  170. 'strict_undefined':strict_undefined,
  171. 'imports':imports,
  172. 'enable_loop':enable_loop,
  173. 'preprocessor':preprocessor}
  174. if collection_size == -1:
  175. self._collection = {}
  176. self._uri_cache = {}
  177. else:
  178. self._collection = util.LRUCache(collection_size)
  179. self._uri_cache = util.LRUCache(collection_size)
  180. self._mutex = threading.Lock()
  181. def get_template(self, uri):
  182. """Return a :class:`.Template` object corresponding to the given
  183. ``uri``.
  184. .. note:: The ``relativeto`` argument is not supported here at the moment.
  185. """
  186. try:
  187. if self.filesystem_checks:
  188. return self._check(uri, self._collection[uri])
  189. else:
  190. return self._collection[uri]
  191. except KeyError:
  192. u = re.sub(r'^\/+', '', uri)
  193. for dir in self.directories:
  194. srcfile = posixpath.normpath(posixpath.join(dir, u))
  195. if os.path.isfile(srcfile):
  196. return self._load(srcfile, uri)
  197. else:
  198. raise exceptions.TopLevelLookupException(
  199. "Cant locate template for uri %r" % uri)
  200. def adjust_uri(self, uri, relativeto):
  201. """Adjust the given ``uri`` based on the given relative URI."""
  202. key = (uri, relativeto)
  203. if key in self._uri_cache:
  204. return self._uri_cache[key]
  205. if uri[0] != '/':
  206. if relativeto is not None:
  207. v = self._uri_cache[key] = posixpath.join(
  208. posixpath.dirname(relativeto), uri)
  209. else:
  210. v = self._uri_cache[key] = '/' + uri
  211. else:
  212. v = self._uri_cache[key] = uri
  213. return v
  214. def filename_to_uri(self, filename):
  215. """Convert the given ``filename`` to a URI relative to
  216. this :class:`.TemplateCollection`."""
  217. try:
  218. return self._uri_cache[filename]
  219. except KeyError:
  220. value = self._relativeize(filename)
  221. self._uri_cache[filename] = value
  222. return value
  223. def _relativeize(self, filename):
  224. """Return the portion of a filename that is 'relative'
  225. to the directories in this lookup.
  226. """
  227. filename = posixpath.normpath(filename)
  228. for dir in self.directories:
  229. if filename[0:len(dir)] == dir:
  230. return filename[len(dir):]
  231. else:
  232. return None
  233. def _load(self, filename, uri):
  234. self._mutex.acquire()
  235. try:
  236. try:
  237. # try returning from collection one
  238. # more time in case concurrent thread already loaded
  239. return self._collection[uri]
  240. except KeyError:
  241. pass
  242. try:
  243. if self.modulename_callable is not None:
  244. module_filename = self.modulename_callable(filename, uri)
  245. else:
  246. module_filename = None
  247. self._collection[uri] = template = Template(
  248. uri=uri,
  249. filename=posixpath.normpath(filename),
  250. lookup=self,
  251. module_filename=module_filename,
  252. **self.template_args)
  253. return template
  254. except:
  255. # if compilation fails etc, ensure
  256. # template is removed from collection,
  257. # re-raise
  258. self._collection.pop(uri, None)
  259. raise
  260. finally:
  261. self._mutex.release()
  262. def _check(self, uri, template):
  263. if template.filename is None:
  264. return template
  265. try:
  266. template_stat = os.stat(template.filename)
  267. if template.module._modified_time < \
  268. template_stat[stat.ST_MTIME]:
  269. self._collection.pop(uri, None)
  270. return self._load(template.filename, uri)
  271. else:
  272. return template
  273. except OSError:
  274. self._collection.pop(uri, None)
  275. raise exceptions.TemplateLookupException(
  276. "Cant locate template for uri %r" % uri)
  277. def put_string(self, uri, text):
  278. """Place a new :class:`.Template` object into this
  279. :class:`.TemplateLookup`, based on the given string of
  280. ``text``.
  281. """
  282. self._collection[uri] = Template(
  283. text,
  284. lookup=self,
  285. uri=uri,
  286. **self.template_args)
  287. def put_template(self, uri, template):
  288. """Place a new :class:`.Template` object into this
  289. :class:`.TemplateLookup`, based on the given
  290. :class:`.Template` object.
  291. """
  292. self._collection[uri] = template