Log in
appy.pod Writing ODT templates The « for » statement

Already introduced in the main section about statements, the for statement deserves more attention.

Recall that its general syntax is the following.

do <document_part> for <variable_name> in <python_expression>

The statement repeats the <document_part> as many times as there are elements in the iterable data structure specified in <python_expression>.

Any iterable data structure is welcome

The <python_expression> must evaluate to any Python iterable data structure. Basically, it means that any data structure you may use in a standard Python for-loop may also be used in a POD for statement. If, when evaluating a for statement, POD detects that the evaluated expression does not produce an iterable data structure, an error will be dumped in the ODT result.

Unwrapping several variables at once

As for the standard Python for-loop, the POD for statement is able to unwrap several variables at once. Consequenly, accept my apologies, because the syntax introduced so far for the pod statement was simplified. The complete syntax is the following.

do <document_part> for <variable_name>[,<variable_name>] in <python_expression>

Suppose you have the following function in your POD context:

def elements():
    return [('a', 1), ('b', 2)]

You can write a POD for statement like the following:

do text for key,count in elements()

The « loop » object

When a for statement is encountered, an internal object is created to store information about the currently walked loop.

For example, while looping over table rows via statement:

do row for element in someFunction()

, if you want to know, within the loop, if you are at the last row, you may use, in any POD statement or expression, the following Python expression:

loop.element.last

Within every loop, an object is added to the loop object, available at the attribute whose name is the name of the iterator variable created by the for statement. Because several inner loops can of course be currently walked, there may be as many attributes on the loop object as there are nested loops. On this sub-object, precomputed attributes are available, like the boolean "last". The complete list of precomputed attributes is described below: it comes from comments present in a method called initialiseLoop on class For, responsible for managing a for statement, in appy/pod/actions.py.

    def initialiseLoop(self, context, elems):
        '''Initialises information about the loop, before entering into it. It
           is possible that this loop overrides an outer loop whose iterator
           has the same name. This method returns a tuple
           (loop, outerOverriddenLoop).'''

        # The "loop" object, made available in the POD context, contains info
        # about all currently walked loops. For every walked loop, a specific
        # object, accessible at getattr(loop, self.iters[0]), stores info
        # about its status:
        # ----------------------------------------------------------------------
        #  length   | the total number of walked elements within the loop
        # ----------------------------------------------------------------------
        #  nb       | the index (starting at 0) of the currently walked element
        # ----------------------------------------------------------------------
        #  first    | True if the currently walked element is the first one
        # ----------------------------------------------------------------------
        #  last     | True if the currently walked element is the last one
        # ----------------------------------------------------------------------
        #  odd      | True if the currently walked element is odd
        # ----------------------------------------------------------------------
        #  even     | True if the currently walked element is even
        # ----------------------------------------------------------------------
        #  previous | Points to the previous element, if any
        # ----------------------------------------------------------------------
        # For example, if you have a "for" statement like this:
        #
        #                 for elem in myListOfElements
        #
        # Within the part of the ODT document impacted by this statement, you
        # may access to loop.elem.length to know the total length of
        # myListOfElements, or loop.elem.nb to know the index of the current
        # elem within myListOfElements.

You may ask: what attribute is added to the loop object if the for statement unwraps several variables ? The answer it: the first of the series of variables. This is the meaning of expression self.iters[0] in the previous comment. If we take back this example:

do text for key,count in elements()

, it will create attribute loop.key.

Hiding variables

The docstring from method initialiseLoop presented above was not supposed to be part of this web page. Nevertheless, it is interesting in the sense that it introduces a problem of potential name clash. Indeed, what happens if a for statement defines an iterator variable whose name is already present in the context? The docstring discusses this problem only between iterator variables of inner / outer loops, but the problem is wider and concerns a potential clash with any name already present in the context, be it added by a for statement, a with statement, or be it present in the global context or in the sub-context of a sub-POD. This problem is similar to the problem of nested scopes in programming languages; POD's answer is similar, too. In the inner scope defined by the for statement, the local name hides the outer name, if present in the context, and denotes the local variable. When going back from the for statement, the global hidden variable is restored and available again in the remaining of the POD template. This variable hiding mechanism, that also holds for variables defined via with statements, works at any nesting level within a POD template. Indeed, such template may contain a series of nested for and/or with statements.