Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

repeat.item.number and repeat.item.index don't seem to work with recursion #177

Open
marcidy opened this issue Jul 15, 2014 · 10 comments
Open

Comments

@marcidy
Copy link

marcidy commented Jul 15, 2014

When using a recursive structure such as below, the repeat.item.number and index don't appear to behave in a sane way. I'm not sure why they decrement once, then stay the same for the current level. They do seem to reset on recursion, but the behavior of next and subsequent values isn't what I would expect.

  <body>
    <div class="step_list" metal:define-macro="thing_list">
      <div class="step" tal:repeat="item thing">
        Repeat Index: <span tal:content="repeat.item.index">test</span>
        Repeat Number: <span tal:content="repeat.item.number">test</span>
        <div tal:define="thing item.children">
          <div metal:use-macro="template.macros['thing_list']" />
        </div>
      </div>
    </div>
  </body>

repeat_numbers

@malthe
Copy link
Owner

malthe commented Jul 15, 2014

But you're redefining thing inside the repeat clause. I'm not sure exactly what you're trying to do?

@marcidy
Copy link
Author

marcidy commented Jul 15, 2014

Sorry it wasn't clear. For recursion purposes to display parents and children to arbitrary depth. And it's possible my expectations are off here.

For example a list of things where each thing has children:

class Thing():
    def __init__(self):
        self.children = None

def return_things():
    ''' create 2 parents, each with 3 children '''
    thing1 = Thing()
    thing2 = Thing()
    thing1.children = [Thing() for x in xrange(0,3)]
    thing2.children = [Thing() for x in xrange(0,3)]

    return [thing1, thing2]

The template displays Thing1 as Index 0. It then loads the macro "thing_list" on the list retrieved from Thing1.children in a recursive manner, by assigning 'thing' to 'item.children'. This works fine and generates the recursion to any depth based on any parent-child I send. The only "issue" is that the repeat.item.index and repeat.item.number don't behave as I'd expect.

If I were writing this as a function, it would be only in the scope of this function call. Perhaps I'm mistaken expecting scope rules to apply to tags like they apply to functions calls. But I'm not sure what the decrement means.

So I expected that the redefinition of the variable "thing" would only have scope in this area:

        <div tal:define="thing item.children">
          <div metal:use-macro="template.macros['thing_list']" />
        </div>

When running the template over the data, I'd expect to see:

Repeat Index: 0 Repeat Number: 1
    Repeat Index: 0 Repeat Number: 1
    Repeat Index: 1 Repeat Number: 2
    Repeat Index: 2 Repeat Number: 3
Repeat Index: 1 Repeat Number: 2
    Repeat Index: 0 Repeat Number: 1
    Repeat Index: 1 Repeat Number: 2
    Repeat Index: 2 Repeat Number: 3

and if I had a 3rd thing:

Repeat Index: 0 Repeat Number: 1
    Repeat Index: 0 Repeat Number: 1
    Repeat Index: 1 Repeat Number: 2
    Repeat Index: 2 Repeat Number: 3
Repeat Index: 1 Repeat Number: 2
    Repeat Index: 0 Repeat Number: 1
    Repeat Index: 1 Repeat Number: 2
    Repeat Index: 2 Repeat Number: 3
Repeat Index: 2 Repeat Number: 3
    Repeat Index: 0 Repeat Number: 1
    Repeat Index: 1 Repeat Number: 2
    Repeat Index: 2 Repeat Number: 3

@malthe
Copy link
Owner

malthe commented Jul 15, 2014

It is a little weird I must admit, especially the negative values.

@marcidy
Copy link
Author

marcidy commented Jul 15, 2014

The more I look at it, the more I think it's the scope. I'm guessing the repeat.item (as opposed to item itself) is being consumed or something and set to 0, then passed up to the calling scope, which checks it, and says "it's consumed". so while the item itself is scoped fine, the repeat.item is not?

Here is the output from a larger example. All the item's are properly nested and accessed recursively by the macro, but the leading number on each line is the repeat.item.number. HTH.

I can get the behavior I want a different way, but was suggested I bring this to your attention.

nested

please excuse the test comments, they weren't originally for public consumption

@malthe
Copy link
Owner

malthe commented Jul 15, 2014

I'll need to look at the generated source and step through to really
understand what this is about. I won't have time before next week though,
because I'm mostly offline this week.

On 15 July 2014 21:57, marcidy [email protected] wrote:

The more I look at it, the more I think it's the scope. the repeat.item
(as opposed to item itself) is being consumed or something and set to 0,
then passed up to the calling scope, which checks it, and says "it's
consumed". so while the item itself is scoped fine, the repeat.item is not?

Here is the output from a larger example. All the item's are properly
nested and accessed recursively by the macro, but the leading number on
each line is the repeat.item.number. HTH.

I can get the behavior I want a different way, but was suggested I bring
this to your attention.

[image: nested]
https://cloud.githubusercontent.com/assets/5845312/3590554/1c824a16-0c5a-11e4-9cc8-4cd2e4611590.png


Reply to this email directly or view it on GitHub
#177 (comment).

@marcidy
Copy link
Author

marcidy commented Jul 15, 2014

No worries, I'm going to use a work-around regardless. This is just a demo. Thanks for your attention.

@pgrunewald
Copy link

I'm experiencing the very same issue. I have posted an example here: https://community.plone.org/t/weird-behavior-in-tal-chameleon-with-nested-repeats-and-macros/453

One issue seems to be, that any previous state of the repeat variable does not get restored, after having finished a tal:repeat block/scope.

@marcidy I'm curious, what's your work-around for this?

@malthe
Copy link
Owner

malthe commented Nov 14, 2016

My work around for this issue – pending being fixed – is to use:

  tal:define="(i, item) enumerate(sequence)"

This gives you an explicit iterator index as a scoped variable.

But I think it's a nasty bug that should be fixed. I took a brief look and it's not clear how to best go about it because the repeat dictionary is very simple and not "scoped".

@malthe malthe added the core label Nov 14, 2016
@marcidy
Copy link
Author

marcidy commented Nov 14, 2016

I never posted my work-around (or actual solution?), sorry about that.

1<div class="step_list" metal:define-macro="step_list" >
2  <div tal:repeat="step steps">
3 < all other stuff  />
4 <div tal:define="steps step.children">
5   <div metal:use-macro="template.macros['step_list']" />
6 </div>

Forgive the abuse of notation below. "Step0" and "Step1" are both variables with the label "step".

1 define macro
2 start loop with steps = "steps0"
3 hit macro and set steps = step0.steps a.k.a "steps1"
4 run inside macro which exhausts steps aka "step1.steps"
5 exit macro and go back to line 2 and get next step from "steps0"

line 2 is a potential issue with this, the label "steps" is reassigned but the original list of steps still exists to be iterated after the macro exists. Not sure if this is a problem or a feature. Feature to me right now : )

Hope this can help in some way. Let me know if there's anything else I can do. Again, apologies for not posting this before, especially to @pgrunewald

@dataflake
Copy link
Collaborator

This appears to be a matter of local scopes not being cleaned up when the local scope is left. Consider this simplistic example with a list of lists. The tal:condition is used to stop processing at the inner list:

<div tal:define="seq python:[[1, 2, 3]]*4">
    <div metal:define-macro="nested_repeat">
      <div tal:condition="python: same_type(seq, [])"
           tal:repeat="item seq">
        Repeat Index: <span tal:content="repeat/item/index">test</span>
        Repeat Number: <span tal:content="repeat/item/number">test</span>
        <div tal:define="seq item">
          <div metal:use-macro="template/macros/nested_repeat" />
        </div>
      </div>
    </div>
</div>

The result:

Repeat Index: 0 Repeat Number: 1
Repeat Index: 0 Repeat Number: 1
Repeat Index: 1 Repeat Number: 2
Repeat Index: 2 Repeat Number: 3
Repeat Index: 2 Repeat Number: 3
Repeat Index: 0 Repeat Number: 1
Repeat Index: 1 Repeat Number: 2
Repeat Index: 2 Repeat Number: 3
Repeat Index: 2 Repeat Number: 3
Repeat Index: 0 Repeat Number: 1
Repeat Index: 1 Repeat Number: 2
Repeat Index: 2 Repeat Number: 3
Repeat Index: 2 Repeat Number: 3
Repeat Index: 0 Repeat Number: 1
Repeat Index: 1 Repeat Number: 2
Repeat Index: 2 Repeat Number: 3

The example shows that the inner repeat loop initializes the repeat variables correctly, but when the inner loop ends and the outer loop runs again those local values are not cleaned up correctly. The outer loop now shows the last repeat variable values that were set on the inner loop.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants