Python Decorators
Decorators in Python are just fantastic. Here are a few I've used in Postr. Update: the wonderful James Hensbridge expanded the decorators, so I've updated this post.
def threaded(f):
def wrapper(*args, **kwargs):
t = threading.Thread(target=f, args=args, kwargs=kwargs)
t.setDaemon(True)
t.start()
wrapper.__name__ = f.__name__
return wrapper
This decorator (stolen from O'Reilly) calls the method in a new thread, so execution returns straight away. I use this for long-running tasks that cannot be handled in an asynchronous manner (such as the photo uploading in Postr, which currently uses urllib). The main thread returns straight away so the interface doesn't block, and the uploads continue in the background.
Of course this new thread cannot just call GTK+ methods, as GTK+ itself isn't threadsafe. So, I have another decorator that causes a method to be always executed in the main loop by scheduling an idle handler that calls it.
def as_idle(f):
def wrapper(*args, **kwargs):
event = threading.Event()
ret = []
def task():
ret.append(f(*args, **kwargs))
event.set()
return False
gobject.idle_add(task)
event.wait()
return ret[0]
wrapper.__name__ = f.__name__
return wrapper
Erich Schubert requested return values, so James added those too. The calling function will block until the idle handler has called the decorated function. I've split the decorators out into a separate file in Postr, so you can view the latest version online.
Magic stuff!
1. make the wrapper function "def wrapper(args, *kwargs)", and pass the kwargs on to the original function.
2. "wrapper._name_ = f._name_" -- this way your methods won't all claim to be called wrapper.
Say... I have this long-running task in the background; when it's finished it will notify the user - e.g. via a button being ungreyed, a popup message, whatever - to decide on what to do next. Then it will go back to work.
The threadsafe decorator is nice for updating the UI, but I'd actually like it to block my long-running task until the user has made his decision. That way all my local variables etc. remain intact.
and javax.swing.SwingUtilities.invokeAndWait
would give ideas on how to do all the thread manipulation?
I suspect all this sort of stuff is a lot easier in languages with closures such as Ruby or Groovy.
func(1, 2, 3) and func(1) are both valid, they would result in arg = [1, 2, 3] and arg = [1], respectively.
**kwargs is similar but as a dictionary (called keyword arguments, kwargs for shot):
func({"one": 1, "two": 2})