views:

218

answers:

2

Very bizarre scoping error which I can't even see. Inside of an updater function, I have a nested helper function to... help w/ something:

    def attach_row(ws,r1,r2):
        es = []
        for i,w in enumerate(ws):
            eb = gtk.EventBox()
            a = gtk.Alignment(xalign=0.0,yalign=0.5)
            a.add(w)
            eb.add(a)
            eb.set_style(self.rowStyle.copy())
            es.append(eb)                
            self.table.attach(eb, i, i+1, r1, r2,
                              xoptions=gtk.EXPAND|gtk.FILL,
                              yoptions=gtk.SHRINK)

        def ene(_,ev):
            for eb in es:
                eb.set_state(gtk.STATE_PRELIGHT)
        def lne(_,ev):
            for eb in es:
                eb.set_state(gtk.STATE_NORMAL)
        for eb in es:                
            eb.connect('enter-notify-event', ene)
            eb.connect('leave-notify-event', lne)

This works once in a while, but if the update() function runs too much, I eventually get:

    for eb in es:
NameError: free variable 'es' referenced before assignment in enclosing scope

What is causing this? es is most certainly assigned before those functions ever get called. Isn't that right? Is some bizarre thing happening where for some reason the ene() for a previously created row gets called while the new one is being created, and the closed over es gets overwritten?

A: 

Don't have enough points to leave this as a comment (just registered) ...

  • No 'es' variable globally or in a higher scope?
  • attach_row isn't also a nested function?
  • NameError exception points to for loop line in ene or lne functions?

One possible, but icky, workaround might be to make ene and lne classes that are instantiated and callable as functions via a __call__() method.

+1  A: 

Pretty mysterious indeed -- looks like the closure's disappearing out from under the inner functions. Wonder if that's related to how pygtk holds such callback functions (I'm not familiar with its internals). To try to probe for that -- what happens if you also append ene and lne to a global list at the end of attach_row, just to make sure they're held "normally" somewhere so their closure survives -- does the problem persist in that case?

If it does, then I have to admit the problem's just TOO mysterious and concur with the previous answer suggesting, as a workaround, the use of callables that hold their state in a clearer way (I'd suggest two bound methods of one class instance, since they share their state, but two instance of a single class with __call__ and receiving the state to set and the list of event boxes in its __init__ is surely also reasonable -- having two separate classes IMHO would be a slight exaggeration;-).

Alex Martelli
hehe, true. I actually realized I have a saner way of doing this - instead of removing and attaching a lot of these rows, I just create one set of them and change the widgets inside them.A note, though: I tried passing "es" as user data to those ene and lne functions. What happend is I no longer got the NameError, but the widgets wouldn't highlight at all. SOmething was still being lost somewhere.If this comes up again I'll try the class idea.
Claudiu
even better way - use a VBox, put one event box per row, and then use a sizegroup to align the columns
Claudiu