Upgrading from buildbot 0.7.12 to 0.8.2

Some notes from my experience upgrading Twisted's buildmaster installation:

tricks for good 2d rendering performance with pygame

pygame's API for initializing a display has a couple attractive sounding flags. Or at least, they sound attractive once you notice that updating your 320x240 window at 30fps is consuming all available cycles on your brand new Intel i9 CPU. They're HWSURFACE and DOUBLEBUF. Hardware surfaces and double buffering is how you make graphics fast, right?



Well... no. You probably can't get a hardware surface anyway, and double buffering is unlikely to help until you figure out how to have a window larger than 320x240 anyway.



What you really need to do to get reasonable rendering performance is make sure that any images you blit to the display have the same color depth. You can do this with the Surface.convert method (you get back a Surface when you load an image from a file, eg a png or a gif). Check the depth of your display surface and then convert your image surfaces to that depth (and just keep the converted versions). Blitting them will get hundreds of times faster.



It's a pretty simple thing, but it's easy to get distracted by HWSURFACE and not notice the depth mismatch (like I did for three days).

pyn is dead, long live pyn

Just about eight years ago I started working on an IRC bot using Twisted, pynfo. I managed to stay interested in working on it for about six months. I kept running an instance of it until this morning. Over the years its features have slowly decayed: it hasn't relayed messages between networks or announced pypi releases for years; more recently changes to Freenode have stopped it from even responding to commands.

As such an old project, I wrote it before I knew about things like unit tests, so even if I were interested in maintaining an IRC bot anymore, it would be easier to start over than try to resuscitate pynfo. Thus, pynfo, I consign you to oblivion. Rest in peace:

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
exarkun   5621  8.9 12.1 275416 251248 ?       Rl    2009 70025:19 /usr/bin/python /usr/bin/twistd -f infobot.tap

filepath 0.1

Hello all,

I'm happy to announce the initial release of filepath.

filepath is an abstract interface to the filesystem. It provides APIs for path name manipulation and for inspecting and modifying the filesystem (for example, renaming files, reading from them, etc). filepath's APIs are intended to be easier than those of the standard library os.path module to use correctly and safely.

filepath is a re-packaging of the twisted.python.filepath module independent from Twisted (except for the test suite which still depends on Twisted Trial).

The low number of this release reflects the newness of this packaging. The implementation is almost entirely mature and well tested in real-world situations from its time as part of Twisted.

You can find the package on PyPI or Launchpad:

http://pypi.python.org/pypi/filepath/0.1
https://launchpad.net/filepath

python-signalfd 0.1

Hello all,

I'm happy to announce the initial release of python-signalfd. This simple package wraps the sigprocmask(2) and signalfd(2) calls, useful for interacting with POSIX signals in slightly more advanced ways than can be done with the built-in signal module.

You can find the package on PyPI or Launchpad:

http://pypi.python.org/pypi/python-signalfd/0.1
https://launchpad.net/python-signalfd

Asynchronous JSON

Today in #twisted.web the topic of generating large JSON responses in a Twisted Web server came up. The problem was that the data being serialized into JSON was so large that the JSON serialization process itself would block the web server, preventing other requests from being serviced.

The first solution that came up was to split the web server into two pieces, so that the URLs which could have these JSON responses were served by a different process than was serving the rest. This is a pretty decent solution, and it also provides the benefit of using extra CPU cores if there are any available. In this case, it complicated things a little since it meant sharing a session across two processes. So we went looking for another approach.

It turns out that the json module supports incremental serialization. When I saw the JSONEncoder.iterencode method, I thought it would be great used in combination with cooperate to create a producer. This would let an application serialize a large structure to JSON without multiple processes, threads, or unreasonably blocking the reactor.

Here's the little bit of glue necessary to make things work:

from json import JSONEncoder

from twisted.internet.task import cooperate

class AsyncJSON(object):
    def __init__(self, value):
        self._value = value


    def beginProducing(self, consumer):
        self._consumer = consumer
        self._iterable = JSONEncoder().iterencode(self._value)
        self._consumer.registerProducer(self, True)
        self._task = cooperate(self._produce())
        d = self._task.whenDone()
        d.addBoth(self._unregister)
        return d


    def pauseProducing(self):
        self._task.pause()


    def resumeProducing(self):
        self._task.resume()


    def stopProducing(self):
        self._task.stop()


    def _produce(self):
        for chunk in self._iterable:
            self._consumer.write(chunk)
            yield None


    def _unregister(self, passthrough): 
        self._consumer.unregisterProducer()
        return passthrough


By using the iterencode method, this avoids spending too much time generating json output at once. Instead, a little bit of the input will be serialized at a time, and each short resulting string is available from the iterator returned by iterencode.

By using cooperate, the _produce generator will iterated in a way that lets it cooperate with the reactor and other event sources/handlers. A few chunks of json data will be written to the consumer, then execution will switch away to something else, then come back and a few more will be written, and so on.

And by using the producer/consumer interface, if the HTTP client which issued the request doesn't read the results as fast as they're being generated, the server will stop generating new output until the client catches up.

Altogether, this provides a very cool, efficient way to generate JSON output.

Here's an example to make it easier to see how one might use AsyncJSON in a resource:

from twisted.web.resource import Resource
from twisted.web.server import NOT_DONE_YET

class BigIntegerList(Resource):
    def render_GET(self, request):
        length = int(request.args['length'][0])
        d = AsyncJSON(range(length)).beginProducing(request)
        d.addCallback(lambda ignored: request.finish())
        return NOT_DONE_YET

Minuteman Campground Bike Trip

Jericho and I took a quick bike trip out of the city a few weekends ago.

The Minuteman Campground is pretty nice. It's still fairly early in the season, so there were only a few other people there. We brought a tent, but there were also campsites with cabins, which might be nice for longer trips (ie, less baggage).

The ride out was about five hours (including a stop for lunch). Coming back was much quicker, maybe three hours (including a stop for lunch and ice cream).

Overall, a fun trip.

January - May Reading List