?

Log in

No account? Create an account

Cargo Pants of Escape

Oct. 7th, 2006 | 11:33 pm

I lost my backup phone tonight. Don't run wearing the cargo pants of escape.

Can anyone recommend a new phone? (Also, yes, I lost everyone's number again. Please get in touch).

Link | Leave a comment | Share

Axiom Powerups Summarised

Sep. 13th, 2006 | 02:04 pm

Thanks to idnar for this.

  • Item is a subclass of Empowered, which has powerUp() and powerDown()
  • You pass in another item (the "powerup") and an interface
  • You can call item.powerupsFor(interface) and item.interfacesFor(powerup)
  • Once you've done foo.powerUp(bar, IFoo), IFoo(foo) will give you bar
  • If foo has many powerups implementing IFoo, IFoo(foo) gives you the first one returned (ordered by priority)
  • However, you generally want either all IFoo powerups, or you don't want to have more than one IFoo powerup installed in the first place.

For more information, see the Axiom website.

Link | Leave a comment | Share

Making the Switch

Jul. 25th, 2006 | 02:10 pm

I'm thinking about switching my Powerbook from OS X to Ubuntu Edgy.

Last time I tried this (pre Dapper release), I had lots and lots of trouble with getting Caps Lock to behave as a control, and with getting my wireless card to work. Has this been fixed?

Any other advice?

Link | Leave a comment {1} | Share

How to Disconnect in Twisted, Really

Jul. 7th, 2006 | 06:51 pm

Trial is about to become a fair bit stricter on tests that don't clean up after themselves. In particular, any test that doesn't properly close all of its connections is going to fail ugly. Lots of Twisted's tests fail very ugly.

If you are already glazing over with apathy towards unit tests, unglaze now. Disconnecting is a little more involved than you might expect, and there's every chance you aren't doing it correctly.

Thanks to moshez and teratorn for reviewing this document. Any errors are still my fault.

How to Disconnect in Twisted, Really

You are writing a test for some network code. You have already written many unit tests for the protocol and the protocol factories, and you assume that the lower level socket calls all work. You have deep confidence in your code, but you need more.

You yearn to write an end-to-end test. To set up the server, have it really listen, have the client really connect. You want data to zoom back and forth over the wire like luxury sportscars on the Autobahn.

Fine.

You start with a server and a client.

You want the client to connect to the server. You want things to happen. When things have happened, you want to assert stuff.

After the assertions, you want to disconnect the client and you want the server to go away.

Good.

I'm not going to help you set things up, I'm not going to help you make things happen. I am going to help you make all your connections just go away.

Let me lay down the law:

  1. The server must stop listening.
  2. The client connection must disconnect.
  3. The server connection must disconnect.

You have to make each of these things happen. That's easy. Here's the tricky part: you have to wait for each of these things to happen.

To make the server stop listening:
    port = reactor.listenTCP(portNumber, factory)
    ...
    port.stopListening()
You can effect the last two with one stroke, using either of the following commands:
    protocol.transport.loseConnection()

OR

    connector = reactor.connectTCP(host, port, factory)
    ...
    connector.disconnect()

Now, just running these commands is not enough. Twisted is asynchronous: you have to wait for it to call you.

The way to wait for something in a Trial test is to return a Deferred from the test method.

stopListening is easy. It returns a Deferred if it is going to take a while. The others are harder. There is only one way to know when the connection has truly been lost: override connectionLost on a Protocol instance. Pass a Deferred to your Protocol and have connectionLost call callback on that Deferred. You will need to do this for both the client and the server protocols. Once you have your three Deferreds, return them in a DeferredList.

Simple.

UPDATE: exarkun tells me that there are two ways to know when the connection has truly been lost on the client side. ClientFactory.clientConnectionLost is the other one.

Summary

In order to clean up a test that connects a client to a server, you need to wait on three Deferreds: one for the listening port, one for the server protocol and one for the client protocol.

Below is an example demonstrating how to disconnect. As you can see, it is quite verbose. Trial does not provide any shortcuts, even though it should.

from twisted.internet import defer, protocol
from twisted.trial import unittest

class ServerProtocol(protocol.Protocol):
    def connectionLost(self, *a):
        self.factory.onConnectionLost.callback(self)

class ClientProtocol(protocol.Protocol):
    def connectionMade(self):
        self.factory.onConnectionMade.callback(self)

    def connectionLost(self, *a):
        self.factory.onConnectionLost.callback(self)


class TestDisconnect(unittest.TestCase):
    def setUp(self):
        self.serverDisconnected = defer.Deferred()
        self.serverPort = self._listenServer(self.serverDisconnected)
        connected = defer.Deferred()
        self.clientDisconnected = defer.Deferred()
        self.clientConnection = self._connectClient(connected,
                                                    self.clientDisconnected)
        return connected

    def _listenServer(self, d):
        from twisted.internet import reactor
        f = protocol.Factory()
        f.onConnectionLost = d
        f.protocol = ServerProtocol
        return reactor.listenTCP(0, f)

    def _connectClient(self, d1, d2):
        from twisted.internet import reactor
        factory = protocol.ClientFactory()
        factory.protocol = ClientProtocol
        factory.onConnectionMade = d1
        factory.onConnectionLost = d2
        return reactor.connectTCP('localhost',
                                  self.serverPort.getHost().port,
                                  factory)

    def tearDown(self):
        d = defer.maybeDeferred(self.serverPort.stopListening)
        self.clientConnection.disconnect()
        return defer.gatherResults([d,
                                    self.clientDisconnected,
                                    self.serverDisconnected])

    def test_disconnect(self):
        pass

Link | Leave a comment {14} | Share

Windows

Jul. 5th, 2006 | 12:24 pm

So, I've just got my old Athlon back from my bro. I've re-installed Windows XP on it, and am now making it usable. (Actually I'm doing other stuff, and occasionally touching this job)
  1. Get Firefox
  2. Get PuTTY
  3. Shell into a unix machine, launch screen, launch irssi
  4. Configure a Twisted development environment
That last one will implies Emacs, SVN and a host of other things. Basically, cyli's page has everything I need except for: It has been a very long time since I used a Windows machine for development. What new tools are kicking around to make this vast, desolate, Westfield of an OS actually usable?

Update: I have already been asked to reboot my computer. Ahh, the memories.

Link | Leave a comment | Share

Phone Lost, Number Found

Jun. 20th, 2006 | 12:21 pm

You can call my mobile again -- 0439 043 221.

Link | Leave a comment | Share

Lost Phone

Jun. 16th, 2006 | 10:25 am

I lost my mobile phone on Wednesday June 14 at about 4pm.

If you have it, and by some miracle find this post, please return it.

Link | Leave a comment {3} | Share

Emacs Tricks

May. 26th, 2006 | 11:32 am

Jp has posted a guide on how to configure emacs for Divmod javascript on the Divmod wiki.

It works, and means the painful indentation problems that I talked about earlier just go away.

Link | Leave a comment | Share

Test Driven Development with Athena

May. 24th, 2006 | 02:15 pm

The boffins at Divmod have produced a fine two-way AJAX toolkit called Athena. To play with it, you'll need to configure Nevow on your coding box. The link should have instructions. If not, Someone should do Something about it.

This post is for people like me who have forgotten how to program without writing unit tests first. Kent Beck's book shows why this is good state to be in.

The Divmod guys have written a unit testing framework called nit that makes it fairly easy to write unit tests for your JavaScript code. This isn't anything new. nit also makes it easy to write unit tests for your Athena widgets, which is awesome. Here's how to do it.

1 Set up files and directories

Create the following files and directories.
  • foo/__init__.py
  • foo/test/__init__.py
  • foo/test/livetest_foo.js
  • foo/test/livetest_foo.py
  • foo/static/js/foo.js
  • nevow/plugins/foopackage.py
We are creating a package called 'foo' for which we are writing tests. The main JavaScript module is in foo/static/js/foo.js and our tests are under foo/test/livetest_*. The 'livetest_' prefix is so that nit will find the tests. The plugin file will be loaded automatically by Nevow using Twisted's plugin system.

2 Set up namespaces

Athena lets you put JavaScript files in namespaces, just like Python. However, unlike Python, you have to explicitly map namespaces to files.

So, in nevow/plugins/foopackage.py:
from twisted.python import util
from nevow import athena
import foo

def _f(*sib):
return util.sibpath(foo.__file__, '/'.join(sib))

fooPkg = athena.JSPackage({
'Foo': _f('static', 'js', 'foo.js'),
'Foo.Tests': _f('test', 'livetest_foo.js')})
This registers foo/static/js/foo.js as the 'Foo' namespace and foo/test/livetest_foo.js as the 'Foo.Tests' namespace.

3 Write A Single, Failing Test

Your test needs to have two parts: a JavaScript part and a Python part.

In foo/test/livetest_foo.py:
from nevow import loaders, tags
from nevow.livetrial import testcase

class OurFirstTest(testcase.TestCase):
    jsClass = u'Foo.Tests.OurFirstTest'
    docFactory = loaders.stan(tags.div(render=tags.directive('liveTest'))['OurFirstTest'])
This is the hook for nit to catch on to your test.   nit will render OurFirstTest as a LiveFragment and bind it to an instance of Foo.Tests.OurFirstTest. 

In foo/test/livetest_foo.js:
// import Nevow.Athena.Test

Foo.Tests.OurFirstTest = Nevow.Athena.Test.TestCase.subclass('OurFirstTest');
Foo.Tests.OurFirstTest.methods(
    function run(self) {
        self.assertEquals('apple', 'orange');
    });
This is a weird way of writing JavaScript.  Emacs hates it – it does the indentation all wrong, and no one at Divmod had a fix on hand.  Anyway, it's all part of the desperate bid to turn JavaScript into Python.   The run method gets called by nit when you click 'Run'.  Observe!

$ nit foo
2006/05/24 11:26 EST [-] Log opened.
2006/05/24 11:26 EST [-] nevow.appserver.NevowSite starting on 8080
2006/05/24 11:26 EST [-] Starting factory <nevow.appserver.NevowSite instance at 0x1427990>

Hit http://localhost:8080/ and you'll see your test and a button.  Hit the button.  You'll see a big red traceback that shows that your test FAILS.

We have a single, failing test.  However, it only does JavaScript stuff, which is boring.

4 Write A Single, Failing Widget Test

To test a widget, we need to create it and render it as a part of the test. Creating a widget requires writing a JavaScript part and a Python part.  We'll also need to modify our test to actually call things on the widget.

So, first, change livetest_foo.py:
from nevow import loaders, tags
from nevow.livetrial import testcase
from foo import widgets

class OurFirstTest(testcase.TestCase):
    jsClass = u'Foo.Tests.OurFirstTest'
    docFactory = loaders.stan(
tags.div(render=tags.directive('liveTest'))[
tags.invisible(render=tags.directive('barwidget'))])

def render_barwidget(self, ctx, data):
widget = widgets.Bar()
widget.setFragmentParent(self)
return widget

This creates the widget Bar and renders it as part of the test.  However, before we implement the widget, let's finish writing the test: that is, the JavaScript part.
// import Foo
// import Nevow.Athena.Test

Foo.Tests.OurFirstTest = Nevow.Athena.Test.TestCase.subclass('First');
Foo.Tests.OurFirstTest.methods(
    function run(self) {
        var bar = self.childWidgets[0];
        self.assertEquals(bar.frotz(), 42);
    });
At this point, 'nit foo' will fail saying it can't import foo/widgets.py.  Creating foo/widgets.py will let it run properly, but the page will render a stack trace indicating that widgets.Bar doesn't exist. Let's create it minimally. Put this in foo/widgets.py
from nevow import loaders, tags, athena

class Bar(athena.LiveFragment):
    jsClass = u'Foo.Bar'
    docFactory = loaders.stan(
        tags.div(render=tags.directive('liveFragment'))['Bar'])
Now we can view the tests. However, running them will produce a failure, saying that 'bar' has no properties, referring to the bar in Foo.Tests.OurFirstTest.run.  This is because we haven't yet written the JavaScript side of the widget.  We've said the class is Foo.Bar, so we'll need to dump it in the Foo namespace which we've configured in foo/static/js/foo.js.  Here it is.
// import Nevow.Athena

Foo.Bar = Nevow.Athena.Widget.subclass('Foo.Bar');
Foo.Bar.methods(
    function frotz(self) {
    });
Now, run 'nit foo' again, hit your localhost and run the tests. You should see "Error: Test Failure: undefined != 42" at the top of your red traceback.  You now have a single failing unit test for an Athena widget.

Link | Leave a comment {4} | Share

Blog and Tech Blog

Apr. 3rd, 2006 | 11:05 am

Prompted by some reader feedback, I'm splitting up my blogged life. I'd like to choose what gets syndicated to Planet Twisted. Tag support isn't there yet, so I'm splitting my blogs.

Hacking stuff is going in blackjml
Everything else is in jml

I'll probably still put tech stuff in jml.lj.com and just cross-post to blackjml.

It's a difficult decision for me. On the one hand, Twisted itself has nothing to do with the books I'm reading, my political views or my theological reflections. On the other hand, hacking on Twisted is part of my life of ministry and one of the ways that I worship God.

So, I've made my decision based on personal ease. I struggle to write in my blog about the things that I'm actually thinking about, that really influence me. I feel that I have to navigate through the minefield of misconceptions that people have about evangelical Christianity. I want to make the rest of my blog easy for the people are reading my blog solely because of my participation in Twisted. On reflection, I think it instead becomes cumbersome and verbose for everyone.

However, if people are reading my blog based on some interest in me, then the task of resolving misconceptions can be shared.

Link | Leave a comment | Share