Added first code for a django-to-jinja converter
authorArmin Ronacher <armin.ronacher@active-4.com>
Tue, 16 Sep 2008 08:59:03 +0000 (10:59 +0200)
committerArmin Ronacher <armin.ronacher@active-4.com>
Tue, 16 Sep 2008 08:59:03 +0000 (10:59 +0200)
--HG--
branch : trunk

ext/django2jinja/django2jinja.py [new file with mode: 0644]
ext/django2jinja/templates/index.html [new file with mode: 0644]
ext/django2jinja/templates/layout.html [new file with mode: 0644]

diff --git a/ext/django2jinja/django2jinja.py b/ext/django2jinja/django2jinja.py
new file mode 100644 (file)
index 0000000..1c9130d
--- /dev/null
@@ -0,0 +1,258 @@
+# -*- coding: utf-8 -*-
+"""
+    Django to Jinja
+    ~~~~~~~~~~~~~~~
+
+    Helper module that can convert django templates into Jinja2 templates.
+
+    :copyright: Copyright 2008 by Armin Ronacher.
+    :license: BSD.
+"""
+import sys
+import re
+from jinja2.defaults import *
+from django.conf import settings
+settings.configure(TEMPLATE_DEBUG=True, TEMPLATE_DIRS=['templates'])
+from django.template import defaulttags as core_tags, loader, TextNode, \
+     FilterExpression, libraries, Variable, loader_tags
+from django.template.debug import DebugVariableNode as VariableNode
+from StringIO import StringIO
+
+
+node_handlers = {}
+_resolved_filters = {}
+
+
+_newline_re = re.compile(r'(?:\r\n|\r|\n)')
+
+
+def node(cls):
+    def proxy(f):
+        node_handlers[cls] = f
+        return f
+    return proxy
+
+
+def translate_variable_name(var):
+    if var == 'forloop':
+        return 'loop'
+    return var
+
+
+def get_filter_name(filter):
+    if filter not in _resolved_filters:
+        for library in libraries.values():
+            for key, value in library.filters.iteritems():
+                _resolved_filters[value] = key
+    return _resolved_filters.get(filter, None)
+
+
+class Writer(object):
+
+    def __init__(self, stream=None,
+                 block_start_string=BLOCK_START_STRING,
+                 block_end_string=BLOCK_END_STRING,
+                 variable_start_string=VARIABLE_START_STRING,
+                 variable_end_string=VARIABLE_END_STRING,
+                 comment_start_string=COMMENT_START_STRING,
+                 comment_end_string=COMMENT_END_STRING):
+        if stream is None:
+            stream = sys.stdout
+        self.stream = stream
+        self.block_start_string = block_start_string
+        self.block_end_string = block_end_string
+        self.variable_start_string = variable_start_string
+        self.variable_end_string = variable_end_string
+        self.comment_start_string = comment_start_string
+        self.comment_end_string = comment_end_string
+
+    def write(self, s):
+        self.stream.write(s)
+
+    def print_expr(self, expr):
+        self.start_variable()
+        self.write(expr)
+        self.end_variable()
+
+    def start_variable(self):
+        self.write(self.variable_start_string + ' ')
+
+    def end_variable(self):
+        self.write(' ' + self.variable_end_string)
+
+    def start_block(self):
+        self.write(self.block_start_string + ' ')
+
+    def end_block(self):
+        self.write(' ' + self.block_end_string)
+
+    def tag(self, name):
+        self.start_block()
+        self.write(name)
+        self.end_block()
+
+    def variable(self, name):
+        self.write(translate_variable_name(name))
+
+    def literal(self, value):
+        value = repr(value)
+        if value[:2] in ('u"', "u'"):
+            value = value[1:]
+        self.write(value)
+
+    def warn(self, message, node=None):
+        if node is not None and hasattr(node, 'source'):
+            message = '[%s:%d] %s' % (
+                node.source[0].name.replace('&lt;', '<') \
+                                   .replace('&gt;', '>'),
+                len(_newline_re.findall(node.source[0].source
+                                        [:node.source[1][0]])),
+                message
+            )
+        print >> sys.stderr, message
+
+    def node(self, node):
+        for cls, handler in node_handlers.iteritems():
+            if type(node) is cls:
+                handler(node, self)
+                break
+        else:
+            self.warn('Untranslatable node %s.%s found' % (
+                node.__module__,
+                node.__class__.__name__
+            ), node)
+
+    def body(self, nodes):
+        for node in nodes:
+            self.node(node)
+
+
+@node(TextNode)
+def text_node(node, writer):
+    writer.write(node.s)
+
+
+@node(Variable)
+def variable(node, writer):
+    if node.literal is not None:
+        writer.literal(node.literal)
+    else:
+        writer.variable(node.var)
+
+
+@node(VariableNode)
+def variable_node(node, writer):
+    writer.start_variable()
+    writer.node(node.filter_expression)
+    writer.end_variable()
+
+
+@node(FilterExpression)
+def filter_expression(node, writer):
+    writer.node(node.var)
+    for filter, args in node.filters:
+        name = get_filter_name(filter)
+        if name is None:
+            writer.warn('Could not find filter %s' % name, node)
+        writer.write('|%s' % name)
+        if args:
+            writer.write('(')
+            for idx, (is_var, value) in enumerate(args):
+                if idx:
+                    writer.write(', ')
+                if is_var:
+                    writer.node(value)
+                else:
+                    writer.literal(value)
+            writer.write(')')
+
+
+@node(core_tags.CommentNode)
+def comment_tag(node, writer):
+    pass
+
+
+@node(core_tags.DebugNode)
+def comment_tag(node, writer):
+    writer.warn('Debug tag detected.  Make sure to add a global function '
+                'called debug to the namespace.', node=node)
+    writer.print_expr('debug()')
+
+
+@node(core_tags.ForNode)
+def for_loop(node, writer):
+    writer.start_block()
+    writer.write('for ')
+    for idx, var in enumerate(node.loopvars):
+        if idx:
+            writer.write(', ')
+        writer.variable(var)
+    writer.write(' in ')
+    if node.is_reversed:
+        writer.write('(')
+    writer.node(node.sequence)
+    if node.is_reversed:
+        writer.write(')|reverse')
+    writer.end_block()
+    writer.body(node.nodelist_loop)
+    writer.tag('endfor')
+
+
+@node(core_tags.IfNode)
+def if_condition(node, writer):
+    writer.start_block()
+    writer.write('if ')
+    join_with = core_tags.IfNode.LinkTypes.or_ and 'or' or 'and'
+    for idx, (ifnot, expr) in enumerate(node.bool_exprs):
+        if idx:
+            writer.write(' %s ' % join_with)
+        if ifnot:
+            writer.write('not ')
+        writer.node(expr)
+    writer.end_block()
+    writer.body(node.nodelist_true)
+    if node.nodelist_false:
+        writer.tag('else')
+        writer.body(node.nodelist_false)
+    writer.tag('endif')
+
+
+@node(core_tags.IfEqualNode)
+def if_equal(node, writer):
+    writer.start_block()
+    writer.write('if ')
+    writer.node(node.var1)
+    writer.write(' == ')
+    writer.node(node.var2)
+    writer.end_block()
+    writer.body(node.nodelist_true)
+    if node.nodelist_false:
+        writer.tag('else')
+        writer.body(node.nodelist_false)
+    writer.tag('endif')
+
+
+@node(loader_tags.BlockNode)
+def block(node, writer):
+    writer.tag('block ' + node.name.replace('-', '_'))
+    node = node
+    while node.parent is not None:
+        node = node.parent
+    writer.body(node.nodelist)
+    writer.tag('endblock')
+
+
+@node(loader_tags.ExtendsNode)
+def extends(node, writer):
+    writer.start_block()
+    writer.write('extends ')
+    if node.parent_name_expr:
+        writer.node(node.parent_name_expr)
+    else:
+        writer.literal(node.parent_name)
+    writer.end_block()
+    writer.body(node.nodelist)
+
+
+if __name__ == '__main__':
+    Writer().body(loader.get_template('index.html'))
diff --git a/ext/django2jinja/templates/index.html b/ext/django2jinja/templates/index.html
new file mode 100644 (file)
index 0000000..a335b66
--- /dev/null
@@ -0,0 +1,16 @@
+{% extends "layout.html" %}
+{% block title %}Foo{% endblock %}
+{% block page-body %}
+    Hello {{ name|cut:"d"|escape }}!
+
+    {% for item in seq reversed %}
+        {% if forloop.index|divisibleby:2 %}
+            <li>{{ item }}</li>
+        {% endif %}
+    {% endfor %}
+    {% ifequal foo bar %}
+        haha
+    {% else %}
+        hmm
+    {% endifequal %}
+{% endblock %}
diff --git a/ext/django2jinja/templates/layout.html b/ext/django2jinja/templates/layout.html
new file mode 100644 (file)
index 0000000..3f21a12
--- /dev/null
@@ -0,0 +1,4 @@
+<title>{% block title %}{% endblock %}</title>
+<div class="body">
+  {% block page-body %}{% endblock %}
+</div>