Sunday, March 26, 2006
Sunday, March 12, 2006
Wrote a program to email weekly trac ticket cha...
Wrote a program to email weekly trac ticket change summaries.
Thursday, March 9, 2006
Reading List
Over about the last year, I've read:
Collapse: How Societies Choose To Fail Or Succeed. Jared Diamond.
Twisty Little Passages: An Approach to Interactive Fiction. Nick Montfort.
The Legacy Of Heorot. Larry Niven, Jerry Pournelle, Steven Barnes.
Choke. Chuck Palahniuk.
The Extravagant Universe. Robert P. Kirshner.
Knife Of Dreams. Robert Jordan.
Dread Empire's Fall: Conventions of War. Walter Jon Williams.
Cosmonaut Keep. Ken Macleod.
The Island of the Day Before. Umberto Eco.
Paths To Otherwhere. James P. Hogan.
Moonseed. Stephen Baxter.
The Zap Gun. Phillip K. Dick.
Germinal. Émile Zola.
These are pretty much all worth a read, although if you read just 12 of them, I'd give The Island of the Day Before a miss. Germinal is probably the best of the bunch, but Collapse is the most educational, Knife of Dreams the most indulgent, and Conventions of War the most fun (but it's the last in a trilogy).
By way of meaningless comparison, about 46KLOC that I've checked into source control over the same period of time has survived to this evening.
Wednesday, March 8, 2006
Axiom Concurrency
Over the last several days, Axiom's concurrency story has come together quite a bit. This is something Divmod has put off, fairly confident that it would be easy once the time came. And hey, we were actually right (that's always fun). Quotient's fulltext indexer is now implemented as a second process which is automatically brought up and down with the main server and which is handed work to do through the Axiom database. This is done in a generic fashion with Axiom framework code doing most of the heavy lifting (like repeatedly adding one to an integer and comparing two numbers to see which is greater), so the actual application-level code in Quotient ends up doing something like this (actually, exactly this) in order to take advantage of the multi-process aspect of the system:
def installOn(self, other):
super(SyncIndexer, self).installOn(other)
self.store.findUnique(mail.MessageSource).addReliableListener(self, iaxiom.REMOTE)
def indexMessage(self, message):
# Do the actual full-text indexing of `message'
processItem = indexMessage
The code barely even has to be aware of the fact that anything is going on in a separate process: it simply declares its interest in MessageSource
and the fact that it has no objection to a potentially esoteric execution context. Any application can take advantage of this feature just as easily to split computationally expensive or high-volume disk-bound tasks into a separate operating system process, where they will not interefere with code that is latency-sensitive (like a network server) and where they can take advantage of additional CPU or disks, providing for greater parallelization of the task.
Of course, like any user of the reliable listener system, be they local or remote, the indexer:
will be run as quickly as the system can manage, taking into account the interactive requirements being placed on the server as well as the other reliable listeners which have been created and are also vying for runtime;
can be throttled back if another task takes precedence;
can be monitored and reported on for user feedback or administrator introspection;
can be prioritized relative to other kinds of listeners;
can be reset entirely and allowed to re-process all old messages (useful when, for example, the fulltext index becomes corrupt or is replaced by a superior system);
or just messages which caused an error and due to code changes may now be processable without one.
All this is due to the fact that the fulltext indexer is not directly responsible for finding messages to index, but has merely subscribed to a source of messages, and it is up to that subscription to decide what and when messages are given to it to be indexed.
Monday, March 6, 2006
JavaScript source lines in Python tracebacks
Added support to Athena today for generating tracebacks like this one:
2006/03/06 19:44 EST [HTTPChannel,250,127.0.0.1] Traceback (most recent call last):
File "", line 0, in
------
File "", line 0, in
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/runtime.js", line 39, in ()
d.callback(result);
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/divmod.js", line 136, in ([object Object])
return methodFunction.apply(this, args);
File "", line 0, in apply([object Object],[object Array])
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/defer.js", line 137, in callback([object Object],[object Object])
self._startRunCallbacks(result);
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/divmod.js", line 136, in ([object Object])
return methodFunction.apply(this, args);
File "", line 0, in apply([object Object],[object Array])
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/defer.js", line 134, in _startRunCallbacks([object Object],[object Object])
self._runCallbacks();
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/divmod.js", line 136, in ()
return methodFunction.apply(this, args);
File "", line 0, in apply([object Object],[object Array])
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/defer.js", line 108, in _runCallbacks([object Object])
self._result = callback.apply(null, args);
File "", line 0, in apply(null,[object Array])
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/athena.js", line 259, in ([object Object])
action.apply(null, actionArgs);
File "", line 0, in apply(null,[object Array])
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/athena.js", line 191, in ("Nevow.Athena.callByAthenaID","s2c241",[object Array])
result = funcObj.apply(null, funcArgs);
File "", line 0, in apply(null,[object Array])
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/athena.js", line 609, in (14,"setStatus",[object Array])
var widget = Nevow.Athena.Widget.fromAthenaID(athenaID);
File "/home/exarkun/Projects/Divmod/trunk/Nevow/nevow/athena.js", line 601, in (14)
throw new Error(nodes.length + " nodes with athena id " + widgetId);
File "", line 0, in Error("0 nodes with athena id 14")
nevow.athena.JSException: Error: 0 nodes with athena id 14
Still a bit unsightly: function arguments probably shouldn't show up, anonymous functions should be reported as "--- ---
), as it is with real Python exceptions.
Anyway, the coolest part is how emacs can parse these and automatically open the appropriate JavaScript file for me when an unhandled error occurs in the browser during debugging.