http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/lexnparse.py
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/lexnparse.py
 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/lexnparse.py
new file mode 100644
index 0000000..69ff4bc
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/lexnparse.py
@@ -0,0 +1,390 @@
+# -*- coding: utf-8 -*-
+"""
+    ambari_jinja2.testsuite.lexnparse
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    All the unittests regarding lexing, parsing and syntax.
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+import time
+import tempfile
+import unittest
+
+from ambari_jinja2.testsuite import JinjaTestCase
+
+from ambari_jinja2 import Environment, Template, TemplateSyntaxError, \
+     UndefinedError, nodes
+
+env = Environment()
+
+
+# how does a string look like in jinja syntax?
+if sys.version_info < (3, 0):
+    def jinja_string_repr(string):
+        return repr(string)[1:]
+else:
+    jinja_string_repr = repr
+
+
+class LexerTestCase(JinjaTestCase):
+
+    def test_raw1(self):
+        tmpl = env.from_string('{% raw %}foo{% endraw %}|'
+                               '{%raw%}{{ bar }}|{% baz %}{%       endraw    
%}')
+        assert tmpl.render() == 'foo|{{ bar }}|{% baz %}'
+
+    def test_raw2(self):
+        tmpl = env.from_string('1  {%- raw -%}   2   {%- endraw -%}   3')
+        assert tmpl.render() == '123'
+
+    def test_balancing(self):
+        env = Environment('{%', '%}', '${', '}')
+        tmpl = env.from_string('''{% for item in seq
+            %}${{'foo': item}|upper}{% endfor %}''')
+        assert tmpl.render(seq=range(3)) == "{'FOO': 0}{'FOO': 1}{'FOO': 2}"
+
+    def test_comments(self):
+        env = Environment('<!--', '-->', '{', '}')
+        tmpl = env.from_string('''\
+<ul>
+<!--- for item in seq -->
+  <li>{item}</li>
+<!--- endfor -->
+</ul>''')
+        assert tmpl.render(seq=range(3)) == ("<ul>\n  <li>0</li>\n  "
+                                             "<li>1</li>\n  <li>2</li>\n</ul>")
+
+    def test_string_escapes(self):
+        for char in u'\0', u'\u2668', u'\xe4', u'\t', u'\r', u'\n':
+            tmpl = env.from_string('{{ %s }}' % jinja_string_repr(char))
+            assert tmpl.render() == char
+        assert env.from_string('{{ "\N{HOT SPRINGS}" }}').render() == u'\u2668'
+
+    def test_bytefallback(self):
+        from pprint import pformat
+        tmpl = env.from_string(u'''{{ 'foo'|pprint }}|{{ 'bär'|pprint }}''')
+        assert tmpl.render() == pformat('foo') + '|' + pformat(u'bär')
+
+    def test_operators(self):
+        from ambari_jinja2.lexer import operators
+        for test, expect in operators.iteritems():
+            if test in '([{}])':
+                continue
+            stream = env.lexer.tokenize('{{ %s }}' % test)
+            stream.next()
+            assert stream.current.type == expect
+
+    def test_normalizing(self):
+        for seq in '\r', '\r\n', '\n':
+            env = Environment(newline_sequence=seq)
+            tmpl = env.from_string('1\n2\r\n3\n4\n')
+            result = tmpl.render()
+            assert result.replace(seq, 'X') == '1X2X3X4'
+
+
+class ParserTestCase(JinjaTestCase):
+
+    def test_php_syntax(self):
+        env = Environment('<?', '?>', '<?=', '?>', '<!--', '-->')
+        tmpl = env.from_string('''\
+<!-- I'm a comment, I'm not interesting -->\
+<? for item in seq -?>
+    <?= item ?>
+<?- endfor ?>''')
+        assert tmpl.render(seq=range(5)) == '01234'
+
+    def test_erb_syntax(self):
+        env = Environment('<%', '%>', '<%=', '%>', '<%#', '%>')
+        tmpl = env.from_string('''\
+<%# I'm a comment, I'm not interesting %>\
+<% for item in seq -%>
+    <%= item %>
+<%- endfor %>''')
+        assert tmpl.render(seq=range(5)) == '01234'
+
+    def test_comment_syntax(self):
+        env = Environment('<!--', '-->', '${', '}', '<!--#', '-->')
+        tmpl = env.from_string('''\
+<!--# I'm a comment, I'm not interesting -->\
+<!-- for item in seq --->
+    ${item}
+<!--- endfor -->''')
+        assert tmpl.render(seq=range(5)) == '01234'
+
+    def test_balancing(self):
+        tmpl = env.from_string('''{{{'foo':'bar'}.foo}}''')
+        assert tmpl.render() == 'bar'
+
+    def test_start_comment(self):
+        tmpl = env.from_string('''{# foo comment
+and bar comment #}
+{% macro blub() %}foo{% endmacro %}
+{{ blub() }}''')
+        assert tmpl.render().strip() == 'foo'
+
+    def test_line_syntax(self):
+        env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%')
+        tmpl = env.from_string('''\
+<%# regular comment %>
+% for item in seq:
+    ${item}
+% endfor''')
+        assert [int(x.strip()) for x in tmpl.render(seq=range(5)).split()] == \
+               range(5)
+
+        env = Environment('<%', '%>', '${', '}', '<%#', '%>', '%', '##')
+        tmpl = env.from_string('''\
+<%# regular comment %>
+% for item in seq:
+    ${item} ## the rest of the stuff
+% endfor''')
+        assert [int(x.strip()) for x in tmpl.render(seq=range(5)).split()] == \
+                range(5)
+
+    def test_line_syntax_priority(self):
+        # XXX: why is the whitespace there in front of the newline?
+        env = Environment('{%', '%}', '${', '}', '/*', '*/', '##', '#')
+        tmpl = env.from_string('''\
+/* ignore me.
+   I'm a multiline comment */
+## for item in seq:
+* ${item}          # this is just extra stuff
+## endfor''')
+        assert tmpl.render(seq=[1, 2]).strip() == '* 1\n* 2'
+        env = Environment('{%', '%}', '${', '}', '/*', '*/', '#', '##')
+        tmpl = env.from_string('''\
+/* ignore me.
+   I'm a multiline comment */
+# for item in seq:
+* ${item}          ## this is just extra stuff
+    ## extra stuff i just want to ignore
+# endfor''')
+        assert tmpl.render(seq=[1, 2]).strip() == '* 1\n\n* 2'
+
+    def test_error_messages(self):
+        def assert_error(code, expected):
+            try:
+                Template(code)
+            except TemplateSyntaxError, e:
+                assert str(e) == expected, 'unexpected error message'
+            else:
+                assert False, 'that was suposed to be an error'
+
+        assert_error('{% for item in seq %}...{% endif %}',
+                     "Encountered unknown tag 'endif'. Jinja was looking "
+                     "for the following tags: 'endfor' or 'else'. The "
+                     "innermost block that needs to be closed is 'for'.")
+        assert_error('{% if foo %}{% for item in seq %}...{% endfor %}{% 
endfor %}',
+                     "Encountered unknown tag 'endfor'. Jinja was looking for "
+                     "the following tags: 'elif' or 'else' or 'endif'. The "
+                     "innermost block that needs to be closed is 'if'.")
+        assert_error('{% if foo %}',
+                     "Unexpected end of template. Jinja was looking for the "
+                     "following tags: 'elif' or 'else' or 'endif'. The "
+                     "innermost block that needs to be closed is 'if'.")
+        assert_error('{% for item in seq %}',
+                     "Unexpected end of template. Jinja was looking for the "
+                     "following tags: 'endfor' or 'else'. The innermost block "
+                     "that needs to be closed is 'for'.")
+        assert_error('{% block foo-bar-baz %}',
+                     "Block names in Jinja have to be valid Python identifiers 
"
+                     "and may not contain hypens, use an underscore instead.")
+        assert_error('{% unknown_tag %}',
+                     "Encountered unknown tag 'unknown_tag'.")
+
+
+class SyntaxTestCase(JinjaTestCase):
+
+    def test_call(self):
+        env = Environment()
+        env.globals['foo'] = lambda a, b, c, e, g: a + b + c + e + g
+        tmpl = env.from_string("{{ foo('a', c='d', e='f', *['b'], **{'g': 
'h'}) }}")
+        assert tmpl.render() == 'abdfh'
+
+    def test_slicing(self):
+        tmpl = env.from_string('{{ [1, 2, 3][:] }}|{{ [1, 2, 3][::-1] }}')
+        assert tmpl.render() == '[1, 2, 3]|[3, 2, 1]'
+
+    def test_attr(self):
+        tmpl = env.from_string("{{ foo.bar }}|{{ foo['bar'] }}")
+        assert tmpl.render(foo={'bar': 42}) == '42|42'
+
+    def test_subscript(self):
+        tmpl = env.from_string("{{ foo[0] }}|{{ foo[-1] }}")
+        assert tmpl.render(foo=[0, 1, 2]) == '0|2'
+
+    def test_tuple(self):
+        tmpl = env.from_string('{{ () }}|{{ (1,) }}|{{ (1, 2) }}')
+        assert tmpl.render() == '()|(1,)|(1, 2)'
+
+    def test_math(self):
+        tmpl = env.from_string('{{ (1 + 1 * 2) - 3 / 2 }}|{{ 2**3 }}')
+        assert tmpl.render() == '1.5|8'
+
+    def test_div(self):
+        tmpl = env.from_string('{{ 3 // 2 }}|{{ 3 / 2 }}|{{ 3 % 2 }}')
+        assert tmpl.render() == '1|1.5|1'
+
+    def test_unary(self):
+        tmpl = env.from_string('{{ +3 }}|{{ -3 }}')
+        assert tmpl.render() == '3|-3'
+
+    def test_concat(self):
+        tmpl = env.from_string("{{ [1, 2] ~ 'foo' }}")
+        assert tmpl.render() == '[1, 2]foo'
+
+    def test_compare(self):
+        tmpl = env.from_string('{{ 1 > 0 }}|{{ 1 >= 1 }}|{{ 2 < 3 }}|'
+                               '{{ 2 == 2 }}|{{ 1 <= 1 }}')
+        assert tmpl.render() == 'True|True|True|True|True'
+
+    def test_inop(self):
+        tmpl = env.from_string('{{ 1 in [1, 2, 3] }}|{{ 1 not in [1, 2, 3] }}')
+        assert tmpl.render() == 'True|False'
+
+    def test_literals(self):
+        tmpl = env.from_string('{{ [] }}|{{ {} }}|{{ () }}')
+        assert tmpl.render().lower() == '[]|{}|()'
+
+    def test_bool(self):
+        tmpl = env.from_string('{{ true and false }}|{{ false '
+                               'or true }}|{{ not false }}')
+        assert tmpl.render() == 'False|True|True'
+
+    def test_grouping(self):
+        tmpl = env.from_string('{{ (true and false) or (false and true) and 
not false }}')
+        assert tmpl.render() == 'False'
+
+    def test_django_attr(self):
+        tmpl = env.from_string('{{ [1, 2, 3].0 }}|{{ [[1]].0.0 }}')
+        assert tmpl.render() == '1|1'
+
+    def test_conditional_expression(self):
+        tmpl = env.from_string('''{{ 0 if true else 1 }}''')
+        assert tmpl.render() == '0'
+
+    def test_short_conditional_expression(self):
+        tmpl = env.from_string('<{{ 1 if false }}>')
+        assert tmpl.render() == '<>'
+
+        tmpl = env.from_string('<{{ (1 if false).bar }}>')
+        self.assert_raises(UndefinedError, tmpl.render)
+
+    def test_filter_priority(self):
+        tmpl = env.from_string('{{ "foo"|upper + "bar"|upper }}')
+        assert tmpl.render() == 'FOOBAR'
+
+    def test_function_calls(self):
+        tests = [
+            (True, '*foo, bar'),
+            (True, '*foo, *bar'),
+            (True, '*foo, bar=42'),
+            (True, '**foo, *bar'),
+            (True, '**foo, bar'),
+            (False, 'foo, bar'),
+            (False, 'foo, bar=42'),
+            (False, 'foo, bar=23, *args'),
+            (False, 'a, b=c, *d, **e'),
+            (False, '*foo, **bar')
+        ]
+        for should_fail, sig in tests:
+            if should_fail:
+                self.assert_raises(TemplateSyntaxError,
+                    env.from_string, '{{ foo(%s) }}' % sig)
+            else:
+                env.from_string('foo(%s)' % sig)
+
+    def test_tuple_expr(self):
+        for tmpl in [
+            '{{ () }}',
+            '{{ (1, 2) }}',
+            '{{ (1, 2,) }}',
+            '{{ 1, }}',
+            '{{ 1, 2 }}',
+            '{% for foo, bar in seq %}...{% endfor %}',
+            '{% for x in foo, bar %}...{% endfor %}',
+            '{% for x in foo, %}...{% endfor %}'
+        ]:
+            assert env.from_string(tmpl)
+
+    def test_trailing_comma(self):
+        tmpl = env.from_string('{{ (1, 2,) }}|{{ [1, 2,] }}|{{ {1: 2,} }}')
+        assert tmpl.render().lower() == '(1, 2)|[1, 2]|{1: 2}'
+
+    def test_block_end_name(self):
+        env.from_string('{% block foo %}...{% endblock foo %}')
+        self.assert_raises(TemplateSyntaxError, env.from_string,
+                           '{% block x %}{% endblock y %}')
+
+    def test_contant_casing(self):
+        for const in True, False, None:
+            tmpl = env.from_string('{{ %s }}|{{ %s }}|{{ %s }}' % (
+                str(const), str(const).lower(), str(const).upper()
+            ))
+            assert tmpl.render() == '%s|%s|' % (const, const)
+
+    def test_test_chaining(self):
+        self.assert_raises(TemplateSyntaxError, env.from_string,
+                           '{{ foo is string is sequence }}')
+        env.from_string('{{ 42 is string or 42 is number }}'
+            ).render() == 'True'
+
+    def test_string_concatenation(self):
+        tmpl = env.from_string('{{ "foo" "bar" "baz" }}')
+        assert tmpl.render() == 'foobarbaz'
+
+    def test_notin(self):
+        bar = xrange(100)
+        tmpl = env.from_string('''{{ not 42 in bar }}''')
+        assert tmpl.render(bar=bar) == unicode(not 42 in bar)
+
+    def test_implicit_subscribed_tuple(self):
+        class Foo(object):
+            def __getitem__(self, x):
+                return x
+        t = env.from_string('{{ foo[1, 2] }}')
+        assert t.render(foo=Foo()) == u'(1, 2)'
+
+    def test_raw2(self):
+        tmpl = env.from_string('{% raw %}{{ FOO }} and {% BAR %}{% endraw %}')
+        assert tmpl.render() == '{{ FOO }} and {% BAR %}'
+
+    def test_const(self):
+        tmpl = env.from_string('{{ true }}|{{ false }}|{{ none }}|'
+                               '{{ none is defined }}|{{ missing is defined 
}}')
+        assert tmpl.render() == 'True|False|None|True|False'
+
+    def test_neg_filter_priority(self):
+        node = env.parse('{{ -1|foo }}')
+        assert isinstance(node.body[0].nodes[0], nodes.Filter)
+        assert isinstance(node.body[0].nodes[0].node, nodes.Neg)
+
+    def test_const_assign(self):
+        constass1 = '''{% set true = 42 %}'''
+        constass2 = '''{% for none in seq %}{% endfor %}'''
+        for tmpl in constass1, constass2:
+            self.assert_raises(TemplateSyntaxError, env.from_string, tmpl)
+
+    def test_localset(self):
+        tmpl = env.from_string('''{% set foo = 0 %}\
+{% for item in [1, 2] %}{% set foo = 1 %}{% endfor %}\
+{{ foo }}''')
+        assert tmpl.render() == '0'
+
+    def test_parse_unary(self):
+        tmpl = env.from_string('{{ -foo["bar"] }}')
+        assert tmpl.render(foo={'bar': 42}) == '-42'
+        tmpl = env.from_string('{{ -foo["bar"]|abs }}')
+        assert tmpl.render(foo={'bar': 42}) == '42'
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(LexerTestCase))
+    suite.addTest(unittest.makeSuite(ParserTestCase))
+    suite.addTest(unittest.makeSuite(SyntaxTestCase))
+    return suite

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/loader.py
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/loader.py 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/loader.py
new file mode 100644
index 0000000..e133bd4
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/loader.py
@@ -0,0 +1,191 @@
+# -*- coding: utf-8 -*-
+"""
+    ambari_jinja2.testsuite.loader
+    ~~~~~~~~~~~~~~~~~~~~~~~
+
+    Test the loaders.
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import sys
+import time
+import tempfile
+import shutil
+import unittest
+
+from ambari_jinja2.testsuite import JinjaTestCase, dict_loader, \
+     package_loader, filesystem_loader, function_loader, \
+     choice_loader, prefix_loader
+
+from ambari_jinja2 import Environment, loaders
+from ambari_jinja2.loaders import split_template_path
+from ambari_jinja2.exceptions import TemplateNotFound
+
+
+class LoaderTestCase(JinjaTestCase):
+
+    def test_dict_loader(self):
+        env = Environment(loader=dict_loader)
+        tmpl = env.get_template('justdict.html')
+        assert tmpl.render().strip() == 'FOO'
+        self.assert_raises(TemplateNotFound, env.get_template, 'missing.html')
+
+    def test_package_loader(self):
+        env = Environment(loader=package_loader)
+        tmpl = env.get_template('test.html')
+        assert tmpl.render().strip() == 'BAR'
+        self.assert_raises(TemplateNotFound, env.get_template, 'missing.html')
+
+    def test_filesystem_loader(self):
+        env = Environment(loader=filesystem_loader)
+        tmpl = env.get_template('test.html')
+        assert tmpl.render().strip() == 'BAR'
+        tmpl = env.get_template('foo/test.html')
+        assert tmpl.render().strip() == 'FOO'
+        self.assert_raises(TemplateNotFound, env.get_template, 'missing.html')
+
+    def test_choice_loader(self):
+        env = Environment(loader=choice_loader)
+        tmpl = env.get_template('justdict.html')
+        assert tmpl.render().strip() == 'FOO'
+        tmpl = env.get_template('test.html')
+        assert tmpl.render().strip() == 'BAR'
+        self.assert_raises(TemplateNotFound, env.get_template, 'missing.html')
+
+    def test_function_loader(self):
+        env = Environment(loader=function_loader)
+        tmpl = env.get_template('justfunction.html')
+        assert tmpl.render().strip() == 'FOO'
+        self.assert_raises(TemplateNotFound, env.get_template, 'missing.html')
+
+    def test_prefix_loader(self):
+        env = Environment(loader=prefix_loader)
+        tmpl = env.get_template('a/test.html')
+        assert tmpl.render().strip() == 'BAR'
+        tmpl = env.get_template('b/justdict.html')
+        assert tmpl.render().strip() == 'FOO'
+        self.assert_raises(TemplateNotFound, env.get_template, 'missing')
+
+    def test_caching(self):
+        changed = False
+        class TestLoader(loaders.BaseLoader):
+            def get_source(self, environment, template):
+                return u'foo', None, lambda: not changed
+        env = Environment(loader=TestLoader(), cache_size=-1)
+        tmpl = env.get_template('template')
+        assert tmpl is env.get_template('template')
+        changed = True
+        assert tmpl is not env.get_template('template')
+        changed = False
+
+        env = Environment(loader=TestLoader(), cache_size=0)
+        assert env.get_template('template') \
+               is not env.get_template('template')
+
+        env = Environment(loader=TestLoader(), cache_size=2)
+        t1 = env.get_template('one')
+        t2 = env.get_template('two')
+        assert t2 is env.get_template('two')
+        assert t1 is env.get_template('one')
+        t3 = env.get_template('three')
+        assert 'one' in env.cache
+        assert 'two' not in env.cache
+        assert 'three' in env.cache
+
+    def test_split_template_path(self):
+        assert split_template_path('foo/bar') == ['foo', 'bar']
+        assert split_template_path('./foo/bar') == ['foo', 'bar']
+        self.assert_raises(TemplateNotFound, split_template_path, '../foo')
+
+
+class ModuleLoaderTestCase(JinjaTestCase):
+    archive = None
+
+    def compile_down(self, zip='deflated', py_compile=False):
+        super(ModuleLoaderTestCase, self).setup()
+        log = []
+        self.reg_env = Environment(loader=prefix_loader)
+        if zip is not None:
+            self.archive = tempfile.mkstemp(suffix='.zip')[1]
+        else:
+            self.archive = tempfile.mkdtemp()
+        self.reg_env.compile_templates(self.archive, zip=zip,
+                                       log_function=log.append,
+                                       py_compile=py_compile)
+        self.mod_env = Environment(loader=loaders.ModuleLoader(self.archive))
+        return ''.join(log)
+
+    def teardown(self):
+        super(ModuleLoaderTestCase, self).teardown()
+        if hasattr(self, 'mod_env'):
+            if os.path.isfile(self.archive):
+                os.remove(self.archive)
+            else:
+                shutil.rmtree(self.archive)
+            self.archive = None
+
+    def test_log(self):
+        log = self.compile_down()
+        assert 'Compiled "a/foo/test.html" as ' \
+               'tmpl_a790caf9d669e39ea4d280d597ec891c4ef0404a' in log
+        assert 'Finished compiling templates' in log
+        assert 'Could not compile "a/syntaxerror.html": ' \
+               'Encountered unknown tag \'endif\'' in log
+
+    def _test_common(self):
+        tmpl1 = self.reg_env.get_template('a/test.html')
+        tmpl2 = self.mod_env.get_template('a/test.html')
+        assert tmpl1.render() == tmpl2.render()
+
+        tmpl1 = self.reg_env.get_template('b/justdict.html')
+        tmpl2 = self.mod_env.get_template('b/justdict.html')
+        assert tmpl1.render() == tmpl2.render()
+
+    def test_deflated_zip_compile(self):
+        self.compile_down(zip='deflated')
+        self._test_common()
+
+    def test_stored_zip_compile(self):
+        self.compile_down(zip='stored')
+        self._test_common()
+
+    def test_filesystem_compile(self):
+        self.compile_down(zip=None)
+        self._test_common()
+
+    def test_weak_references(self):
+        self.compile_down()
+        tmpl = self.mod_env.get_template('a/test.html')
+        key = loaders.ModuleLoader.get_template_key('a/test.html')
+        name = self.mod_env.loader.module.__name__
+
+        assert hasattr(self.mod_env.loader.module, key)
+        assert name in sys.modules
+
+        # unset all, ensure the module is gone from sys.modules
+        self.mod_env = tmpl = None
+
+        try:
+            import gc
+            gc.collect()
+        except:
+            pass
+
+        assert name not in sys.modules
+
+    def test_byte_compilation(self):
+        log = self.compile_down(py_compile=True)
+        assert 'Byte-compiled "a/test.html"' in log
+        tmpl1 = self.mod_env.get_template('a/test.html')
+        mod = self.mod_env.loader.module. \
+            tmpl_3c4ddf650c1a73df961a6d3d2ce2752f1b8fd490
+        assert mod.__file__.endswith('.pyc')
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(LoaderTestCase))
+    suite.addTest(unittest.makeSuite(ModuleLoaderTestCase))
+    return suite

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/regression.py
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/regression.py
 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/regression.py
new file mode 100644
index 0000000..b457df2
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/regression.py
@@ -0,0 +1,258 @@
+# -*- coding: utf-8 -*-
+"""
+    ambari_jinja2.testsuite.regression
+    ~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Tests corner cases and bugs.
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import time
+import tempfile
+import unittest
+
+from ambari_jinja2.testsuite import JinjaTestCase
+
+from ambari_jinja2 import Template, Environment, DictLoader, 
TemplateSyntaxError, \
+     TemplateNotFound, PrefixLoader
+
+env = Environment()
+
+
+class CornerTestCase(JinjaTestCase):
+
+    def test_assigned_scoping(self):
+        t = env.from_string('''
+        {%- for item in (1, 2, 3, 4) -%}
+            [{{ item }}]
+        {%- endfor %}
+        {{- item -}}
+        ''')
+        assert t.render(item=42) == '[1][2][3][4]42'
+
+        t = env.from_string('''
+        {%- for item in (1, 2, 3, 4) -%}
+            [{{ item }}]
+        {%- endfor %}
+        {%- set item = 42 %}
+        {{- item -}}
+        ''')
+        assert t.render() == '[1][2][3][4]42'
+
+        t = env.from_string('''
+        {%- set item = 42 %}
+        {%- for item in (1, 2, 3, 4) -%}
+            [{{ item }}]
+        {%- endfor %}
+        {{- item -}}
+        ''')
+        assert t.render() == '[1][2][3][4]42'
+
+    def test_closure_scoping(self):
+        t = env.from_string('''
+        {%- set wrapper = "<FOO>" %}
+        {%- for item in (1, 2, 3, 4) %}
+            {%- macro wrapper() %}[{{ item }}]{% endmacro %}
+            {{- wrapper() }}
+        {%- endfor %}
+        {{- wrapper -}}
+        ''')
+        assert t.render() == '[1][2][3][4]<FOO>'
+
+        t = env.from_string('''
+        {%- for item in (1, 2, 3, 4) %}
+            {%- macro wrapper() %}[{{ item }}]{% endmacro %}
+            {{- wrapper() }}
+        {%- endfor %}
+        {%- set wrapper = "<FOO>" %}
+        {{- wrapper -}}
+        ''')
+        assert t.render() == '[1][2][3][4]<FOO>'
+
+        t = env.from_string('''
+        {%- for item in (1, 2, 3, 4) %}
+            {%- macro wrapper() %}[{{ item }}]{% endmacro %}
+            {{- wrapper() }}
+        {%- endfor %}
+        {{- wrapper -}}
+        ''')
+        assert t.render(wrapper=23) == '[1][2][3][4]23'
+
+
+class BugTestCase(JinjaTestCase):
+
+    def test_keyword_folding(self):
+        env = Environment()
+        env.filters['testing'] = lambda value, some: value + some
+        assert env.from_string("{{ 'test'|testing(some='stuff') }}") \
+               .render() == 'teststuff'
+
+    def test_extends_output_bugs(self):
+        env = Environment(loader=DictLoader({
+            'parent.html': '(({% block title %}{% endblock %}))'
+        }))
+
+        t = env.from_string('{% if expr %}{% extends "parent.html" %}{% endif 
%}'
+                            '[[{% block title %}title{% endblock %}]]'
+                            '{% for item in [1, 2, 3] %}({{ item }}){% endfor 
%}')
+        assert t.render(expr=False) == '[[title]](1)(2)(3)'
+        assert t.render(expr=True) == '((title))'
+
+    def test_urlize_filter_escaping(self):
+        tmpl = env.from_string('{{ "http://www.example.org/<foo"|urlize }}')
+        assert tmpl.render() == '<a 
href="http://www.example.org/&lt;foo";>http://www.example.org/&lt;foo</a>'
+
+    def test_loop_call_loop(self):
+        tmpl = env.from_string('''
+
+        {% macro test() %}
+            {{ caller() }}
+        {% endmacro %}
+
+        {% for num1 in range(5) %}
+            {% call test() %}
+                {% for num2 in range(10) %}
+                    {{ loop.index }}
+                {% endfor %}
+            {% endcall %}
+        {% endfor %}
+
+        ''')
+
+        assert tmpl.render().split() == map(unicode, range(1, 11)) * 5
+
+    def test_weird_inline_comment(self):
+        env = Environment(line_statement_prefix='%')
+        self.assert_raises(TemplateSyntaxError, env.from_string,
+                           '% for item in seq {# missing #}\n...% endfor')
+
+    def test_old_macro_loop_scoping_bug(self):
+        tmpl = env.from_string('{% for i in (1, 2) %}{{ i }}{% endfor %}'
+                               '{% macro i() %}3{% endmacro %}{{ i() }}')
+        assert tmpl.render() == '123'
+
+    def test_partial_conditional_assignments(self):
+        tmpl = env.from_string('{% if b %}{% set a = 42 %}{% endif %}{{ a }}')
+        assert tmpl.render(a=23) == '23'
+        assert tmpl.render(b=True) == '42'
+
+    def test_stacked_locals_scoping_bug(self):
+        env = Environment(line_statement_prefix='#')
+        t = env.from_string('''\
+# for j in [1, 2]:
+#   set x = 1
+#   for i in [1, 2]:
+#     print x
+#     if i % 2 == 0:
+#       set x = x + 1
+#     endif
+#   endfor
+# endfor
+# if a
+#   print 'A'
+# elif b
+#   print 'B'
+# elif c == d
+#   print 'C'
+# else
+#   print 'D'
+# endif
+    ''')
+        assert t.render(a=0, b=False, c=42, d=42.0) == '1111C'
+
+    def test_stacked_locals_scoping_bug_twoframe(self):
+        t = Template('''
+            {% set x = 1 %}
+            {% for item in foo %}
+                {% if item == 1 %}
+                    {% set x = 2 %}
+                {% endif %}
+            {% endfor %}
+            {{ x }}
+        ''')
+        rv = t.render(foo=[1]).strip()
+        assert rv == u'1'
+
+    def test_call_with_args(self):
+        t = Template("""{% macro dump_users(users) -%}
+        <ul>
+          {%- for user in users -%}
+            <li><p>{{ user.username|e }}</p>{{ caller(user) }}</li>
+          {%- endfor -%}
+          </ul>
+        {%- endmacro -%}
+
+        {% call(user) dump_users(list_of_user) -%}
+          <dl>
+            <dl>Realname</dl>
+            <dd>{{ user.realname|e }}</dd>
+            <dl>Description</dl>
+            <dd>{{ user.description }}</dd>
+          </dl>
+        {% endcall %}""")
+
+        assert [x.strip() for x in t.render(list_of_user=[{
+            'username':'apo',
+            'realname':'something else',
+            'description':'test'
+        }]).splitlines()] == [
+            u'<ul><li><p>apo</p><dl>',
+            u'<dl>Realname</dl>',
+            u'<dd>something else</dd>',
+            u'<dl>Description</dl>',
+            u'<dd>test</dd>',
+            u'</dl>',
+            u'</li></ul>'
+        ]
+
+    def test_empty_if_condition_fails(self):
+        self.assert_raises(TemplateSyntaxError, Template, '{% if %}....{% 
endif %}')
+        self.assert_raises(TemplateSyntaxError, Template, '{% if foo %}...{% 
elif %}...{% endif %}')
+        self.assert_raises(TemplateSyntaxError, Template, '{% for x in %}..{% 
endfor %}')
+
+    def test_recursive_loop_bug(self):
+        tpl1 = Template("""
+        {% for p in foo recursive%}
+            {{p.bar}}
+            {% for f in p.fields recursive%}
+                {{f.baz}}
+                {{p.bar}}
+                {% if f.rec %}
+                    {{ loop(f.sub) }}
+                {% endif %}
+            {% endfor %}
+        {% endfor %}
+        """)
+
+        tpl2 = Template("""
+        {% for p in foo%}
+            {{p.bar}}
+            {% for f in p.fields recursive%}
+                {{f.baz}}
+                {{p.bar}}
+                {% if f.rec %}
+                    {{ loop(f.sub) }}
+                {% endif %}
+            {% endfor %}
+        {% endfor %}
+        """)
+
+    def test_correct_prefix_loader_name(self):
+        env = Environment(loader=PrefixLoader({
+            'foo':  DictLoader({})
+        }))
+        try:
+            env.get_template('foo/bar.html')
+        except TemplateNotFound, e:
+            assert e.name == 'foo/bar.html'
+        else:
+            assert False, 'expected error here'
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(CornerTestCase))
+    suite.addTest(unittest.makeSuite(BugTestCase))
+    return suite

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/broken.html
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/broken.html
 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/broken.html
new file mode 100644
index 0000000..77669fa
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/broken.html
@@ -0,0 +1,3 @@
+Before
+{{ fail() }}
+After

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/foo/test.html
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/foo/test.html
 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/foo/test.html
new file mode 100644
index 0000000..b7d6715
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/foo/test.html
@@ -0,0 +1 @@
+FOO

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/syntaxerror.html
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/syntaxerror.html
 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/syntaxerror.html
new file mode 100644
index 0000000..f21b817
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/syntaxerror.html
@@ -0,0 +1,4 @@
+Foo
+{% for item in broken %}
+  ...
+{% endif %}

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/test.html
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/test.html
 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/test.html
new file mode 100644
index 0000000..ba578e4
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/res/templates/test.html
@@ -0,0 +1 @@
+BAR

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/security.py
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/security.py
 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/security.py
new file mode 100644
index 0000000..893526d
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/security.py
@@ -0,0 +1,134 @@
+# -*- coding: utf-8 -*-
+"""
+    ambari_jinja2.testsuite.security
+    ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Checks the sandbox and other security features.
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import time
+import tempfile
+import unittest
+
+from ambari_jinja2.testsuite import JinjaTestCase
+
+from ambari_jinja2 import Environment
+from ambari_jinja2.sandbox import SandboxedEnvironment, \
+     ImmutableSandboxedEnvironment, unsafe
+from ambari_jinja2 import Markup, escape
+from ambari_jinja2.exceptions import SecurityError, TemplateSyntaxError
+
+
+class PrivateStuff(object):
+
+    def bar(self):
+        return 23
+
+    @unsafe
+    def foo(self):
+        return 42
+
+    def __repr__(self):
+        return 'PrivateStuff'
+
+
+class PublicStuff(object):
+    bar = lambda self: 23
+    _foo = lambda self: 42
+
+    def __repr__(self):
+        return 'PublicStuff'
+
+
+class SandboxTestCase(JinjaTestCase):
+
+    def test_unsafe(self):
+        env = SandboxedEnvironment()
+        self.assert_raises(SecurityError, env.from_string("{{ foo.foo() 
}}").render,
+                           foo=PrivateStuff())
+        self.assert_equal(env.from_string("{{ foo.bar() 
}}").render(foo=PrivateStuff()), '23')
+
+        self.assert_raises(SecurityError, env.from_string("{{ foo._foo() 
}}").render,
+                           foo=PublicStuff())
+        self.assert_equal(env.from_string("{{ foo.bar() 
}}").render(foo=PublicStuff()), '23')
+        self.assert_equal(env.from_string("{{ foo.__class__ 
}}").render(foo=42), '')
+        self.assert_equal(env.from_string("{{ foo.func_code 
}}").render(foo=lambda:None), '')
+        self.assert_raises(SecurityError, env.from_string(
+            "{{ foo.__class__.__subclasses__() }}").render, foo=42)
+
+    def test_immutable_environment(self):
+        env = ImmutableSandboxedEnvironment()
+        self.assert_raises(SecurityError, env.from_string(
+            '{{ [].append(23) }}').render)
+        self.assert_raises(SecurityError, env.from_string(
+            '{{ {1:2}.clear() }}').render)
+
+    def test_restricted(self):
+        env = SandboxedEnvironment()
+        self.assert_raises(TemplateSyntaxError, env.from_string,
+                      "{% for item.attribute in seq %}...{% endfor %}")
+        self.assert_raises(TemplateSyntaxError, env.from_string,
+                      "{% for foo, bar.baz in seq %}...{% endfor %}")
+
+    def test_markup_operations(self):
+        # adding two strings should escape the unsafe one
+        unsafe = '<script 
type="application/x-some-script">alert("foo");</script>'
+        safe = Markup('<em>username</em>')
+        assert unsafe + safe == unicode(escape(unsafe)) + unicode(safe)
+
+        # string interpolations are safe to use too
+        assert Markup('<em>%s</em>') % '<bad user>' == \
+               '<em>&lt;bad user&gt;</em>'
+        assert Markup('<em>%(username)s</em>') % {
+            'username': '<bad user>'
+        } == '<em>&lt;bad user&gt;</em>'
+
+        # an escaped object is markup too
+        assert type(Markup('foo') + 'bar') is Markup
+
+        # and it implements __html__ by returning itself
+        x = Markup("foo")
+        assert x.__html__() is x
+
+        # it also knows how to treat __html__ objects
+        class Foo(object):
+            def __html__(self):
+                return '<em>awesome</em>'
+            def __unicode__(self):
+                return 'awesome'
+        assert Markup(Foo()) == '<em>awesome</em>'
+        assert Markup('<strong>%s</strong>') % Foo() == \
+               '<strong><em>awesome</em></strong>'
+
+        # escaping and unescaping
+        assert escape('"<>&\'') == '&#34;&lt;&gt;&amp;&#39;'
+        assert Markup("<em>Foo &amp; Bar</em>").striptags() == "Foo & Bar"
+        assert Markup("&lt;test&gt;").unescape() == "<test>"
+
+
+    def test_template_data(self):
+        env = Environment(autoescape=True)
+        t = env.from_string('{% macro say_hello(name) %}'
+                            '<p>Hello {{ name }}!</p>{% endmacro %}'
+                            '{{ say_hello("<blink>foo</blink>") }}')
+        escaped_out = '<p>Hello &lt;blink&gt;foo&lt;/blink&gt;!</p>'
+        assert t.render() == escaped_out
+        assert unicode(t.module) == escaped_out
+        assert escape(t.module) == escaped_out
+        assert t.module.say_hello('<blink>foo</blink>') == escaped_out
+        assert escape(t.module.say_hello('<blink>foo</blink>')) == escaped_out
+
+
+    def test_attr_filter(self):
+        env = SandboxedEnvironment()
+        tmpl = env.from_string('{{ 
42|attr("__class__")|attr("__subclasses__")() }}')
+        self.assert_raises(SecurityError, tmpl.render)
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(SandboxTestCase))
+    return suite

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/tests.py
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/tests.py 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/tests.py
new file mode 100644
index 0000000..2f7b792
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/tests.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+"""
+    ambari_jinja2.testsuite.tests
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Who tests the tests?
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD, see LICENSE for more details.
+"""
+import unittest
+from ambari_jinja2.testsuite import JinjaTestCase
+
+from ambari_jinja2 import Markup, Environment
+
+env = Environment()
+
+
+class TestsTestCase(JinjaTestCase):
+
+    def test_defined(self):
+        tmpl = env.from_string('{{ missing is defined }}|{{ true is defined 
}}')
+        assert tmpl.render() == 'False|True'
+
+    def test_even(self):
+        tmpl = env.from_string('''{{ 1 is even }}|{{ 2 is even }}''')
+        assert tmpl.render() == 'False|True'
+
+    def test_odd(self):
+        tmpl = env.from_string('''{{ 1 is odd }}|{{ 2 is odd }}''')
+        assert tmpl.render() == 'True|False'
+
+    def test_lower(self):
+        tmpl = env.from_string('''{{ "foo" is lower }}|{{ "FOO" is lower }}''')
+        assert tmpl.render() == 'True|False'
+
+    def test_typechecks(self):
+        tmpl = env.from_string('''
+            {{ 42 is undefined }}
+            {{ 42 is defined }}
+            {{ 42 is none }}
+            {{ none is none }}
+            {{ 42 is number }}
+            {{ 42 is string }}
+            {{ "foo" is string }}
+            {{ "foo" is sequence }}
+            {{ [1] is sequence }}
+            {{ range is callable }}
+            {{ 42 is callable }}
+            {{ range(5) is iterable }}
+        ''')
+        assert tmpl.render().split() == [
+            'False', 'True', 'False', 'True', 'True', 'False',
+            'True', 'True', 'True', 'True', 'False', 'True'
+        ]
+
+    def test_sequence(self):
+        tmpl = env.from_string(
+            '{{ [1, 2, 3] is sequence }}|'
+            '{{ "foo" is sequence }}|'
+            '{{ 42 is sequence }}'
+        )
+        assert tmpl.render() == 'True|True|False'
+
+    def test_upper(self):
+        tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}')
+        assert tmpl.render() == 'True|False'
+
+    def test_sameas(self):
+        tmpl = env.from_string('{{ foo is sameas false }}|'
+                               '{{ 0 is sameas false }}')
+        assert tmpl.render(foo=False) == 'True|False'
+
+    def test_no_paren_for_arg1(self):
+        tmpl = env.from_string('{{ foo is sameas none }}')
+        assert tmpl.render(foo=None) == 'True'
+
+    def test_escaped(self):
+        env = Environment(autoescape=True)
+        tmpl = env.from_string('{{ x is escaped }}|{{ y is escaped }}')
+        assert tmpl.render(x='foo', y=Markup('foo')) == 'False|True'
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(TestsTestCase))
+    return suite

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/utils.py
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/utils.py 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/utils.py
new file mode 100644
index 0000000..297ba6a
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/testsuite/utils.py
@@ -0,0 +1,85 @@
+# -*- coding: utf-8 -*-
+"""
+    ambari_jinja2.testsuite.utils
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Tests utilities jinja uses.
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD, see LICENSE for more details.
+"""
+import os
+import gc
+import unittest
+
+import pickle
+
+from ambari_jinja2.testsuite import JinjaTestCase
+
+from ambari_jinja2 import Environment, Undefined, DebugUndefined, \
+     StrictUndefined, UndefinedError, Template, meta
+from ambari_jinja2.utils import LRUCache, escape, object_type_repr
+
+
+class LRUCacheTestCase(JinjaTestCase):
+
+    def test_simple(self):
+        d = LRUCache(3)
+        d["a"] = 1
+        d["b"] = 2
+        d["c"] = 3
+        d["a"]
+        d["d"] = 4
+        assert len(d) == 3
+        assert 'a' in d and 'c' in d and 'd' in d and 'b' not in d
+
+    def test_pickleable(self):
+        cache = LRUCache(2)
+        cache["foo"] = 42
+        cache["bar"] = 23
+        cache["foo"]
+
+        for protocol in range(3):
+            copy = pickle.loads(pickle.dumps(cache, protocol))
+            assert copy.capacity == cache.capacity
+            assert copy._mapping == cache._mapping
+            assert copy._queue == cache._queue
+
+
+class HelpersTestCase(JinjaTestCase):
+
+    def test_object_type_repr(self):
+        class X(object):
+            pass
+        self.assert_equal(object_type_repr(42), 'int object')
+        self.assert_equal(object_type_repr([]), 'list object')
+        self.assert_equal(object_type_repr(X()),
+                         'ambari_jinja2.testsuite.utils.X object')
+        self.assert_equal(object_type_repr(None), 'None')
+        self.assert_equal(object_type_repr(Ellipsis), 'Ellipsis')
+
+
+class MarkupLeakTestCase(JinjaTestCase):
+
+    def test_markup_leaks(self):
+        counts = set()
+        for count in xrange(20):
+            for item in xrange(1000):
+                escape("foo")
+                escape("<foo>")
+                escape(u"foo")
+                escape(u"<foo>")
+            counts.add(len(gc.get_objects()))
+        assert len(counts) == 1, 'ouch, c extension seems to leak objects'
+
+
+def suite():
+    suite = unittest.TestSuite()
+    suite.addTest(unittest.makeSuite(LRUCacheTestCase))
+    suite.addTest(unittest.makeSuite(HelpersTestCase))
+
+    # this test only tests the c extension
+    if not hasattr(escape, 'func_code'):
+        suite.addTest(unittest.makeSuite(MarkupLeakTestCase))
+
+    return suite

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/utils.py
----------------------------------------------------------------------
diff --git a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/utils.py 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/utils.py
new file mode 100644
index 0000000..bd3c0b4
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/utils.py
@@ -0,0 +1,601 @@
+# -*- coding: utf-8 -*-
+"""
+    ambari_jinja2.utils
+    ~~~~~~~~~~~~
+
+    Utility functions.
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD, see LICENSE for more details.
+"""
+import re
+import sys
+import errno
+try:
+    from thread import allocate_lock
+except ImportError:
+    from dummy_thread import allocate_lock
+from collections import deque
+from itertools import imap
+
+
+_word_split_re = re.compile(r'(\s+)')
+_punctuation_re = re.compile(
+    '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % (
+        '|'.join(imap(re.escape, ('(', '<', '&lt;'))),
+        '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '&gt;')))
+    )
+)
+_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$')
+_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)')
+_entity_re = re.compile(r'&([^;]+);')
+_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
+_digits = '0123456789'
+
+# special singleton representing missing values for the runtime
+missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})()
+
+# internal code
+internal_code = set()
+
+
+# concatenate a list of strings and convert them to unicode.
+# unfortunately there is a bug in python 2.4 and lower that causes
+# unicode.join trash the traceback.
+_concat = u''.join
+try:
+    def _test_gen_bug():
+        raise TypeError(_test_gen_bug)
+        yield None
+    _concat(_test_gen_bug())
+except TypeError, _error:
+    if not _error.args or _error.args[0] is not _test_gen_bug:
+        def concat(gen):
+            try:
+                return _concat(list(gen))
+            except:
+                # this hack is needed so that the current frame
+                # does not show up in the traceback.
+                exc_type, exc_value, tb = sys.exc_info()
+                raise exc_type, exc_value, tb.tb_next
+    else:
+        concat = _concat
+    del _test_gen_bug, _error
+
+
+# for python 2.x we create outselves a next() function that does the
+# basics without exception catching.
+try:
+    next = next
+except NameError:
+    def next(x):
+        return x.next()
+
+
+# if this python version is unable to deal with unicode filenames
+# when passed to encode we let this function encode it properly.
+# This is used in a couple of places.  As far as Jinja is concerned
+# filenames are unicode *or* bytestrings in 2.x and unicode only in
+# 3.x because compile cannot handle bytes
+if sys.version_info < (3, 0):
+    def _encode_filename(filename):
+        if isinstance(filename, unicode):
+            return filename.encode('utf-8')
+        return filename
+else:
+    def _encode_filename(filename):
+        assert filename is None or isinstance(filename, str), \
+            'filenames must be strings'
+        return filename
+
+from keyword import iskeyword as is_python_keyword
+
+
+# common types.  These do exist in the special types module too which however
+# does not exist in IronPython out of the box.  Also that way we don't have
+# to deal with implementation specific stuff here
+class _C(object):
+    def method(self): pass
+def _func():
+    yield None
+FunctionType = type(_func)
+GeneratorType = type(_func())
+MethodType = type(_C.method)
+CodeType = type(_C.method.func_code)
+try:
+    raise TypeError()
+except TypeError:
+    _tb = sys.exc_info()[2]
+    TracebackType = type(_tb)
+    FrameType = type(_tb.tb_frame)
+del _C, _tb, _func
+
+
+def contextfunction(f):
+    """This decorator can be used to mark a function or method context 
callable.
+    A context callable is passed the active :class:`Context` as first argument 
when
+    called from the template.  This is useful if a function wants to get access
+    to the context or functions provided on the context object.  For example
+    a function that returns a sorted list of template variables the current
+    template exports could look like this::
+
+        @contextfunction
+        def get_exported_names(context):
+            return sorted(context.exported_vars)
+    """
+    f.contextfunction = True
+    return f
+
+
+def evalcontextfunction(f):
+    """This decoraotr can be used to mark a function or method as an eval
+    context callable.  This is similar to the :func:`contextfunction`
+    but instead of passing the context, an evaluation context object is
+    passed.  For more information about the eval context, see
+    :ref:`eval-context`.
+
+    .. versionadded:: 2.4
+    """
+    f.evalcontextfunction = True
+    return f
+
+
+def environmentfunction(f):
+    """This decorator can be used to mark a function or method as environment
+    callable.  This decorator works exactly like the :func:`contextfunction`
+    decorator just that the first argument is the active :class:`Environment`
+    and not context.
+    """
+    f.environmentfunction = True
+    return f
+
+
+def internalcode(f):
+    """Marks the function as internally used"""
+    internal_code.add(f.func_code)
+    return f
+
+
+def is_undefined(obj):
+    """Check if the object passed is undefined.  This does nothing more than
+    performing an instance check against :class:`Undefined` but looks nicer.
+    This can be used for custom filters or tests that want to react to
+    undefined variables.  For example a custom default filter can look like
+    this::
+
+        def default(var, default=''):
+            if is_undefined(var):
+                return default
+            return var
+    """
+    from ambari_jinja2.runtime import Undefined
+    return isinstance(obj, Undefined)
+
+
+def consume(iterable):
+    """Consumes an iterable without doing anything with it."""
+    for event in iterable:
+        pass
+
+
+def clear_caches():
+    """Jinja2 keeps internal caches for environments and lexers.  These are
+    used so that Jinja2 doesn't have to recreate environments and lexers all
+    the time.  Normally you don't have to care about that but if you are
+    messuring memory consumption you may want to clean the caches.
+    """
+    from ambari_jinja2.environment import _spontaneous_environments
+    from ambari_jinja2.lexer import _lexer_cache
+    _spontaneous_environments.clear()
+    _lexer_cache.clear()
+
+
+def import_string(import_name, silent=False):
+    """Imports an object based on a string.  This use useful if you want to
+    use import paths as endpoints or something similar.  An import path can
+    be specified either in dotted notation (``xml.sax.saxutils.escape``)
+    or with a colon as object delimiter (``xml.sax.saxutils:escape``).
+
+    If the `silent` is True the return value will be `None` if the import
+    fails.
+
+    :return: imported object
+    """
+    try:
+        if ':' in import_name:
+            module, obj = import_name.split(':', 1)
+        elif '.' in import_name:
+            items = import_name.split('.')
+            module = '.'.join(items[:-1])
+            obj = items[-1]
+        else:
+            return __import__(import_name)
+        return getattr(__import__(module, None, None, [obj]), obj)
+    except (ImportError, AttributeError):
+        if not silent:
+            raise
+
+
+def open_if_exists(filename, mode='rb'):
+    """Returns a file descriptor for the filename if that file exists,
+    otherwise `None`.
+    """
+    try:
+        return open(filename, mode)
+    except IOError, e:
+        if e.errno not in (errno.ENOENT, errno.EISDIR):
+            raise
+
+
+def object_type_repr(obj):
+    """Returns the name of the object's type.  For some recognized
+    singletons the name of the object is returned instead. (For
+    example for `None` and `Ellipsis`).
+    """
+    if obj is None:
+        return 'None'
+    elif obj is Ellipsis:
+        return 'Ellipsis'
+    # __builtin__ in 2.x, builtins in 3.x
+    if obj.__class__.__module__ in ('__builtin__', 'builtins'):
+        name = obj.__class__.__name__
+    else:
+        name = obj.__class__.__module__ + '.' + obj.__class__.__name__
+    return '%s object' % name
+
+
+def pformat(obj, verbose=False):
+    """Prettyprint an object.  Either use the `pretty` library or the
+    builtin `pprint`.
+    """
+    try:
+        from pretty import pretty
+        return pretty(obj, verbose=verbose)
+    except ImportError:
+        from pprint import pformat
+        return pformat(obj)
+
+
+def urlize(text, trim_url_limit=None, nofollow=False):
+    """Converts any URLs in text into clickable links. Works on http://,
+    https:// and www. links. Links can have trailing punctuation (periods,
+    commas, close-parens) and leading punctuation (opening parens) and
+    it'll still do the right thing.
+
+    If trim_url_limit is not None, the URLs in link text will be limited
+    to trim_url_limit characters.
+
+    If nofollow is True, the URLs in link text will get a rel="nofollow"
+    attribute.
+    """
+    trim_url = lambda x, limit=trim_url_limit: limit is not None \
+                         and (x[:limit] + (len(x) >=limit and '...'
+                         or '')) or x
+    words = _word_split_re.split(unicode(escape(text)))
+    nofollow_attr = nofollow and ' rel="nofollow"' or ''
+    for i, word in enumerate(words):
+        match = _punctuation_re.match(word)
+        if match:
+            lead, middle, trail = match.groups()
+            if middle.startswith('www.') or (
+                '@' not in middle and
+                not middle.startswith('http://') and
+                len(middle) > 0 and
+                middle[0] in _letters + _digits and (
+                    middle.endswith('.org') or
+                    middle.endswith('.net') or
+                    middle.endswith('.com')
+                )):
+                middle = '<a href="http://%s"%s>%s</a>' % (middle,
+                    nofollow_attr, trim_url(middle))
+            if middle.startswith('http://') or \
+               middle.startswith('https://'):
+                middle = '<a href="%s"%s>%s</a>' % (middle,
+                    nofollow_attr, trim_url(middle))
+            if '@' in middle and not middle.startswith('www.') and \
+               not ':' in middle and _simple_email_re.match(middle):
+                middle = '<a href="mailto:%s";>%s</a>' % (middle, middle)
+            if lead + middle + trail != word:
+                words[i] = lead + middle + trail
+    return u''.join(words)
+
+
+def generate_lorem_ipsum(n=5, html=True, min=20, max=100):
+    """Generate some lorem impsum for the template."""
+    from ambari_jinja2.constants import LOREM_IPSUM_WORDS
+    from random import choice, randrange
+    words = LOREM_IPSUM_WORDS.split()
+    result = []
+
+    for _ in xrange(n):
+        next_capitalized = True
+        last_comma = last_fullstop = 0
+        word = None
+        last = None
+        p = []
+
+        # each paragraph contains out of 20 to 100 words.
+        for idx, _ in enumerate(xrange(randrange(min, max))):
+            while True:
+                word = choice(words)
+                if word != last:
+                    last = word
+                    break
+            if next_capitalized:
+                word = word.capitalize()
+                next_capitalized = False
+            # add commas
+            if idx - randrange(3, 8) > last_comma:
+                last_comma = idx
+                last_fullstop += 2
+                word += ','
+            # add end of sentences
+            if idx - randrange(10, 20) > last_fullstop:
+                last_comma = last_fullstop = idx
+                word += '.'
+                next_capitalized = True
+            p.append(word)
+
+        # ensure that the paragraph ends with a dot.
+        p = u' '.join(p)
+        if p.endswith(','):
+            p = p[:-1] + '.'
+        elif not p.endswith('.'):
+            p += '.'
+        result.append(p)
+
+    if not html:
+        return u'\n\n'.join(result)
+    return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result))
+
+
+class LRUCache(object):
+    """A simple LRU Cache implementation."""
+
+    # this is fast for small capacities (something below 1000) but doesn't
+    # scale.  But as long as it's only used as storage for templates this
+    # won't do any harm.
+
+    def __init__(self, capacity):
+        self.capacity = capacity
+        self._mapping = {}
+        self._queue = deque()
+        self._postinit()
+
+    def _postinit(self):
+        # alias all queue methods for faster lookup
+        self._popleft = self._queue.popleft
+        self._pop = self._queue.pop
+        if hasattr(self._queue, 'remove'):
+            self._remove = self._queue.remove
+        self._wlock = allocate_lock()
+        self._append = self._queue.append
+
+    def _remove(self, obj):
+        """Python 2.4 compatibility."""
+        for idx, item in enumerate(self._queue):
+            if item == obj:
+                del self._queue[idx]
+                break
+
+    def __getstate__(self):
+        return {
+            'capacity':     self.capacity,
+            '_mapping':     self._mapping,
+            '_queue':       self._queue
+        }
+
+    def __setstate__(self, d):
+        self.__dict__.update(d)
+        self._postinit()
+
+    def __getnewargs__(self):
+        return (self.capacity,)
+
+    def copy(self):
+        """Return an shallow copy of the instance."""
+        rv = self.__class__(self.capacity)
+        rv._mapping.update(self._mapping)
+        rv._queue = deque(self._queue)
+        return rv
+
+    def get(self, key, default=None):
+        """Return an item from the cache dict or `default`"""
+        try:
+            return self[key]
+        except KeyError:
+            return default
+
+    def setdefault(self, key, default=None):
+        """Set `default` if the key is not in the cache otherwise
+        leave unchanged. Return the value of this key.
+        """
+        try:
+            return self[key]
+        except KeyError:
+            self[key] = default
+            return default
+
+    def clear(self):
+        """Clear the cache."""
+        self._wlock.acquire()
+        try:
+            self._mapping.clear()
+            self._queue.clear()
+        finally:
+            self._wlock.release()
+
+    def __contains__(self, key):
+        """Check if a key exists in this cache."""
+        return key in self._mapping
+
+    def __len__(self):
+        """Return the current size of the cache."""
+        return len(self._mapping)
+
+    def __repr__(self):
+        return '<%s %r>' % (
+            self.__class__.__name__,
+            self._mapping
+        )
+
+    def __getitem__(self, key):
+        """Get an item from the cache. Moves the item up so that it has the
+        highest priority then.
+
+        Raise an `KeyError` if it does not exist.
+        """
+        rv = self._mapping[key]
+        if self._queue[-1] != key:
+            try:
+                self._remove(key)
+            except ValueError:
+                # if something removed the key from the container
+                # when we read, ignore the ValueError that we would
+                # get otherwise.
+                pass
+            self._append(key)
+        return rv
+
+    def __setitem__(self, key, value):
+        """Sets the value for an item. Moves the item up so that it
+        has the highest priority then.
+        """
+        self._wlock.acquire()
+        try:
+            if key in self._mapping:
+                try:
+                    self._remove(key)
+                except ValueError:
+                    # __getitem__ is not locked, it might happen
+                    pass
+            elif len(self._mapping) == self.capacity:
+                del self._mapping[self._popleft()]
+            self._append(key)
+            self._mapping[key] = value
+        finally:
+            self._wlock.release()
+
+    def __delitem__(self, key):
+        """Remove an item from the cache dict.
+        Raise an `KeyError` if it does not exist.
+        """
+        self._wlock.acquire()
+        try:
+            del self._mapping[key]
+            try:
+                self._remove(key)
+            except ValueError:
+                # __getitem__ is not locked, it might happen
+                pass
+        finally:
+            self._wlock.release()
+
+    def items(self):
+        """Return a list of items."""
+        result = [(key, self._mapping[key]) for key in list(self._queue)]
+        result.reverse()
+        return result
+
+    def iteritems(self):
+        """Iterate over all items."""
+        return iter(self.items())
+
+    def values(self):
+        """Return a list of all values."""
+        return [x[1] for x in self.items()]
+
+    def itervalue(self):
+        """Iterate over all values."""
+        return iter(self.values())
+
+    def keys(self):
+        """Return a list of all keys ordered by most recent usage."""
+        return list(self)
+
+    def iterkeys(self):
+        """Iterate over all keys in the cache dict, ordered by
+        the most recent usage.
+        """
+        return reversed(tuple(self._queue))
+
+    __iter__ = iterkeys
+
+    def __reversed__(self):
+        """Iterate over the values in the cache dict, oldest items
+        coming first.
+        """
+        return iter(tuple(self._queue))
+
+    __copy__ = copy
+
+
+# register the LRU cache as mutable mapping if possible
+try:
+    from collections import MutableMapping
+    MutableMapping.register(LRUCache)
+except ImportError:
+    pass
+
+
+class Cycler(object):
+    """A cycle helper for templates."""
+
+    def __init__(self, *items):
+        if not items:
+            raise RuntimeError('at least one item has to be provided')
+        self.items = items
+        self.reset()
+
+    def reset(self):
+        """Resets the cycle."""
+        self.pos = 0
+
+    @property
+    def current(self):
+        """Returns the current item."""
+        return self.items[self.pos]
+
+    def next(self):
+        """Goes one item ahead and returns it."""
+        rv = self.current
+        self.pos = (self.pos + 1) % len(self.items)
+        return rv
+
+
+class Joiner(object):
+    """A joining helper for templates."""
+
+    def __init__(self, sep=u', '):
+        self.sep = sep
+        self.used = False
+
+    def __call__(self):
+        if not self.used:
+            self.used = True
+            return u''
+        return self.sep
+
+
+# try markupsafe first, if that fails go with Jinja2's bundled version
+# of markupsafe.  Markupsafe was previously Jinja2's implementation of
+# the Markup object but was moved into a separate package in a patchleve
+# release
+try:
+    from markupsafe import Markup, escape, soft_unicode
+except ImportError:
+    from ambari_jinja2._markupsafe import Markup, escape, soft_unicode
+
+
+# partials
+try:
+    from functools import partial
+except ImportError:
+    class partial(object):
+        def __init__(self, _func, *args, **kwargs):
+            self._func = _func
+            self._args = args
+            self._kwargs = kwargs
+        def __call__(self, *args, **kwargs):
+            kwargs.update(self._kwargs)
+            return self._func(*(self._args + args), **kwargs)

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/visitor.py
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/visitor.py 
b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/visitor.py
new file mode 100644
index 0000000..0561adf
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_jinja2/ambari_jinja2/visitor.py
@@ -0,0 +1,87 @@
+# -*- coding: utf-8 -*-
+"""
+    ambari_jinja2.visitor
+    ~~~~~~~~~~~~~~
+
+    This module implements a visitor for the nodes.
+
+    :copyright: (c) 2010 by the Jinja Team.
+    :license: BSD.
+"""
+from ambari_jinja2.nodes import Node
+
+
+class NodeVisitor(object):
+    """Walks the abstract syntax tree and call visitor functions for every
+    node found.  The visitor functions may return values which will be
+    forwarded by the `visit` method.
+
+    Per default the visitor functions for the nodes are ``'visit_'`` +
+    class name of the node.  So a `TryFinally` node visit function would
+    be `visit_TryFinally`.  This behavior can be changed by overriding
+    the `get_visitor` function.  If no visitor function exists for a node
+    (return value `None`) the `generic_visit` visitor is used instead.
+    """
+
+    def get_visitor(self, node):
+        """Return the visitor function for this node or `None` if no visitor
+        exists for this node.  In that case the generic visit function is
+        used instead.
+        """
+        method = 'visit_' + node.__class__.__name__
+        return getattr(self, method, None)
+
+    def visit(self, node, *args, **kwargs):
+        """Visit a node."""
+        f = self.get_visitor(node)
+        if f is not None:
+            return f(node, *args, **kwargs)
+        return self.generic_visit(node, *args, **kwargs)
+
+    def generic_visit(self, node, *args, **kwargs):
+        """Called if no explicit visitor function exists for a node."""
+        for node in node.iter_child_nodes():
+            self.visit(node, *args, **kwargs)
+
+
+class NodeTransformer(NodeVisitor):
+    """Walks the abstract syntax tree and allows modifications of nodes.
+
+    The `NodeTransformer` will walk the AST and use the return value of the
+    visitor functions to replace or remove the old node.  If the return
+    value of the visitor function is `None` the node will be removed
+    from the previous location otherwise it's replaced with the return
+    value.  The return value may be the original node in which case no
+    replacement takes place.
+    """
+
+    def generic_visit(self, node, *args, **kwargs):
+        for field, old_value in node.iter_fields():
+            if isinstance(old_value, list):
+                new_values = []
+                for value in old_value:
+                    if isinstance(value, Node):
+                        value = self.visit(value, *args, **kwargs)
+                        if value is None:
+                            continue
+                        elif not isinstance(value, Node):
+                            new_values.extend(value)
+                            continue
+                    new_values.append(value)
+                old_value[:] = new_values
+            elif isinstance(old_value, Node):
+                new_node = self.visit(old_value, *args, **kwargs)
+                if new_node is None:
+                    delattr(node, field)
+                else:
+                    setattr(node, field, new_node)
+        return node
+
+    def visit_list(self, node, *args, **kwargs):
+        """As transformers may return lists in some places this method
+        can be used to enforce a list as return value.
+        """
+        rv = self.visit(node, *args, **kwargs)
+        if not isinstance(rv, list):
+            rv = [rv]
+        return rv

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/artwork/jinjalogo.svg
----------------------------------------------------------------------
diff --git a/ambari-common/src/main/python/ambari_jinja2/artwork/jinjalogo.svg 
b/ambari-common/src/main/python/ambari_jinja2/artwork/jinjalogo.svg
new file mode 100644
index 0000000..cc11c5c
--- /dev/null
+++ b/ambari-common/src/main/python/ambari_jinja2/artwork/jinjalogo.svg
@@ -0,0 +1,132 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/";
+   xmlns:cc="http://web.resource.org/cc/";
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#";
+   xmlns:svg="http://www.w3.org/2000/svg";
+   xmlns="http://www.w3.org/2000/svg";
+   xmlns:xlink="http://www.w3.org/1999/xlink";
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd";
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape";
+   width="300"
+   height="120"
+   id="svg2"
+   sodipodi:version="0.32"
+   inkscape:version="0.45.1"
+   version="1.0"
+   sodipodi:docbase="/Users/mitsuhiko/Development/ambari_jinja2/artwork"
+   sodipodi:docname="jinjalogo.svg"
+   
inkscape:export-filename="/Users/mitsuhiko/Development/ambari_jinja2/docs/_static/jinjabanner.png"
+   inkscape:export-xdpi="60"
+   inkscape:export-ydpi="60"
+   inkscape:output_extension="org.inkscape.output.svg.inkscape">
+  <defs
+     id="defs4">
+    <linearGradient
+       id="linearGradient6558">
+      <stop
+         style="stop-color:#575757;stop-opacity:1;"
+         offset="0"
+         id="stop6560" />
+      <stop
+         style="stop-color:#2f2f2f;stop-opacity:1;"
+         offset="1"
+         id="stop6562" />
+    </linearGradient>
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6558"
+       id="radialGradient6564"
+       cx="61.297766"
+       cy="60.910986"
+       fx="61.297766"
+       fy="60.910986"
+       r="44.688254"
+       gradientTransform="matrix(1,0,0,0.945104,0,3.343747)"
+       gradientUnits="userSpaceOnUse" />
+    <radialGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6558"
+       id="radialGradient6580"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(1,0,0,0.945104,0.355158,3.334402)"
+       cx="61.297766"
+       cy="60.910986"
+       fx="61.297766"
+       fy="60.910986"
+       r="44.688254" />
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6558"
+       id="linearGradient4173"
+       x1="255.15521"
+       y1="32.347946"
+       x2="279.8912"
+       y2="32.347946"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.8073249,0,0,0.8073249,57.960878,7.4036303)" 
/>
+    <linearGradient
+       inkscape:collect="always"
+       xlink:href="#linearGradient6558"
+       id="linearGradient5145"
+       gradientUnits="userSpaceOnUse"
+       gradientTransform="matrix(0.7902775,0,0,0.82474,60.019977,8.0684132)"
+       x1="255.15521"
+       y1="32.347946"
+       x2="279.8912"
+       y2="32.347946" />
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     gridtolerance="10000"
+     guidetolerance="10"
+     objecttolerance="10"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="2.8"
+     inkscape:cx="137.4752"
+     inkscape:cy="57.574575"
+     inkscape:document-units="px"
+     inkscape:current-layer="layer1"
+     width="300px"
+     height="120px"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:window-width="1396"
+     inkscape:window-height="900"
+     inkscape:window-x="0"
+     inkscape:window-y="22" />
+  <metadata
+     id="metadata7">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage"; />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1">
+    <path
+       
style="font-size:12px;font-style:normal;font-weight:normal;fill:#f4f4f4;fill-opacity:1;stroke:#e7e7e7;stroke-width:0.8;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1;font-family:Bitstream
 Vera Sans;stroke-miterlimit:4;stroke-dasharray:none"
+       d="M 165.36463,80.874808 L 165.36463,80.874808 L 153.32556,80.874808 L 
153.32556,81.8344 L 147.64994,81.8344 L 147.64994,36.035583 L 
165.36463,36.035583 L 165.36463,20.333129 C 170.58154,21.031083 
173.07533,22.077914 172.84609,23.473621 C 172.78871,24.055258 
172.21545,24.549594 171.12624,24.956624 L 171.12624,36.035583 L 
189.09895,36.035583 L 189.09895,82.532286 L 183.33733,82.532286 L 
183.33733,80.874808 L 171.12624,80.874808 L 171.12624,102.94548 L 
165.36463,102.94548 L 165.36463,80.874808 M 153.32556,55.489173 L 
153.32556,55.489173 L 165.36463,55.489173 L 165.36463,41.793146 L 
153.32556,41.793146 L 153.32556,55.489173 M 171.12624,55.489173 L 
171.12624,55.489173 L 183.33733,55.489173 L 183.33733,41.793146 L 
171.12624,41.793146 L 171.12624,55.489173 M 183.33733,61.333977 L 
183.33733,61.333977 L 171.12624,61.333977 L 171.12624,75.030006 L 
183.33733,75.030006 L 183.33733,61.333977 M 165.36463,61.333977 L 
165.36463,61.333977 L 153.32556,61.333977 L 153.32556,75.030006 L 165.364
 63,75.030006 L 165.36463,61.333977 M 132.85897,59.414792 C 137.33069,63.136883 
140.99969,67.934848 143.86618,73.808701 L 139.13654,77.385372 C 
137.24467,72.965445 134.6362,69.12707 131.31114,65.87024 L 131.31114,102.94548 
L 125.63554,102.94548 L 125.63554,68.57455 C 122.31042,71.947693 
118.52671,74.913707 114.28436,77.47261 L 109.64069,73.372526 C 
121.50782,67.091566 130.62312,55.489212 136.98668,38.565417 L 
116.26221,38.565417 L 116.26221,32.720615 L 125.80754,32.720615 L 
125.80754,20.333129 C 130.85245,21.031083 133.31761,22.048838 
133.20299,23.386383 C 133.14561,24.026183 132.57235,24.549594 
131.48307,24.956624 L 131.48307,32.720615 L 140.77043,32.720615 L 
143.60824,36.733469 C 140.68444,45.51526 137.10137,53.075692 
132.85897,59.414792 M 254.11016,49.469901 L 254.11016,49.469901 L 
254.11016,20.333129 C 259.21243,21.031083 261.67755,22.048838 
261.50562,23.386383 C 261.44823,23.909869 261.04699,24.346044 
260.30172,24.694917 C 260.30164,24.694986 260.30164,24.694986 260.30172,24.694
 917 L 260.30172,24.694917 L 259.78578,24.956624 L 259.78578,49.469901 L 
277.15652,49.469901 L 277.15652,55.227471 L 259.78578,55.227471 L 
259.78578,93.785712 L 281.45616,93.785712 L 281.45616,99.63051 L 
232.35378,99.63051 L 232.35378,93.785712 L 254.11016,93.785712 L 
254.11016,55.227471 L 236.22346,55.227471 L 236.22346,49.469901 L 
254.11016,49.469901 M 225.5603,59.327554 C 231.12111,63.107798 
235.62145,67.876693 239.06127,73.634235 L 234.76157,77.647079 C 
231.60845,72.180322 227.82475,67.934848 223.41044,64.910648 L 
223.41044,102.94548 L 217.73484,102.94548 L 217.73484,67.44049 C 
212.91919,71.627831 207.70222,75.030021 202.084,77.647079 L 197.87027,73.198053 
C 212.66118,66.917101 224.01239,55.372897 231.92377,38.565417 L 
205.35172,38.565417 L 205.35172,32.720615 L 217.99283,32.720615 L 
217.99283,20.333129 C 223.03774,21.031083 225.50291,22.048838 
225.38829,23.386383 C 225.33089,24.026183 224.75765,24.549594 
223.66837,24.956624 L 223.66837,32.720615 L 236.22346,32.720615 L 238.80326
 ,36.733469 C 235.13421,45.51526 230.71987,53.046611 225.5603,59.327554"
+       id="text4761" />
+    <path
+       
style="font-size:44.09793472px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;text-align:start;line-height:125%;writing-mode:lr-tb;text-anchor:start;fill:#b41717;fill-opacity:1;stroke:#7f2828;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;font-family:Candara;stroke-miterlimit:4;stroke-dasharray:none"
+       d="M 149.14708,37.774469 C 148.97807,41.117899 148.84526,44.824225 
148.74871,48.893456 C 148.67626,52.962754 148.3818,70.641328 
148.38184,75.524422 C 148.3818,79.065795 148.05588,81.991266 
147.40406,84.300835 C 146.75219,86.610422 145.72612,88.557071 
144.32585,90.140779 C 142.94969,91.724494 141.17522,92.901283 
139.00239,93.671139 C 136.82953,94.440996 134.22211,94.825935 
131.18014,94.825935 C 128.83828,94.825935 126.73787,94.59498 
124.87889,94.133049 L 125.4221,89.31593 C 127.13623,90.0418 128.92278,90.404734 
130.78177,90.404733 C 132.85805,90.404734 134.66875,90.140782 
136.2139,89.612876 C 137.78315,89.062981 139.02651,88.216133 
139.94396,87.072335 C 140.8855,85.928548 141.54942,84.520804 141.93572,82.8491 
C 142.34613,81.177412 142.55134,78.988811 142.55136,76.283285 C 
142.55134,66.297119 142.62852,44.659257 142.26641,37.774469 L 
149.14708,37.774469 M 166.38498,80.732697 L 159.83024,80.732697 C 
160.16821,76.333498 160.33723,71.307412 160.33723,65.654424 C 160.33723,59.2976 
 159.91471,53.963567 159.06973,49.652319 L 166.31257,48.761483 C 
166.02284,53.358679 165.87799,58.98965 165.87799,65.654424 C 
165.87799,70.933479 166.04699,75.959565 166.38498,80.732697 M 
167.90601,39.490159 C 167.90598,40.611994 167.5076,41.590815 166.7109,42.42662 
C 165.91418,43.240515 164.79155,43.647442 163.343,43.647399 C 
162.11172,43.647442 161.146,43.295504 160.44588,42.591595 C 159.76988,41.865769 
159.43188,40.996927 159.43188,39.98507 C 159.43188,38.885304 
159.84231,37.928485 160.66315,37.114591 C 161.48399,36.30078 
162.61869,35.893853 164.06727,35.893811 C 165.25023,35.893853 
166.17975,36.256783 166.85575,36.982609 C 167.55588,37.686526 
167.90598,38.522373 167.90601,39.490159 M 206.72748,80.732697 L 
200.13651,80.732697 C 200.66763,74.947749 200.93319,68.634899 
200.9332,61.794122 C 200.93319,58.406756 200.1727,56.097177 198.65174,54.865371 
C 197.15487,53.61163 195.00619,52.984747 192.20564,52.984714 C 
188.77731,52.984747 185.61465,54.117535 182.71753,56.383099 C 182.71753,63
 .883761 182.76583,72.000287 182.86238,80.732697 L 176.27142,80.732697 C 
176.68182,73.254058 176.88707,67.843042 176.88707,64.499632 C 
176.88707,59.352589 176.3559,54.359493 175.29363,49.520339 L 
181.66734,48.695493 L 182.35539,52.720761 L 182.64511,52.720761 C 
186.21823,49.773323 190.04483,48.299592 194.12499,48.299567 C 
198.13265,48.299592 201.23499,49.113454 203.43201,50.741118 C 
205.62895,52.346863 206.72747,55.217334 206.72748,59.352563 C 
206.72747,59.770507 206.70331,60.595362 206.65507,61.827118 C 
206.60675,63.058915 206.5826,63.883761 206.58262,64.30167 C 206.5826,67.975018 
206.63088,73.452022 206.72748,80.732697 M 222.69791,48.695493 C 
222.28747,55.514282 222.08225,62.355041 222.08225,69.21778 C 
222.08225,71.043461 222.14262,73.463019 222.26332,76.476468 C 
222.40822,79.467925 222.4806,81.502559 222.48063,82.580363 C 222.4806,89.685068 
219.51105,93.996287 213.57195,95.514024 L 211.76124,93.006484 C 
213.90995,91.356766 215.2378,89.597085 215.74478,87.727431 C 216.49321,85.0439
 12 216.86743,79.324953 216.86743,70.570535 C 216.86743,61.178248 
216.3846,54.16153 215.41887,49.520339 L 222.69791,48.695493 M 
224.2551,39.490159 C 224.2551,40.611994 223.85673,41.590815 223.06006,42.42662 
C 222.26332,43.240515 221.14069,43.647442 219.69213,43.647399 C 
218.46084,43.647442 217.49515,43.295504 216.795,42.591595 C 216.119,41.865769 
215.781,40.996927 215.781,39.98507 C 215.781,38.885304 216.19144,37.928485 
217.01231,37.114591 C 217.83316,36.30078 218.96785,35.893853 220.4164,35.893811 
C 221.5994,35.893853 222.52889,36.256783 223.20492,36.982609 C 
223.90503,37.686526 224.2551,38.522373 224.2551,39.490159 M 259.60008,80.732697 
L 253.91446,80.930661 C 253.62473,79.852857 253.47987,78.830045 
253.4799,77.862216 L 253.11774,77.862216 C 250.14817,80.325772 
246.10427,81.557546 240.98606,81.557547 C 238.20962,81.557546 
235.8195,80.820682 233.81563,79.346948 C 231.81178,77.851221 
230.80988,75.728607 230.80988,72.979099 C 230.80988,69.591724 
232.37914,66.875216 235.51769,64.829574
  C 238.65625,62.761967 244.48667,61.67316 253.00913,61.563165 C 
253.08155,61.035275 253.11772,60.430386 253.11774,59.748497 C 
253.11772,57.043003 252.32104,55.239336 250.72765,54.337474 C 
249.15832,53.435661 246.76819,52.984747 243.55721,52.984714 C 
239.76681,52.984747 236.03678,53.413668 232.3671,54.271484 L 232.9827,49.718301 
C 236.60411,48.77251 240.76873,48.299592 245.47658,48.299567 C 
249.77395,48.299592 253.09359,49.113454 255.43545,50.741118 C 
257.77728,52.346863 258.94819,55.096363 258.94824,58.989625 C 
258.94819,60.023469 258.88785,61.904117 258.76715,64.631608 C 
258.67054,67.337133 258.62228,69.140806 258.6223,70.042632 C 
258.62228,74.045913 258.94819,77.609265 259.60008,80.732697 M 
253.19019,74.331856 C 253.06945,70.988469 253.00909,67.986016 
253.00913,65.324484 C 248.47027,65.324498 245.01786,65.632443 
242.65187,66.248318 C 238.69248,67.348131 236.71278,69.448748 
236.71278,72.550177 C 236.71278,75.541643 239.03044,77.037371 
243.66588,77.037366 C 247.64942,77.037371 250.8
 2416,76.135534 253.19019,74.331856"
+       id="text3736"
+       
sodipodi:nodetypes="ccsscssccscccsccccsccsccsscsssccccscscccsccccscsssccscscccscccsscsssccccccscsccscsccscscsccccssc"
 />
+    <path
+       
style="fill:url(#radialGradient6564);fill-opacity:1.0;fill-rule:evenodd;stroke:#323232;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
+       d="M 105.45673,18.675923 C 105.45673,18.675923 88.211949,26.918461 
74.172834,28.737898 C 60.133727,30.557333 33.360434,32.377571 
28.045622,31.093256 C 22.730818,29.808941 18.915645,28.309196 
18.915645,28.309196 L 20.021441,32.056583 L 16.609513,35.052471 L 
17.2144,36.121726 L 18.61792,36.22764 L 22.92773,36.762252 L 
23.532621,38.688909 L 25.937975,38.905784 L 27.143021,42.970927 C 
27.143021,42.970927 32.254764,43.399628 33.758953,43.399628 C 
35.263142,43.399628 38.271966,43.187802 38.271966,43.187802 L 
38.371202,44.791657 L 39.477002,45.003495 L 39.477002,46.824227 L 
37.066917,48.967759 L 37.671807,49.073671 L 37.671807,49.820127 C 
37.671807,49.820127 32.255457,50.252157 30.049301,49.93109 C 
27.843157,49.610006 27.440747,49.608286 27.440747,49.608286 L 
27.242258,49.820127 L 27.143021,50.783455 L 27.643946,50.783455 L 
27.84242,54.959544 L 38.976091,54.530844 L 38.172728,68.980747 L 
38.073481,70.796442 L 28.645781,70.261816 L 28.546544,66.408513 L 
30.649462,66.408513 L 30.85267
 3,64.910557 L 32.757107,64.481857 L 33.059555,64.058192 L 25.937975,62.343374 
L 20.522364,63.947229 L 21.42496,64.698732 L 22.327572,64.698732 L 
22.426809,65.984848 L 24.331254,66.09076 L 24.331254,69.838147 L 
22.228335,70.372777 L 22.630009,71.225146 L 23.130934,71.547931 L 
23.130934,74.437917 L 24.435218,74.437917 L 24.435218,87.813529 L 
22.327572,88.13632 L 22.630009,91.989617 L 23.929569,92.206492 L 
23.731093,100.98236 L 29.449141,101.08826 L 28.244105,92.418334 L 
36.868446,92.206492 L 36.268285,96.912181 L 35.464925,100.23086 L 
44.188501,100.33677 L 44.287739,91.777793 L 50.303506,91.243181 L 
50.005786,96.700351 L 49.802585,99.90807 L 54.920484,99.90807 L 
54.717274,91.132217 L 55.421397,91.243181 L 55.619882,87.067076 L 
54.816521,87.067076 L 54.518798,85.352258 L 54.017874,80.429702 L 
54.216359,74.760706 L 55.31743,74.760706 L 55.31743,71.336105 L 
53.913913,71.442015 L 54.117112,67.402096 L 55.747469,67.240708 L 
55.823083,65.929374 L 56.749319,65.793192 L 57.699176,65.071956 L 
 51.985842,63.896802 L 46.31977,65.15265 L 46.872668,66.060507 L 
47.47283,66.010066 L 48.172228,65.984848 L 48.299828,67.639144 L 
49.878196,67.563497 L 49.906548,71.144447 L 43.111042,70.988097 L 
43.337879,67.160002 L 43.559978,63.679927 L 43.559978,59.105378 L 
43.763188,54.288748 L 57.373101,53.592733 L 73.567955,52.659674 L 
73.71917,55.736265 L 73.142647,63.120082 L 72.892183,69.9945 L 
66.928387,69.888585 L 66.900039,65.071956 L 69.106918,64.991267 L 
69.206169,63.629486 L 70.108765,63.493308 L 70.061506,63.226006 L 
70.964116,63.175568 L 71.465028,62.504773 L 64.721507,60.926122 L 
58.001612,62.368592 L 58.4789,63.200785 L 59.230285,63.1453 L 
59.230285,63.523577 L 60.156518,63.523577 L 60.156518,65.046738 L 
62.136575,65.071956 L 62.112937,69.298485 L 60.109259,69.298485 L 
60.080907,70.261816 L 60.785031,70.342507 L 60.70942,74.009202 L 
62.188552,74.089909 L 62.013701,88.620507 L 60.057282,89.018952 L 
60.080907,89.714967 L 60.761406,89.714967 L 60.761406,93.437137 L 
61.886113,93.43713
 7 L 61.588391,98.52109 L 61.210343,102.95945 L 68.331912,103.14605 L 
68.105084,99.29275 L 67.580538,96.085028 L 67.476575,93.300955 L 
73.520696,93.195041 L 73.345845,97.502272 L 73.317494,102.05159 L 
76.729426,102.3189 L 81.3653,102.1323 L 82.820807,101.70358 L 
82.017437,99.26753 L 81.818959,95.439438 L 81.440912,92.710853 L 
87.206218,92.499027 L 86.955759,95.842931 L 86.932133,101.08826 L 
89.238253,101.30009 L 91.520751,101.24965 L 92.621828,100.90165 L 
91.969693,95.923633 L 91.747577,92.176239 L 92.725793,92.070324 L 
92.749427,88.726422 L 93.02352,88.670945 L 92.976244,87.949712 L 
91.846823,87.949712 L 91.619996,85.488427 L 91.520751,74.811143 L 
92.371377,74.785924 L 92.371377,71.280616 L 92.725793,71.336105 L 
92.725793,70.640088 L 91.468773,70.529127 L 91.497126,66.463987 L 
93.600043,66.277382 L 93.477182,64.910557 L 94.403419,64.829863 L 
94.351424,64.562549 L 95.580099,63.947229 L 89.337489,62.69138 L 
82.995657,63.977495 L 83.39733,64.723951 L 84.375543,64.643256 L 84.427528,64.
 966046 L 85.254515,64.966046 L 85.301775,66.569901 L 87.357445,66.544681 L 
87.532293,70.478688 L 80.264217,70.423216 L 79.413593,64.512124 L 
78.733106,61.380041 L 78.184923,55.761484 L 78.510996,52.473053 L 
92.999878,51.373557 L 93.047136,46.476221 L 93.774891,46.289613 L 
93.727651,45.543159 L 93.174743,45.220372 C 93.174629,45.220372 
85.252181,46.395266 82.745197,46.66284 C 82.0389,46.738209 82.09239,46.733258 
81.516524,46.79397 L 81.440912,45.886118 L 78.444837,44.317564 L 
78.482644,42.491786 L 79.512842,42.461518 L 79.588444,39.949808 C 
79.588444,39.949808 85.728225,39.546834 88.009582,39.0117 C 90.290937,38.476559 
93.524432,37.942456 93.524432,37.942456 L 95.055545,33.79662 L 
98.089437,32.913987 L 98.339888,32.217972 L 105.20628,30.316548 L 
105.98602,29.676006 L 103.37744,23.976741 L 103.62792,22.690624 L 
104.95584,21.994611 L 105.91041,19.079404 L 105.45673,18.675923 z M 
72.466874,40.403728 L 72.429067,42.476654 L 73.983813,42.542211 L 
73.884576,44.509221 L 70.836515,46.506487 
 L 70.647496,47.081457 L 71.876167,47.091543 L 71.866712,47.575729 L 
62.552432,48.029652 L 62.613863,46.652742 L 63.039175,45.966809 L 
63.067524,45.528025 L 63.07698,44.579832 L 63.341609,43.949374 L 
63.440849,43.439982 L 63.440849,43.076841 L 63.842533,41.47297 L 
72.466874,40.403728 z M 52.987688,42.168984 L 52.760853,43.561027 L 
53.488599,44.418431 L 53.441349,45.916386 L 54.117112,46.960408 L 
53.942262,48.191039 L 54.443185,48.912273 L 44.939872,49.2855 L 
44.916247,48.967759 L 46.017333,48.831579 L 46.069307,48.428097 L 
43.66394,47.121797 L 43.536351,45.03375 L 44.689411,44.978276 L 
44.788661,42.72883 L 52.987688,42.168984 z M 67.051262,74.276518 L 
72.81657,74.649742 L 72.618099,82.411833 L 73.36947,88.776857 L 
67.254465,88.565018 L 67.051262,74.276518 z M 28.44258,74.599304 L 
37.671807,75.078442 L 36.868446,80.429702 L 36.868446,84.928593 L 
37.520583,87.440302 L 28.494569,87.869006 L 28.44258,74.599304 z M 
87.508658,74.649742 L 87.508658,87.924488 L 81.644113,88.353194 L 81.44091
 2,81.342592 L 80.788764,74.811143 L 87.508658,74.649742 z M 
43.087416,74.947312 L 49.906548,74.972531 L 49.977434,87.278902 L 
43.611966,87.389863 L 43.285891,83.400379 L 43.262266,79.441156 L 
43.087416,74.947312 z "
+       id="path4735" />
+  </g>
+</svg>

http://git-wip-us.apache.org/repos/asf/ambari/blob/658360a5/ambari-common/src/main/python/ambari_jinja2/custom_fixers/fix_alt_unicode.py
----------------------------------------------------------------------
diff --git 
a/ambari-common/src/main/python/ambari_jinja2/custom_fixers/fix_alt_unicode.py 
b/ambari-common/src/main/python/ambari_jinja2/custom_fixers/fix_alt_unicode.py
new file mode 100644
index 0000000..96a81c1
--- /dev/null
+++ 
b/ambari-common/src/main/python/ambari_jinja2/custom_fixers/fix_alt_unicode.py
@@ -0,0 +1,13 @@
+from lib2to3 import fixer_base
+from lib2to3.fixer_util import Name, BlankLine
+
+
+class FixAltUnicode(fixer_base.BaseFix):
+    PATTERN = """
+    func=funcdef< 'def' name='__unicode__'
+                  parameters< '(' NAME ')' > any+ >
+    """
+
+    def transform(self, node, results):
+        name = results['name']
+        name.replace(Name('__str__', prefix=name.prefix))

Reply via email to