Here's some code from Richard Jones' Blog:
with gui.vertical:
text = gui.label('hello!')
items = gui.selection(['one', 'two', 'three'])
with gui.button('click me!'):
def on_click():
text.value = items.value
text.foreground = red
My question is: how the heck did he do this? How can the context manager access the scope inside the with block? Here's a basic template for trying to figure this out:
from __future__ import with_statement
class button(object):
def __enter__(self):
#do some setup
pass
def __exit__(self, exc_type, exc_value, traceback):
#XXX: how can we find the testing() function?
pass
with button():
def testing():
pass
Here's one way:
Edit: stretched code a bit more, added some explanation...:
Catching caller's locals at
__exit__
is easy -- trickier is avoiding those locals that were already defined before thewith
block, which is why I added to main two local functions that thewith
should ignore. I'm not 100% happy with this solution, which looks a bit complicated, but I couldn't get equality testing correct with either==
oris
, so I resorted to this rather complicated approach.I've also added a loop (to make more strongly sure the
def
s before / within / after are being properly handled) and a type-check and function-call to make sure the right incarnation oftesting
is the one that's identified (everything seems to work fine) -- of course the code as written only works if thedef
inside thewith
is for a function callable without arguments, it's not hard to get the signature withinspect
to ward against that (but since I'm doing the call only for the purpose of checking that the right function objects are identified, I didn't bother about this last refinement;-).To answer your question, yes, it's frame introspection.
But the syntax I would create to do the same thing is
Here I would implement
gui.button
as a decorator that returns button instance given some parameters and events (though it appears to me now thatbutton = gui.button('click me!', mybutton_onclick
is fine as well).I would also leave
gui.vertical
as it is since it can be implemented without introspection. I'm not sure about its implementation, but it may involve settinggui.direction = gui.VERTICAL
so thatgui.label()
and others use it in computing their coordinates.Now when I look at this, I think I'd try the syntax:
(the idea being that similarly to how label is made out of text, a button is made out of text and function)