runtime.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. # runtime.py
  2. # Copyright (C) 2006, 2007, 2008, 2009, 2010 Michael Bayer mike_mp@zzzcomputing.com
  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. """provides runtime services for templates, including Context, Namespace, and various helper functions."""
  7. from mako import exceptions, util
  8. import __builtin__, inspect, sys
  9. class Context(object):
  10. """provides runtime namespace, output buffer, and various callstacks for templates."""
  11. def __init__(self, buffer, **data):
  12. self._buffer_stack = [buffer]
  13. self._orig = data # original data, minus the builtins
  14. self._data = __builtin__.__dict__.copy() # the context data which includes builtins
  15. self._data.update(data)
  16. self._kwargs = data.copy()
  17. self._with_template = None
  18. self._outputting_as_unicode = None
  19. self.namespaces = {}
  20. # "capture" function which proxies to the generic "capture" function
  21. self._data['capture'] = lambda x, *args, **kwargs: capture(self, x, *args, **kwargs)
  22. # "caller" stack used by def calls with content
  23. self.caller_stack = self._data['caller'] = CallerStack()
  24. @property
  25. def lookup(self):
  26. return self._with_template.lookup
  27. @property
  28. def kwargs(self):
  29. return self._kwargs.copy()
  30. def push_caller(self, caller):
  31. self.caller_stack.append(caller)
  32. def pop_caller(self):
  33. del self.caller_stack[-1]
  34. def keys(self):
  35. return self._data.keys()
  36. def __getitem__(self, key):
  37. return self._data[key]
  38. def _push_writer(self):
  39. """push a capturing buffer onto this Context and return the new Writer function."""
  40. buf = util.FastEncodingBuffer()
  41. self._buffer_stack.append(buf)
  42. return buf.write
  43. def _pop_buffer_and_writer(self):
  44. """pop the most recent capturing buffer from this Context
  45. and return the current writer after the pop.
  46. """
  47. buf = self._buffer_stack.pop()
  48. return buf, self._buffer_stack[-1].write
  49. def _push_buffer(self):
  50. """push a capturing buffer onto this Context."""
  51. self._push_writer()
  52. def _pop_buffer(self):
  53. """pop the most recent capturing buffer from this Context."""
  54. return self._buffer_stack.pop()
  55. def get(self, key, default=None):
  56. return self._data.get(key, default)
  57. def write(self, string):
  58. """write a string to this Context's underlying output buffer."""
  59. self._buffer_stack[-1].write(string)
  60. def writer(self):
  61. """return the current writer function"""
  62. return self._buffer_stack[-1].write
  63. def _copy(self):
  64. c = Context.__new__(Context)
  65. c._buffer_stack = self._buffer_stack
  66. c._data = self._data.copy()
  67. c._orig = self._orig
  68. c._kwargs = self._kwargs
  69. c._with_template = self._with_template
  70. c._outputting_as_unicode = self._outputting_as_unicode
  71. c.namespaces = self.namespaces
  72. c.caller_stack = self.caller_stack
  73. return c
  74. def locals_(self, d):
  75. """create a new Context with a copy of this Context's current state, updated with the given dictionary."""
  76. if len(d) == 0:
  77. return self
  78. c = self._copy()
  79. c._data.update(d)
  80. return c
  81. def _clean_inheritance_tokens(self):
  82. """create a new copy of this Context with tokens related to inheritance state removed."""
  83. c = self._copy()
  84. x = c._data
  85. x.pop('self', None)
  86. x.pop('parent', None)
  87. x.pop('next', None)
  88. return c
  89. class CallerStack(list):
  90. def __init__(self):
  91. self.nextcaller = None
  92. def __nonzero__(self):
  93. return self._get_caller() and True or False
  94. def _get_caller(self):
  95. return self[-1]
  96. def __getattr__(self, key):
  97. return getattr(self._get_caller(), key)
  98. def _push_frame(self):
  99. self.append(self.nextcaller or None)
  100. self.nextcaller = None
  101. def _pop_frame(self):
  102. self.nextcaller = self.pop()
  103. class Undefined(object):
  104. """represents an undefined value in a template."""
  105. def __str__(self):
  106. raise NameError("Undefined")
  107. def __nonzero__(self):
  108. return False
  109. UNDEFINED = Undefined()
  110. class _NSAttr(object):
  111. def __init__(self, parent):
  112. self.__parent = parent
  113. def __getattr__(self, key):
  114. ns = self.__parent
  115. while ns:
  116. if hasattr(ns.module, key):
  117. return getattr(ns.module, key)
  118. else:
  119. ns = ns.inherits
  120. raise AttributeError(key)
  121. class Namespace(object):
  122. """provides access to collections of rendering methods, which
  123. can be local, from other templates, or from imported modules"""
  124. def __init__(self, name, context, module=None,
  125. template=None, templateuri=None,
  126. callables=None, inherits=None,
  127. populate_self=True, calling_uri=None):
  128. self.name = name
  129. if module is not None:
  130. mod = __import__(module)
  131. for token in module.split('.')[1:]:
  132. mod = getattr(mod, token)
  133. self._module = mod
  134. else:
  135. self._module = None
  136. if templateuri is not None:
  137. self.template = _lookup_template(context, templateuri, calling_uri)
  138. self._templateuri = self.template.module._template_uri
  139. else:
  140. self.template = template
  141. if self.template is not None:
  142. self._templateuri = self.template.module._template_uri
  143. self.context = context
  144. self.inherits = inherits
  145. if callables is not None:
  146. self.callables = dict([(c.func_name, c) for c in callables])
  147. else:
  148. self.callables = None
  149. if populate_self and self.template is not None:
  150. (lclcallable, lclcontext) = _populate_self_namespace(context, self.template, self_ns=self)
  151. @property
  152. def module(self):
  153. return self._module or self.template.module
  154. @property
  155. def filename(self):
  156. if self._module:
  157. return self._module.__file__
  158. else:
  159. return self.template.filename
  160. @property
  161. def uri(self):
  162. return self.template.uri
  163. @property
  164. def attr(self):
  165. if not hasattr(self, '_attr'):
  166. self._attr = _NSAttr(self)
  167. return self._attr
  168. def get_namespace(self, uri):
  169. """return a namespace corresponding to the given template uri.
  170. if a relative uri, it is adjusted to that of the template of this namespace"""
  171. key = (self, uri)
  172. if self.context.namespaces.has_key(key):
  173. return self.context.namespaces[key]
  174. else:
  175. ns = Namespace(uri, self.context._copy(), templateuri=uri, calling_uri=self._templateuri)
  176. self.context.namespaces[key] = ns
  177. return ns
  178. def get_template(self, uri):
  179. return _lookup_template(self.context, uri, self._templateuri)
  180. def get_cached(self, key, **kwargs):
  181. if self.template:
  182. if not self.template.cache_enabled:
  183. createfunc = kwargs.get('createfunc', None)
  184. if createfunc:
  185. return createfunc()
  186. else:
  187. return None
  188. if self.template.cache_dir:
  189. kwargs.setdefault('data_dir', self.template.cache_dir)
  190. if self.template.cache_type:
  191. kwargs.setdefault('type', self.template.cache_type)
  192. if self.template.cache_url:
  193. kwargs.setdefault('url', self.template.cache_url)
  194. return self.cache.get(key, **kwargs)
  195. @property
  196. def cache(self):
  197. return self.template.cache
  198. def include_file(self, uri, **kwargs):
  199. """include a file at the given uri"""
  200. _include_file(self.context, uri, self._templateuri, **kwargs)
  201. def _populate(self, d, l):
  202. for ident in l:
  203. if ident == '*':
  204. for (k, v) in self._get_star():
  205. d[k] = v
  206. else:
  207. d[ident] = getattr(self, ident)
  208. def _get_star(self):
  209. if self.callables:
  210. for key in self.callables:
  211. yield (key, self.callables[key])
  212. if self.template:
  213. def get(key):
  214. callable_ = self.template._get_def_callable(key)
  215. return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
  216. for k in self.template.module._exports:
  217. yield (k, get(k))
  218. if self._module:
  219. def get(key):
  220. callable_ = getattr(self._module, key)
  221. return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
  222. for k in dir(self._module):
  223. if k[0] != '_':
  224. yield (k, get(k))
  225. def __getattr__(self, key):
  226. if self.callables and key in self.callables:
  227. return self.callables[key]
  228. if self.template and self.template.has_def(key):
  229. callable_ = self.template._get_def_callable(key)
  230. return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
  231. if self._module and hasattr(self._module, key):
  232. callable_ = getattr(self._module, key)
  233. return lambda *args, **kwargs:callable_(self.context, *args, **kwargs)
  234. if self.inherits is not None:
  235. return getattr(self.inherits, key)
  236. raise AttributeError("Namespace '%s' has no member '%s'" % (self.name, key))
  237. def supports_caller(func):
  238. """apply a caller_stack compatibility decorator to a plain Python function."""
  239. def wrap_stackframe(context, *args, **kwargs):
  240. context.caller_stack._push_frame()
  241. try:
  242. return func(context, *args, **kwargs)
  243. finally:
  244. context.caller_stack._pop_frame()
  245. return wrap_stackframe
  246. def capture(context, callable_, *args, **kwargs):
  247. """execute the given template def, capturing the output into a buffer."""
  248. if not callable(callable_):
  249. raise exceptions.RuntimeException(
  250. "capture() function expects a callable as "
  251. "its argument (i.e. capture(func, *args, **kwargs))"
  252. )
  253. context._push_buffer()
  254. try:
  255. callable_(*args, **kwargs)
  256. finally:
  257. buf = context._pop_buffer()
  258. return buf.getvalue()
  259. def _decorate_toplevel(fn):
  260. def decorate_render(render_fn):
  261. def go(context, *args, **kw):
  262. def y(*args, **kw):
  263. return render_fn(context, *args, **kw)
  264. try:
  265. y.__name__ = render_fn.__name__[7:]
  266. except TypeError:
  267. # < Python 2.4
  268. pass
  269. return fn(y)(context, *args, **kw)
  270. return go
  271. return decorate_render
  272. def _decorate_inline(context, fn):
  273. def decorate_render(render_fn):
  274. dec = fn(render_fn)
  275. def go(*args, **kw):
  276. return dec(context, *args, **kw)
  277. return go
  278. return decorate_render
  279. def _include_file(context, uri, calling_uri, **kwargs):
  280. """locate the template from the given uri and include it in the current output."""
  281. template = _lookup_template(context, uri, calling_uri)
  282. (callable_, ctx) = _populate_self_namespace(context._clean_inheritance_tokens(), template)
  283. callable_(ctx, **_kwargs_for_include(callable_, context._orig, **kwargs))
  284. def _inherit_from(context, uri, calling_uri):
  285. """called by the _inherit method in template modules to set up the inheritance chain at the start
  286. of a template's execution."""
  287. if uri is None:
  288. return None
  289. template = _lookup_template(context, uri, calling_uri)
  290. self_ns = context['self']
  291. ih = self_ns
  292. while ih.inherits is not None:
  293. ih = ih.inherits
  294. lclcontext = context.locals_({'next':ih})
  295. ih.inherits = Namespace("self:%s" % template.uri, lclcontext, template = template, populate_self=False)
  296. context._data['parent'] = lclcontext._data['local'] = ih.inherits
  297. callable_ = getattr(template.module, '_mako_inherit', None)
  298. if callable_ is not None:
  299. ret = callable_(template, lclcontext)
  300. if ret:
  301. return ret
  302. gen_ns = getattr(template.module, '_mako_generate_namespaces', None)
  303. if gen_ns is not None:
  304. gen_ns(context)
  305. return (template.callable_, lclcontext)
  306. def _lookup_template(context, uri, relativeto):
  307. lookup = context._with_template.lookup
  308. if lookup is None:
  309. raise exceptions.TemplateLookupException("Template '%s' has no TemplateLookup associated" % context._with_template.uri)
  310. uri = lookup.adjust_uri(uri, relativeto)
  311. try:
  312. return lookup.get_template(uri)
  313. except exceptions.TopLevelLookupException, e:
  314. raise exceptions.TemplateLookupException(str(e))
  315. def _populate_self_namespace(context, template, self_ns=None):
  316. if self_ns is None:
  317. self_ns = Namespace('self:%s' % template.uri, context, template=template, populate_self=False)
  318. context._data['self'] = context._data['local'] = self_ns
  319. if hasattr(template.module, '_mako_inherit'):
  320. ret = template.module._mako_inherit(template, context)
  321. if ret:
  322. return ret
  323. return (template.callable_, context)
  324. def _render(template, callable_, args, data, as_unicode=False):
  325. """create a Context and return the string output of the given template and template callable."""
  326. if as_unicode:
  327. buf = util.FastEncodingBuffer(unicode=True)
  328. elif template.output_encoding:
  329. buf = util.FastEncodingBuffer(
  330. unicode=as_unicode,
  331. encoding=template.output_encoding,
  332. errors=template.encoding_errors)
  333. else:
  334. buf = util.StringIO()
  335. context = Context(buf, **data)
  336. context._outputting_as_unicode = as_unicode
  337. context._with_template = template
  338. _render_context(template, callable_, context, *args, **_kwargs_for_callable(callable_, data))
  339. return context._pop_buffer().getvalue()
  340. def _kwargs_for_callable(callable_, data):
  341. argspec = inspect.getargspec(callable_)
  342. # for normal pages, **pageargs is usually present
  343. if argspec[2]:
  344. return data
  345. # for rendering defs from the top level, figure out the args
  346. namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]
  347. kwargs = {}
  348. for arg in namedargs:
  349. if arg != 'context' and arg in data and arg not in kwargs:
  350. kwargs[arg] = data[arg]
  351. return kwargs
  352. def _kwargs_for_include(callable_, data, **kwargs):
  353. argspec = inspect.getargspec(callable_)
  354. namedargs = argspec[0] + [v for v in argspec[1:3] if v is not None]
  355. for arg in namedargs:
  356. if arg != 'context' and arg in data and arg not in kwargs:
  357. kwargs[arg] = data[arg]
  358. return kwargs
  359. def _render_context(tmpl, callable_, context, *args, **kwargs):
  360. import mako.template as template
  361. # create polymorphic 'self' namespace for this template with possibly updated context
  362. if not isinstance(tmpl, template.DefTemplate):
  363. # if main render method, call from the base of the inheritance stack
  364. (inherit, lclcontext) = _populate_self_namespace(context, tmpl)
  365. _exec_template(inherit, lclcontext, args=args, kwargs=kwargs)
  366. else:
  367. # otherwise, call the actual rendering method specified
  368. (inherit, lclcontext) = _populate_self_namespace(context, tmpl.parent)
  369. _exec_template(callable_, context, args=args, kwargs=kwargs)
  370. def _exec_template(callable_, context, args=None, kwargs=None):
  371. """execute a rendering callable given the callable, a Context, and optional explicit arguments
  372. the contextual Template will be located if it exists, and the error handling options specified
  373. on that Template will be interpreted here.
  374. """
  375. template = context._with_template
  376. if template is not None and (template.format_exceptions or template.error_handler):
  377. error = None
  378. try:
  379. callable_(context, *args, **kwargs)
  380. except Exception, e:
  381. _render_error(template, context, e)
  382. except:
  383. e = sys.exc_info()[0]
  384. _render_error(template, context, e)
  385. else:
  386. callable_(context, *args, **kwargs)
  387. def _render_error(template, context, error):
  388. if template.error_handler:
  389. result = template.error_handler(context, error)
  390. if not result:
  391. raise error
  392. else:
  393. error_template = exceptions.html_error_template()
  394. if context._outputting_as_unicode:
  395. context._buffer_stack[:] = [util.FastEncodingBuffer(unicode=True)]
  396. else:
  397. context._buffer_stack[:] = [util.FastEncodingBuffer(
  398. error_template.output_encoding,
  399. error_template.encoding_errors)]
  400. context._with_template = error_template
  401. error_template.render_context(context, error=error)