Search code examples
pythonmako

Reused variable in Mako template cause "UnboundLocalError: local variable 'xyz' referenced before assignment"


I have this "funny" issue. I know this error message is found in a lot of places, but I couldn't find one explicitely related to Mako.

In a Mako template, I have (snippet):

<%inherit file="other.mako"/>
<%def name="my_method()">Search for ${label}</%def>
[...]
<h2>${label.capitalize()} found: ${len(l)}</h2>
...
<ol>
% for (label, field_name) in some_list:
    <li>${label}: ${field_name}</li>
% endfor
</ol>

and I would get the error:

UnboundLocalError: local variable 'label' referenced before assignment

The weird part is that if I just don't use the second ${label.capitalize()}, I don't get any error, and the value of ${label} in the <%def> is the one I want, not the one from the for-loop. If I had the same error with the variable in <%def>, it may be clear to not reuse the same variable name, but in this case, I'm quite puzzled that such thing happens.

Can anyone tell me how I can avoid this beside renaming the variable label in the for-loop? If I rename the for-loop variable name, the problem disappear. I am transferring from another templating system that did not have this sort of error, so this similar scenario happens quite often.

Thanks for any pointers,

D.

EDIT for clarity:

I am calling my template using:

renderers.render_to_response(my_mako_tmpl,
         {'label': 'value', 'somelist':[a,b,c], 'l':[]},
         request=request)

My question is: Why the fact that I have the % for (label, field_name)-loop, the variable label is giving me and error in ${label.capitalize()}, where as it does not give me any error for Search for ${label}.

If I change my for-loop for:

% for (label_name, field_name) in some_list:

I get no error.

If I don't change the for-loop, but I change:

<h2>${label.capitalize()} found: ${len(l)}</h2>

to

<h2>Items found: ${len(l)}</h2>

I get no error even if ${label} is used in my <%def>.

Also, to add info for the template usage, I added the <%inherit> and here is how the other.mako is defined (snippet):

<%def name="my_method()">Default value</%def>
Value of my_method() = ${self.my_method()}

So I don't need to pass any value to my_method().

Hope this make the question more clear.


Solution

  • It is best to think of a Mako template as being similar to a piece of python code. Thus, as in python, you will have issues if you expect a variable to take on both local scope and global (or nested scope).

    Thinking of a template as a function that takes variables, and defines within it various other functions including a body() function, then your problem is effectively similar to the following

    >>> def f(x):
    ...     def body():
    ...         print x
    ...         for x in range(5):
    ...             print x
    ...     body()
    ... 
    >>> f(1)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 6, in f
      File "<stdin>", line 3, in body
    UnboundLocalError: local variable 'x' referenced before assignment
    

    You shouldn't expect this to work in Python, and similarly, you shouldn't expect it to work in a Mako template.