Friday 30 November 2007

"@apply antipattern" or "Python doesn't need a with_statement!"

Remember the new funky open syntax?

with open("somefile","r") as f:
print f.readlines()
It looks very pretty and ensures that the file gets closed after usage. Neat. However, nothing stops us from writing a decorator that does similar job without importing from __future__:
def opened(*args):
def decorator(function):
def ret():
f = open(*args)
try:
function(f)
finally:
f.close()
return ret
return decorator
I know what it looks like. Even though, it makes us able to write simply:
@opened("file.txt", "r")
def do_it(f):
print f.readlines()
do_it()
or even:
@apply 
@opened("file.txt", "r")
def do_it(f):
print f.readlines()
You see now why Guido doesn't like functional programming.

Saturday 24 November 2007

"With" statement comes handy with OpenGL

OpenGL programming in python may be painful in some situations. The reason for that is openGL's specific interface. Let me show you:

// C code that draws a triangle
glBegin(GL_TRIANGLES);
glVertex3f( 0.0f, 1.0f, 0.0f);
glVertex3f(-1.0f,-1.0f, 0.0f);
glVertex3f( 1.0f,-1.0f, 0.0f);
glEnd();


# the same code in python
glBegin(GL_TRIANGLES)
glVertex3f( 0, 1, 0)
glVertex3f(-1, -1, 0)
glVertex3f( 1, -1, 0)
glEnd()

Now we can make this code a bit clearer using the 'with' statement. Let's wrap glBegin and make it return a context which calls glEnd() after exiting the 'with' block. Having done that, we can write

# python code that draws a triangle
with glBegin(GL_TRIANGLES):
glVertex3f( 0, 1, 0)
glVertex3f(-1, -1, 0)
glVertex3f( 1, -1, 0)

Implementing such behaviour is very easy

from pyglet import gl as pyglet_gl

class GlEndCallingContext(object):
def __enter__(*_): pass
def __exit__(*_): pyglet_gl.glEnd()

def glBegin(*args):
pyglet_gl.glBegin(*args)
return GlEndCallingContext()

In fact, we can go even further and make all glFunctions return some dummy context. We could then add indentation after functions that don't need closing, just in case the overall view of the code should benefit:

with glColorf3(1, 0, 0):
glVertexf(0, 0, 0)
...

I implemented first 5 nehe tutorials in that way, using a wonderful pyglet library.

Sunday 4 November 2007

Painless module monkey-patching

One of Python's great advantages is its absolutely dynamic nature, which enables you to alter many objects (including modules) in runtime. This is particularily useful for testing since you can monkey-patch your module before exercising the SUT and make your tests way more granular.

However, the process has drawbacks, the biggest of which is its tendency towards verbosity. In order to not break other tests in a suite, you need to store the original attributes of the module and set them again after excercising the SUT. And to make sure the module _will_ be fixed you need a try-finally clause. With more than one modules this can lead to a several headache.

So, driven mad by a need to write 40-line test method excercising 4-liner, I imagined a dream-interface for that. What I came up with, besides the module-mocking power offered a mocha-like interface for mocking other objects as well. I have to say, I'm pretty damn proud of the result.



class Automaton(object):

...

def save_in_png(self, filename):
f = open('/tmp/graph.dot', "w")
f.write(self.as_dot())
f.close()
subprocess.check_call(['dot', '-Tpng', '-o', filename, '/tmp/graph.dot'])



class AutomatonTest(unittest.TestCase):

...


def test_save_in_png(self):

a = Automaton({(1, 'a'): [2, 3], (2, 'b'): [3]}, 1, [2, 3])
mock = Mocker(self)
mockfile = mock.object('file').expects('write', args=['beetle']).expects('close')
mock.function_in_module('subprocess.check_call',
args=[['dot', '-Tpng', '-o', 'ladybird', '/tmp/graph.dot']])
mock.function_in_module('__builtin__.open',
args=['/tmp/graph.dot', 'w'],
returns=mockfile)
mock.method(a, 'as_dot', args=[], returns='beetle')

mock.run(a.save_in_png, 'ladybird')







If the test fails, the mocker tries to be helpful again:


Traceback (most recent call last):
File "automaton_test.py", line 77, in test_save_in_png
mock.run(a.save_in_png, 'biedronka')
File "/Users/konrad/dev/sandbox/automata/mocking.py", line 73, in run
'Calls listing of %s fails: \nactual: %s\nexpected: %s' % (key, pretty(invoke_dict['actual_calls']), pretty(invoke_dict['expected_calls'])))
AssertionError: Calls listing of file.close fails:
actual: <No calls>
expected: file.close()

Friday 2 November 2007

GraphViz fun


I'm writing a tiny toy project involving automata, which was a good excuse to learn the dot language finally. I used GraphViz couple of times before and was very fond of it. After trying out several approaches I decided to put down all things I needed in order to get my automata graphs render as needed. These were:


  1. Print the epsilon character.
  2. Switch the orientation to horizontal (it's vertical by default).
  3. Render a double circle node.
  4. Render several graphs in a single plot.
  5. Have invisible nodes.
  6. Have invisible edges.


Having all this before my eyes, it became obvious that the only way to learn how to do it is by example, which is exactly what entered in google. I found this amazing flickr set with examples addressing almost all my problems. So here are the answers:


Print the epsilon character.
You can use html entities in your labels
Switch the orientation to horizontal.
Pass the directive rankdir=LR
Render a double circle node.
node [shape=doublecircle]
Render several graphs in a single plot.
This one was actually not a problem at all - if your nodes don't connect, you get an unconnected graph
Have invisible nodes.
node [shape=plaintext label=""]
Have invisible edges.
edge [style=invis]


So, the presentation's ready. Time to start writing the algorithm :)