Ejemplo n.º 1
0
 def test_it_raises_exception_in_correct_file(self):
     with create_templates([
         ('a.html', u'<py:include href="b.html"/>'),
         ('b.html', u'${1 / 0.0}'),
     ]) as d:
         loader = TemplateLoader([d])
         try:
             loader.load('a.html').render({})
         except ZeroDivisionError as e:
             assert 'b.html' in str(e)
         else:
             assert False
Ejemplo n.º 2
0
 def test_it_raises_interpolation_exception_at_right_lineno(self):
     for x in range(1, 5):
         with create_templates([
             ('a.html', u'<py:include href="b.html"/>'),
             ('b.html', (u'\n' * x) + '${a.b}'),
         ]) as d:
             loader = TemplateLoader([d])
             try:
                 loader.load('a.html').render({'a': object()})
             except AttributeError as e:
                 assert 'b.html", line {}'.format(x + 1) in str(e)
             else:
                 assert False
Ejemplo n.º 3
0
    def test_template_render_calls_can_be_interleaved(self):
        """
        Interleaving calls to templates can happen when one template calls
        another. Normally you'd do this with py:include, but sometimes it's
        useful to be able to render a sub template completely separately
        with its own context dict. Problem is the runtime.data.context
        has global scope, so the inner template stomps over the caller's
        context.

        NB it's tempting to pass __piglet_ctx around as a parameter to all
        template functions, removing the need for the threading.local entirely.
        However that entails either (A) requiring the user to add a
        __piglet_ctx parameter to every template function call or (B) requiring
        the user to use the <py:call> syntax so we can hook into the call and
        inject the parameter, or (C) manipulating the ast to find function
        calls and autoinject parameters.

        (A) is clunky (and I don't like exposing __piglet_ctx to the user)
        (B) is simple but adds an inconsistency, and it annoys me that the
        regular python function call syntax wouldn't available for template
        functions.
        (C) might be possible - I got as far as implementing this for functions
        defined in the same template, but gave up for functions imported using
        <py:import>.
        """
        with create_templates({
            'a': u'$x ${b()} $x',
            'b': u'$y$y$y',
        }) as d:
            loader = TemplateLoader([d])
            a = loader.load('a')
            b = loader.load('b')
            s = a.render({'x': 2, 'b': lambda: b.render({'y': 3})})
            assert s == '2 333 2'
Ejemplo n.º 4
0
 def test_it_evalutes_pyblock_replacement(self):
     with create_templates([
         ('a', u'<py:block name="page">A</py:block>'),
         ('b', (u'<py:extends href="a">'
                u'<py:block name="page">B</py:block></py:extends>'))
     ]) as d:
         loader = TemplateLoader([d])
         assert loader.load('b').render({}) == 'B'
Ejemplo n.º 5
0
 def test_dynamic_pyextends(self):
     with create_templates([
         ('foo', u'<p>foo</p>'),
         ('bar', u'<p>bar</p>'),
         ('main', u'<py:extends href="$t"/>')
     ]) as d:
         loader = TemplateLoader([d])
         assert loader.load('main').render({'t': 'foo'}) == '<p>foo</p>'
         assert loader.load('main').render({'t': 'bar'}) == '<p>bar</p>'
Ejemplo n.º 6
0
 def test_it_keeps_defs_inside_extends(self):
     with create_templates({
         'a': u'''<py:block name="a">A</py:block>''',
         'b': u'''<py:extends href="a">
             <py:def function="f">HELLO</py:def>
             <py:block name="a">${f()}</py:block></py:extends>''',
     }) as d:
         loader = TemplateLoader([d])
         s = loader.load('b').render({})
         assert normspace(s) == 'HELLO'
Ejemplo n.º 7
0
 def test_it_evaluates_intermediate_supers_once_only(self):
     with create_templates([
         ('a', (u'<py:block name="x">A</py:block>')),
         ('b', (u'<py:extends href="a">'
                u'<py:block name="x">B ${super()}</py:block>'
                u'</py:extends>')),
         ('c', (u'<py:extends href="b"></py:extends>')),
     ]) as d:
         loader = TemplateLoader([d])
         assert loader.load('c').render({}) == 'B A'
Ejemplo n.º 8
0
 def test_py_include_can_be_nested_in_py_def(self):
     with create_templates([
         ('a', (u'<py:def function="a">'
                u'<py:include href="b"/>'
                u'</py:def>'
                u'${a()}')),
         ('b', u'whoa nelly!')
     ]) as d:
         loader = TemplateLoader([d])
         assert loader.load('a').render({}) == 'whoa nelly!'
Ejemplo n.º 9
0
 def test_it_compiles_pyimport(self):
     """
     py:import is hoisted to module level, meaning the template + loader
     machinery must already be installed at compile time
     """
     with create_templates([
         ('a', u'a says <py:import href="b" alias="b"/>${b.hello()}'),
         ('b', u'<py:def function="hello">hello world!</py:def>')]
     ) as d:
         loader = TemplateLoader([d])
         assert loader.load('a').render({}) == 'a says hello world!'
Ejemplo n.º 10
0
 def test_it_calls_super_over_deep_hierarchy(self):
     with create_templates({
         'a': u'''<py:block name="a">A</py:block>''',
         'b': u'''<py:extends href="a">
             <py:block name="a">${super()} B</py:block></py:extends>''',
         'c': u'''<py:extends href="b">
             <py:block name="a">${super()} C</py:block></py:extends>''',
     }) as d:
         loader = TemplateLoader([d])
         s = loader.load('c').render({})
         assert normspace(s) == 'A B C'
Ejemplo n.º 11
0
 def test_it_replaces_blocks_over_deep_hierarchy3(self):
     with create_templates({
         'a': u'''A INTRO <py:block name="content"></py:block>''',
         'b': u'''<py:extends href="a"></py:extends>''',
         'c': u'''<py:extends href="b">
         <py:block name="content">C</py:block>
             </py:extends>
         ''',
     }) as d:
         loader = TemplateLoader([d])
         s = loader.load('c').render({})
         assert normspace(s) == 'A INTRO C'
Ejemplo n.º 12
0
 def test_it_evaluates_nested_pyblocks_with_super(self):
     with create_templates([
         ('a', (u'<py:block name="page">'
                u'Apage '
                u'<py:block name="heading">Ahead</py:block>'
                u'</py:block>')),
         ('b1', (u'<py:extends href="a">'
                 u'<py:block name="page">B</py:block>'
                 u'</py:extends>')),
         ('b2', (u'<py:extends href="a">'
                 u'<py:block name="heading">B ${super()}</py:block>'
                 u'</py:extends>')),
     ]) as d:
         loader = TemplateLoader([d])
         assert loader.load('b1').render({}) == 'B'
         assert loader.load('b2').render({}) == 'Apage B Ahead'
Ejemplo n.º 13
0
    def test_pywith_on_extends_tag(self):
        """
        Sometimes in a layout template you want to make an attribute
        customizable. Using py:block isn't possible
        (eg ``<body class='<py:block name="bodyclass"/>'/>`` is not well
        formed), so a workaround is::

            <!-- layout.html -->
            <html class="$htmlclass">
            ...
            </html>


            <!-- page.html -->
            <py:extends href="layout.html" with="htmlclass='page'">
            </py:extends>
        """
        with create_templates([
            ('a', u'<html class="$htmlclass"></html>'),
            ('b', u'<py:extends href="a" with="htmlclass=\'foo\'"/>')
        ]) as d:
            loader = TemplateLoader([d])
            assert loader.load('b').render({}) == '<html class="foo"></html>'