Index: third_party/pystache/src/renderengine.py |
diff --git a/third_party/pystache/src/renderengine.py b/third_party/pystache/src/renderengine.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c797b1765a5ae09ca1c6bd9cd3adeb6a219ea913 |
--- /dev/null |
+++ b/third_party/pystache/src/renderengine.py |
@@ -0,0 +1,181 @@ |
+# coding: utf-8 |
+ |
+""" |
+Defines a class responsible for rendering logic. |
+ |
+""" |
+ |
+import re |
+ |
+from pystache.common import is_string |
+from pystache.parser import parse |
+ |
+ |
+def context_get(stack, name): |
+ """ |
+ Find and return a name from a ContextStack instance. |
+ |
+ """ |
+ return stack.get(name) |
+ |
+ |
+class RenderEngine(object): |
+ |
+ """ |
+ Provides a render() method. |
+ |
+ This class is meant only for internal use. |
+ |
+ As a rule, the code in this class operates on unicode strings where |
+ possible rather than, say, strings of type str or markupsafe.Markup. |
+ This means that strings obtained from "external" sources like partials |
+ and variable tag values are immediately converted to unicode (or |
+ escaped and converted to unicode) before being operated on further. |
+ This makes maintaining, reasoning about, and testing the correctness |
+ of the code much simpler. In particular, it keeps the implementation |
+ of this class independent of the API details of one (or possibly more) |
+ unicode subclasses (e.g. markupsafe.Markup). |
+ |
+ """ |
+ |
+ # TODO: it would probably be better for the constructor to accept |
+ # and set as an attribute a single RenderResolver instance |
+ # that encapsulates the customizable aspects of converting |
+ # strings and resolving partials and names from context. |
+ def __init__(self, literal=None, escape=None, resolve_context=None, |
+ resolve_partial=None, to_str=None): |
+ """ |
+ Arguments: |
+ |
+ literal: the function used to convert unescaped variable tag |
+ values to unicode, e.g. the value corresponding to a tag |
+ "{{{name}}}". The function should accept a string of type |
+ str or unicode (or a subclass) and return a string of type |
+ unicode (but not a proper subclass of unicode). |
+ This class will only pass basestring instances to this |
+ function. For example, it will call str() on integer variable |
+ values prior to passing them to this function. |
+ |
+ escape: the function used to escape and convert variable tag |
+ values to unicode, e.g. the value corresponding to a tag |
+ "{{name}}". The function should obey the same properties |
+ described above for the "literal" function argument. |
+ This function should take care to convert any str |
+ arguments to unicode just as the literal function should, as |
+ this class will not pass tag values to literal prior to passing |
+ them to this function. This allows for more flexibility, |
+ for example using a custom escape function that handles |
+ incoming strings of type markupsafe.Markup differently |
+ from plain unicode strings. |
+ |
+ resolve_context: the function to call to resolve a name against |
+ a context stack. The function should accept two positional |
+ arguments: a ContextStack instance and a name to resolve. |
+ |
+ resolve_partial: the function to call when loading a partial. |
+ The function should accept a template name string and return a |
+ template string of type unicode (not a subclass). |
+ |
+ to_str: a function that accepts an object and returns a string (e.g. |
+ the built-in function str). This function is used for string |
+ coercion whenever a string is required (e.g. for converting None |
+ or 0 to a string). |
+ |
+ """ |
+ self.escape = escape |
+ self.literal = literal |
+ self.resolve_context = resolve_context |
+ self.resolve_partial = resolve_partial |
+ self.to_str = to_str |
+ |
+ # TODO: Rename context to stack throughout this module. |
+ |
+ # From the spec: |
+ # |
+ # When used as the data value for an Interpolation tag, the lambda |
+ # MUST be treatable as an arity 0 function, and invoked as such. |
+ # The returned value MUST be rendered against the default delimiters, |
+ # then interpolated in place of the lambda. |
+ # |
+ def fetch_string(self, context, name): |
+ """ |
+ Get a value from the given context as a basestring instance. |
+ |
+ """ |
+ val = self.resolve_context(context, name) |
+ |
+ if callable(val): |
+ # Return because _render_value() is already a string. |
+ return self._render_value(val(), context) |
+ |
+ if not is_string(val): |
+ return self.to_str(val) |
+ |
+ return val |
+ |
+ def fetch_section_data(self, context, name): |
+ """ |
+ Fetch the value of a section as a list. |
+ |
+ """ |
+ data = self.resolve_context(context, name) |
+ |
+ # From the spec: |
+ # |
+ # If the data is not of a list type, it is coerced into a list |
+ # as follows: if the data is truthy (e.g. `!!data == true`), |
+ # use a single-element list containing the data, otherwise use |
+ # an empty list. |
+ # |
+ if not data: |
+ data = [] |
+ else: |
+ # The least brittle way to determine whether something |
+ # supports iteration is by trying to call iter() on it: |
+ # |
+ # http://docs.python.org/library/functions.html#iter |
+ # |
+ # It is not sufficient, for example, to check whether the item |
+ # implements __iter__ () (the iteration protocol). There is |
+ # also __getitem__() (the sequence protocol). In Python 2, |
+ # strings do not implement __iter__(), but in Python 3 they do. |
+ try: |
+ iter(data) |
+ except TypeError: |
+ # Then the value does not support iteration. |
+ data = [data] |
+ else: |
+ if is_string(data) or isinstance(data, dict): |
+ # Do not treat strings and dicts (which are iterable) as lists. |
+ data = [data] |
+ # Otherwise, treat the value as a list. |
+ |
+ return data |
+ |
+ def _render_value(self, val, context, delimiters=None): |
+ """ |
+ Render an arbitrary value. |
+ |
+ """ |
+ if not is_string(val): |
+ # In case the template is an integer, for example. |
+ val = self.to_str(val) |
+ if type(val) is not unicode: |
+ val = self.literal(val) |
+ return self.render(val, context, delimiters) |
+ |
+ def render(self, template, context_stack, delimiters=None): |
+ """ |
+ Render a unicode template string, and return as unicode. |
+ |
+ Arguments: |
+ |
+ template: a template string of type unicode (but not a proper |
+ subclass of unicode). |
+ |
+ context_stack: a ContextStack instance. |
+ |
+ """ |
+ parsed_template = parse(template, delimiters) |
+ |
+ return parsed_template.render(self, context_stack) |