2008
09.22

Python decorators is just syntactic sugar to something that has been possible to do in python since version 1.0, for more background/technical information on decorators check out this pep.

The first and most important thing to remember about decorators is this: Whatever that is returned from the decorator function will replace the original function you’re decorating. Let’s start with the simplest of all decorators:

def foo(func):
	print "Decorating %s" % func.__name__
	return func

@foo	# foo is *not* called
def bar():
	print "bar"

bar()

This decorator does very little, it takes the function we’re decorating as it’s first argument, prints the name of it and then returns it, and because it returns the original function *nothing* happens except that the message Decorating bar is printed when we run the code above and when calling bar() at the last line we will get bar printed also.

As the one comment in the code above says note that foo is *not* called, the @ operator means “pass this function as the decorator for the function below” so if we only pass our “foo” function in, the calling of it is handled internally in python.

Lets step it up a notch and replace the original function, here’s the code:

def foo(func):
	def inner():
		print "Bar is gone"
	return inner

@foo # Not called
def bar():
	print "bar"

bar()

Our foo-function is a bit more advanced here since it contains an inner function, so what happens here? @foo still tells python that foo should be invoked as the decorator for bar, which it is – but this time foo returns the function called “inner” instead of the original function (which still is passed as the “func”-argument to foo, we just don’t do anything with it), so when we try to call bar on the last line we will get Bar is gone instead. This is because what I said earlier: Whatever is *returned* from the decorator will take the original functions place, so our bar() function is actually gone now and inner() has taken it’s place.

I guess most of you have seen decorators that take arguments, such as:

@decorator("arg1", "arg2")
def some_function();
	pass

These “decorators” are actually normal functions that you call with arguments, that returns decorators that are then called by python internally, here’s a real example:

def foo(string):
	def decorator(func):
		def replacement(times=1):
			print string*times
		return replacement
	return decorator

@foo("hi ho ") # Called
def bar():
	print "bar"

bar()
bar(5)

So when we call foo(”hi ho “) here, the foo function is *actually* called here and returns a decorator that takes the original decorated function as its only argument, this decorator returns a function called “replacement” that takes one argument with a default value of 1. The replacement function that is returned from the decorator replaces the original bar() function, so when we call it the first time we get
hi ho and when we call it the second time with 5 as the argument, we will get: hi ho hi ho hi ho hi ho hi ho .

The important thing to notice in the last example here is that foo is NOT a decorator here, it’s a function that takes one argument (in this case “hi ho “) and returns a decorator (aptly named “decorator” here).

2008
09.20

I just installed mod_wsgi on my windows (yeah yeah, I know…) laptop, it turned out to be amazingly easy but I figured I’d put up notes on how to do it here anyways.

  • Head over to http://adal.chiriliuc.com/mod_wsgi/ and fetch the latest (at the time of this writing it was revision_1018_2.3) apache module for your combination of python/apache
    • mod_wsgi_py24_apache20 is for Python 2.4 and Apache 2.0
    • mod_wsgi_py24_apache22 is for Python 2.4 and Apache 2.2
    • mod_wsgi_py25_apache20 is for Python 2.5 and Apache 2.0
    • mod_wsgi_py25_apache22 is for Python 2.5 and Apache 2.2
  • Save the mod_wsgi.so file into your apache modules directory, for me it was located in C:\Program Files\Apache Software Foundation\Apache2.2\modules
  • Create a directory somewhere outside of your webroot that will host your mod_wsgi-application, for simplicity’s sake I choose C:\wsgi
  • Locate your httpd.conf-file and open it up in a text editor, it’s usually located in the conf-directory of your apache installation, for me it was: C:\Program Files\Apache Software Foundation\Apache2.2\conf\httpd.conf
  • Do a search for “LoadModule” and you’ll get a block of LoadModule-statements, put this on its own line there: LoadModule wsgi_module modules/mod_wsgi.so so it looks something like this:
    #LoadModule usertrack_module modules/mod_usertrack.so
    LoadModule version_module modules/mod_version.so
    #LoadModule vhost_alias_module modules/mod_vhost_alias.so
    LoadModule wsgi_module modules/mod_wsgi.so
  • Now find the <Directory> block for your web-root, for me it was: <Directory “C:/Program Files/Apache Software Foundation/Apache2.2/htdocs”>, under it add this block:
    WSGIScriptAlias /wsgi "C:/wsgi/handler.py"
    
    <Directory "C:/wsgi">
            AllowOverride None
            Options None
            Order deny,allow
            Allow from all
    </Directory>

    Notice that the two bold sections point at where I created my application directory so if you didn’t choose C:\wsgi you need to change this to reflect whatever directory you chose, the /wsgi part of the just before the first bold statement is the apache alias we want to use for our wsgi application – which in this case will be /wsgi also. Also note the use of forward instead of backwards slashes even when on windows.

  • The observant reader might’ve noticed the /handler.py at the end of the first bolded statement in the bullet point above, this is our handler script / entrypoint into our application. So add this code to handler.py and put it in C:\wsgi

    def application(environ, start_response):
        status = '200 OK'
        output = 'Hello World!'
    
        response_headers = [('Content-type', 'text/plain'),
                            ('Content-Length', str(len(output)))]
        start_response(status, response_headers)
    
        return [output]
    
  • Now just restart apache and go to http://localhost/wsgi and enjoy the beautiful world of python web development
2008
09.01

I’m sorry, I lied to you guys – this part will not contain a real world example because I felt there were to many new concepts introduced. In this part we’ll go through something called asynchronous programming and how to create a scheduler to keep track of all the tasks we’re performing.

We’ll start of with the Task-base class, it wraps a generator exposing it’s own .next()-method and a .suspended()-method that is used by our scheduler to decide if we should run the task or not. A task can basically be anything, but more often then not it’s some type of operation that involves a delay we can’t control when it’s complete – such as a network call, disk access or something similar.

The directory structure for all of these examples is the following:

/
	/demo
		__init__.py
		tasks.py
		scheduler.py
	example_N.py

demo/tasks.py:

class Task(object):
	def __init__(self, generator = None):
		self.generator = generator

	def suspended(self):
		return False

	def next(self):
		return self.generator.next()

No doubt a fairly simple class, three methods spanning just one line each. Either download the entire code for these examples or write it yourself and put it in the demo/tasks.py file.

Lets put together our scheduler, the scheduler isn’t a class – it’s just a module with two variables and two functions, it to is fairly simple and spans just about twenty five lines:

demo/scheduler.py:

queue = []

def add(task):
	global queue
	queue.append(task)

def run():
	global queue

	stack = queue
	queue = []

	while len(stack) > 0:
		task = stack.pop(0)

		try:
			if not task.suspended():
				task.next()
		except StopIteration:
			continue

		queue.append(task)

		if len(stack) == 0 and len(queue) > 0:
			stack = queue
			queue = []

One global list named queue, and two functions: add() that just appends an element on the queue and run() that runs through the queue until there are no more generators left in it. Let’s go through the run() function line by line:

  • stack = queue – We store the queue in a temporary local variable
  • queue = [] – Set queue to an empty list
  • task = stack.pop(0) – Pop the first element of the stack
  • if not task.suspended(): – If the task isn’t suspended continue
  • task.next() – Call .next() on the task which in turn forwards the call to the generator the task wraps
  • except StopIteration: – If we get a StopIteration exception from the executing task/generator we should just continue with the next one (this will push the task that was responsible for the exception out of the queue)
  • queue.append(task) – Add the task in question to the queue again
  • if len(stack) == 0 and len(queue) > 0: – If the stack is empty and the queue isn’t
  • stack = queue – Put the queue in the stack
  • queue = [] – And clear the queue

Let’s put this together in a simple example so we can see that our scheduler works as it should:

example_1.py

from demo import tasks, scheduler

def echo(word):
	while True:
		print word
		yield

scheduler.add(
	tasks.Task(
		echo("Hello")))

scheduler.add(
	tasks.Task(
		echo("World!")))

scheduler.run()

If we run this from the the terminal with python example_1.py we will get Hello and World! looped over our screen for eternity:

...
Hello
World!
Hello
World!
Hello
World!
Hello
World!
Hello
World!
...

Maybe not so exiting, but if you look closely you will see that Hello and World! alternate between each other, meaning that when the generator that prints Hello yields the first time the scheduler will take control passing execution to the generator printing World!, when that has printed out its message the stack is empty so the scheduler fills the stack with the queue again and beings from the top, and it all begins again.

Let’s add a new function called sleeper() to the mix, here you have example_2.py:

from time import sleep
from demo import tasks, scheduler

def echo(word):
	while True:
		print word
		yield

def sleeper(seconds):
	while True:
		sleep(seconds)
		yield

scheduler.add(
	tasks.Task(
		echo("Hello")))

scheduler.add(
	tasks.Task(
		sleeper(1)))

scheduler.add(
	tasks.Task(
		echo("World!")))

scheduler.add(
	tasks.Task(
		sleeper(1)))

scheduler.run()

You should be able to figure out what happens when we run this example from the terminal, Hello will print – and then yield and the scheduler passes execution to the first sleeper, pausing for one second and then passing it to World! printing that which yields and then execution gets passed to our second sleeper generator that pauses execution for one second, this is mainly so you can see that they actually take turn instead of some mindless spaming from an infinite repeating loop without pauses.

Let’s make use of that .suspended()-method on the Task class shall we? Let’s create a generator that gets called every second time the scheduler asks for it instead of every iteration, here’s the code – add it to the end of demo/tasks.py:

class EvenTask(Task):
	def __init__(self, *args, **kwargs):
		super(self.__class__, self).__init__(*args, **kwargs)
		self.counter = 0

	def suspended(self):
		self.counter += 1
		return self.counter % 2 != 0

If you don’t understand the super(self.__class__, self).__init__(*args, **kwargs)-line it’s how you call the parent classes .__init__()-method in Python, .suspended() increases the counter with +1 each time it’s called but only returns false when we’re at an even number, allowing the task to be executed every other time.

Here’s the next example code, example_3.py

from time import sleep
from demo import tasks, scheduler

def echo(word):
	while True:
		print word
		yield

def sleeper(seconds):
	while True:
		sleep(seconds)
		yield

scheduler.add(
	tasks.Task(
		echo("Hello")))

scheduler.add(
	tasks.Task(
		sleeper(1)))

scheduler.add(
	tasks.EvenTask(
		echo("World!")))

scheduler.add(
	tasks.Task(
		sleeper(1)))

scheduler.run()

It’s identical to example_2.py except that the echo(”World!”)-task now is of the type EvenTask instead of Task, if you run this code you will get Hello Hello World! printed, because the World!-task will skip every other time.

...
Hello
Hello
World!
Hello
Hello
World!
Hello
Hello
World!
...

So, I’ll take one last example before calling it for the day – if you remember the line “except StopIteration:” from the scheduler.run()-function we can use that to make a generator drop out of the scheduler’s run()-loop, i present to you the marvelous example_4.py:

from time import sleep
from demo import tasks, scheduler

def echo(word):
	while True:
		print word
		yield

def sleeper(seconds):
	while True:
		sleep(seconds)
		yield

def echo_once(word):
	while True:
		print word
		yield
		raise StopIteration

scheduler.add(
	tasks.Task(
		echo("Hello")))

scheduler.add(
	tasks.Task(
		echo_once("World!")))

scheduler.add(
	tasks.Task(
		sleeper(1)))

scheduler.run()

Running this from the terminal will yield (no pun intended) you something like this, only printing “World!” once:

Hello
World!
Hello
Hello
Hello
...

In the next part, number four I will show you a real world example with asynchronous network i/o, i just wanted to introduce the concept of a scheduler before jumping into a more advanced example.