Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pygtk: free variable referenced before assignment in enclosing scope

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?

like image 250
Claudiu Avatar asked Aug 07 '09 02:08

Claudiu


1 Answers

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;-).

like image 59
Alex Martelli Avatar answered Oct 13 '22 00:10

Alex Martelli