1. Michael Bayer
  2. mako
  3. Issues


Issue #168 new

variables implicitly passed to block cause unexpected error for def

created an issue

Try out the following 3-level inheritance setup, stripped down to show only the situation I want to point out:

Test setup:

  1. base.mako: <%block name="foo"/>
  1. middle.mako: <%inherit file="base.mako"/> <%block name="foo">foo</%block>
  1. final.mako: <%inherit file="middle.mako"/> <%block name="foo"> ${parent.foo()} </%block>
  1. test script: from mako.template import Template from mako.lookup import TemplateLookup mylookup = TemplateLookup(directories=['.'], module_directory='.') if name == 'main': mytemplate = mylookup.get_template('final.mako') print mytemplate.render({'c': 'foo'})

OK, that works fine. Actually it's doing a really great job of helping me "normalize" and modularize the templating for my site, helps keep it all D.R.Y., etc.

Now let's say a coworker who's unaware of Mako 0.4.1 and its handy new "block"s, who was working on our project a few months back when it was built on defs instead, goes to add another template at the level of final.mako, and uses the old syntax we had for the project. We forgot to tell him that our template inheritance API changed and that he needs to use a block where he used to use a def in this case. So as far as he knows we use defs for inheritance as we used to. Let's say he's lazy and hasn't re-read the middle and base templates lately, he just wants to inherit from them as he always did before. He creates the following:

  1. problem.mako <%inherit file="middle.mako"> <%def name="foo()"> ${parent.foo()} </%def>

Here's the point of this ticket. He gets this error:

TypeError: render_foo() got an unexpected keyword argument 'c'

That's not too far off; ideally he would reason that the source of the problem has something to do with something calling something with args when it doesn't expect them. But remember this guy has no idea he's on Mako 0.4.1 and that blocks exist or that middle.mako and base.mako have been converted to use blocks instead of defs. So he's not thinking about pageargs or lack thereof. Instead he goes on a wild goose chase asking the server guys why they're sending this variable 'c' in the context when they didn't used to. In fact they've always been sending in 'c', it just happens that 'c' is there for legacy reasons and has never been used in this project, so this guy never dealt with it before and he thinks its sudden appearance is the problem.

Ok, that's very made-up, but the point is, our hypothetical developer perhaps ought to get an error saying that a block is trying to call a def where a block is expected. Actually the current TypeError is accurate and appropriate, but maybe it should be augmented by a bit of suggestion text e.g. "make sure you're not unknowingly mixing defs and blocks."

Comments (3)

  1. Michael Bayer repo owner

    thanks for the effort here. The situation is not quite so complex, its just a plain bug when a block call resolves to a def.

    1. !python from mako.template import Template from mako.lookup import TemplateLookup

    l = TemplateLookup()

    l.put_string("base", """ <%block name="foo"> foo </%block> ${next.body()} """)

    l.put_string("final", """ <%inherit file="base" /> <%def name="foo()"> ${parent.foo()} </%def> """


    print l.get_template("final").render(c="foo")

  2. Michael Bayer repo owner

    maybe want to say:

    1. !python if 'parent' not in context._data or not hasattr(context._data['parent'], 'foo'): M_block = context['self'].foo assert M_block.is_block, "Not a block" M_block(pageargs)

    @runtime.block def render_foo(context,pageargs): context.caller_stack._push_frame() try: def foo(): return render_foo(context) M_writer = context.writer()

    1. SOURCE LINE 2 M_writer(u'\n foo\n') return '' finally: context.caller_stack._pop_frame()
  3. guest reporter

    Replying to [comment:1 zzzeek]:

    thanks for the effort here. The situation is not quite so complex, its just a plain bug when a block call resolves to a def.

    Yeah, I could have minimized it to just the one bad call. Not sure why I thought anyone would care how it came about or that our use case happened to have three levels. Thanks for taking this up.

  4. Log in to comment