nativetypes.py 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. from ast import literal_eval
  2. from itertools import chain
  3. from itertools import islice
  4. from . import nodes
  5. from ._compat import text_type
  6. from .compiler import CodeGenerator
  7. from .compiler import has_safe_repr
  8. from .environment import Environment
  9. from .environment import Template
  10. def native_concat(nodes):
  11. """Return a native Python type from the list of compiled nodes. If
  12. the result is a single node, its value is returned. Otherwise, the
  13. nodes are concatenated as strings. If the result can be parsed with
  14. :func:`ast.literal_eval`, the parsed value is returned. Otherwise,
  15. the string is returned.
  16. :param nodes: Iterable of nodes to concatenate.
  17. """
  18. head = list(islice(nodes, 2))
  19. if not head:
  20. return None
  21. if len(head) == 1:
  22. raw = head[0]
  23. else:
  24. raw = u"".join([text_type(v) for v in chain(head, nodes)])
  25. try:
  26. return literal_eval(raw)
  27. except (ValueError, SyntaxError, MemoryError):
  28. return raw
  29. class NativeCodeGenerator(CodeGenerator):
  30. """A code generator which renders Python types by not adding
  31. ``to_string()`` around output nodes.
  32. """
  33. @staticmethod
  34. def _default_finalize(value):
  35. return value
  36. def _output_const_repr(self, group):
  37. return repr(u"".join([text_type(v) for v in group]))
  38. def _output_child_to_const(self, node, frame, finalize):
  39. const = node.as_const(frame.eval_ctx)
  40. if not has_safe_repr(const):
  41. raise nodes.Impossible()
  42. if isinstance(node, nodes.TemplateData):
  43. return const
  44. return finalize.const(const)
  45. def _output_child_pre(self, node, frame, finalize):
  46. if finalize.src is not None:
  47. self.write(finalize.src)
  48. def _output_child_post(self, node, frame, finalize):
  49. if finalize.src is not None:
  50. self.write(")")
  51. class NativeEnvironment(Environment):
  52. """An environment that renders templates to native Python types."""
  53. code_generator_class = NativeCodeGenerator
  54. class NativeTemplate(Template):
  55. environment_class = NativeEnvironment
  56. def render(self, *args, **kwargs):
  57. """Render the template to produce a native Python type. If the
  58. result is a single node, its value is returned. Otherwise, the
  59. nodes are concatenated as strings. If the result can be parsed
  60. with :func:`ast.literal_eval`, the parsed value is returned.
  61. Otherwise, the string is returned.
  62. """
  63. vars = dict(*args, **kwargs)
  64. try:
  65. return native_concat(self.root_render_func(self.new_context(vars)))
  66. except Exception:
  67. return self.environment.handle_exception()
  68. NativeEnvironment.template_class = NativeTemplate