Archive for October, 2005

Restoring order with the Deficit of Attention

Due to a temporary lack of internet service, I have been enjoying uninterrupted computer usage. And it is unnerving.

The new attention economy has two sides, having too many distractions is bad, but I’m learning that too few interruptions is also bad.

The always connected nature of modern life means that I am constantly interrupted. By information from RSS. By work via email. By computer literate friends by IM. By others via Skype, SMS or telephone.

My main problem is not that the signal to noise ratio is poor. It is that the signal level can get too high. Removing the signal is not the answer. The supply of information is the reason I endure the interruptions. The answer needs to be a middle ground.

Sometimes I want minimal distraction, and others I’m happy for increased distraction. Unfortunately, the control I have at the moment is an on/off switch. What is needed is an analog control for level of interuption. Something like the status in instant messaging. However, across all information flows.

Smart people are working on being able to monitor information you view and information you seek. The next step is to modulate the information flow. To wrest back control of attention.

At long last: iTunes store in Australia

The perfect iPod accessory, legally downloadable music!

I’ve bought my first song (Ben Lee - Catch My Disease) and am very happy. Click and it all magically happens, based on my existing Apple billing details. All too simple really.

We don’t seem to have won TV shows yet, but Pixar shorts are up and a wide range of music. The downside is that I can see my iTunes account rivaling my Amazon account for purchases ;)

Minix 3.0 released

OSNews story on the release of Minix 3.0.

This is the OS that was given away free as a teaching tool with a text book that many have studied during their uni careers. It has now grown up to become a Real OS, targeting the embedded market.

In terms of OS history, Minix is considered the predecessor to Linux, with Linus learning both from Minix and the associated book.

Kill Spam with WordPress

Update: Perhaps too much optimism too early. Even with what logically seems like a good way of stopping comment spam, I’m still receiving comment spam :(

Back to the drawing board.

 

As I’ve mentioned previously the amount of comment spam has been increasing on my blog. The filtering that WordPress 1.5 does is good enough that no spam had become visible, however the administrative overhead was starting to become untenable.

Given my math background, I quite liked the concept being used over on ridiculous fish. The downside to that approach is that for a good calculation of ∏, you need a lot of comments. Short of letting the spam through, this blog just doesn’t see the level of comments to justify it. The other downside to the approach is that it needs to be explained to the user.

Investigating plug-ins for WordPress were always something on my ToDo list, and comment spam is a good enough reason to go looking.

One spam plug-in I found which I liked the idea of is Bad Behavior. The basic premise is that it will attempt to identify a spam bot based on its behaviour. It will then deny the bot access to the site. The win for this approach is that it works for more than just comment spam. It helps out with referrer and other forms of spam, as it returns a 412 error if a bot is detected. There is some hope my stats may become useful again.

The downside to this approach is that it does use heuristics so isn’t perfect. I haven’t spent long testing it on its own, so I can’t verify how good it is or isn’t. I’ll keep you updated next month if my site stats return to some level of sanity.

The other plug-in that I installed is called HashCash and shows promise of being effective against comment spam.

HashCash works by asking the client posting a comment to calculate a value using JavaScript which is then compared by the server. The author claims that it has been 100% effective for his site, which seems reasonable. The number of bots that are going to have client side JavaScript support seems intuitively to be low.

As more sites improve their spam detection, the spam bots are likely to get smarter. Fortunately, with WordPress’s plug-in architecture, it is a simple matter of upgrading my spam strategy!

Showing a NSSavePanel as a sheet

Using a sheet to display a save panel adds for a significant user interface improvement. Based on the simplicity of a range of other Cocoa features, I was surprised at how tricky it was to get working.

The issues had nothing to do with Cocoa per-se, but with the PyObjC bridge.

Creating the panel and creating the sheet is as follows:


sp = NSSavePanel.savePanel()
sp.setRequiredFileType_('ics')
sp.setNameFieldLabel_('Export As:')

sp.beginSheetForDirectory_file_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
    userDocumentFolder(),'default.ics',
    NSApp().mainWindow(),self,
    'didEndSheet:returnCode:contextInfo:',0)

The tricky bit is the asynchronous callback, which is passed in as an Objective C selector: 'didEndSheet:returnCode:contextInfo:'

In Python terms, the following method does the trick:


def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, info):
    if returnCode == NSCancelButton:
        return

    # Save the file returned by sheet.filename()
    print sheet.filename()

I only had one slight problem. The application crashed when the user clicked a button on the sheet.

The issue is that the Python bridge has no idea as to the type of the arguments in the method listed as the callback from the sheet. When the callback occurs from Cocoa, it goes searching for a method which matches a specific type signature, as documented in NSSavePanel.

An explicit type signature is required for the method to be discoverable. PyObjC provides a few ways of doing this.

Note: the name of the callback function is up to programmer, which is why the type signature is important.

The Python 2.4 version (using decorators) looks like:


from objc import *

@objc.signature('v@:@ii')
def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, info):
    if returnCode == NSCancelButton:
        return

    # Save the file returned by sheet.filename()

With the standard version like:


from objc import *

def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, info):
    if returnCode == NSCancelButton:
        return

    # Save the file returned by sheet.filename()
    print sheet.filename()
didEndSheet_returnCode_contextInfo_ = objc.selector(
        didEndSheet_returnCode_contextInfo_ , signature='v@:@ii')

With the type information added in, the callback can locate the method on the object and all is well in the world. Files are saved, memory protected, everybody is happy.

Almost.

I had a simple question, where did this type string come from?

Searching on Google had resulted in two different strings. Both worked. However they were quite different.


@objc.signature('v16@4:8@12i16i20')
@objc.signature('v@:@ii')

Somewhere, there had to be an explanation for all of this.

The PyObjC documentation provided some good hints. The intro mentions the different syntax for specifying the type signature, and provides an example.

Using a convenience method on AppHelper, the type information does not need to be explicitly included in the code for the endSheet case:


from PyObjCTools import AppHelper

# Python 2.4
@AppHelper.endSheetMethod
def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, info):
	pass

# Python 2.3
def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, info):
    pass
didEndSheet_returnCode_contextInfo_ = AppHelper.endSheetMethod(
        didEndSheet_returnCode_contextInfo_)

This third option provides cleaner code, but still didn’t explain where type signatures come from.

(I must have been a difficult child, a solution isn’t enough, the need to know why/how is the motivator)

The key came in reading through the documentation on wrapping an Objective C class. When the documentation refers to a ‘raw Objective-C method signature’, it means what it says. The string appears to be Objective-C’s internal representation of the type signature.

Apple provides documentation on the syntax. (Note: Try here for updated docs)

Probably a bit further into Objective-C than I wanted to delve, but much was learnt along the way. A good abstraction allows you to peek through it. I’m happy to learn that PyObjC provides that flexibility, and that most of the time, it isn’t needed.

Apple’s recent announcements disappointing

Admittedly, I was really hoping for a video version of AirTunes. At the moment I’m stuck with way too many wires snaking around my living room to get from my laptop to my surround sound system.

In a similar vein, my other gripe is with video play back in iTunes 6. Even with an Airport Express selected for sound, video plays the sound through the laptop speakers. If I’d wanted it to play through the laptop speakers, I would have asked for it. Yes, there is a delay for wireless music, but I would have hoped they were close to fixing that by now.

As for the iTunes Store announcements, they just add a growing resentment, as due to the Australian music labels, we are out in the cold down here.

My sister recently asked me how to download music. I’d assumed she meant by peer to peer, but no, she wanted to know how to buy online. We have a couple of online options for music purchase in Australia, but none that start with iTunes.

Back to waiting …

On the bright side, I’m stoked with my iPod nano :)

Exporting Delicious Library to the web

I’ve been using Delicious Library for a while now and have been incredibly happy with the results. I even managed to figure out which books I had on loan and more importantly discovered I was missing some books from my collection!

The other thing I’d been playing with was including some books on this site that I like. You may have noticed them down the side. There is a bit of PHP code behind the pictures, but I still need to add in the Amazon id number for each book.

My aim is to use Delicious Library to keep track of my physical media, including rating it, and to publish the highest rated media onto the site. Given that Library is basically a thick client built on top of Amazon, I could even include my referrer link, so if anyone buys anything I get a small commission to feed my book buying habits.

Library supports exporting its catalog as a tab separated file. I had hoped for an ‘Export to XML’ option, so it would be simple XSL transform and tada!

Given the Python focus of this site, it didn’t take long before I had a workable script up and running.

The important bits of the script are included below:


from Kid import *
import sys

def normalizeString(str):
    return str.replace('//', '/').replace('/ ', '<br/>').replace('*','<br/>*')

file = open(sys.argv[1], 'rb')
for line in file:
    lines.append(line[:-1])

headings = lines[0].split('t')

books = [dict(zip(headings, normalizeString(line).split('t'))) for line in lines[1:]]

Template(file='template.kid', books=books).serialize()

The Template object is from the Kid template library, which is responsible for generating the resultant XHTML. The template.kid file that I used iterates through the list of dictionaries of books and displays as links the ones that I’ve given a rating of (5/5).


<?xml version='1.0' encoding='utf-8'?>
<html xmlns='http://www.w3.org/1999/xhtml' xmlns:py='http://purl.org/kid/ns#'>
  <head><title>Delicious Library</title></head>
  <body>
    <ul>
      <li py:for='book in books' py:if="book['rating'] == '5'">
        <a py:attrs="href='http://www.amazon.com/exec/obidos/ASIN/' + book['asin'] + '/pseudofish-20?creative=327641&camp=14573&link_code=as1'">
        ${book['title']}
        </a>
      </li>
    </ul>
  </body>
</html>

The end result, is that I can now add books into Library as I purchase and read them, and if I rate the book highly enough then it will turn up on this blog.

Source code and samples are available here. Requires Delicious Library (full or demo) and Kid.

Can a Python eat an Alligator?

The answer is maybe. Fox news has an interesting story about pythons in the Everglades.

It turns out that the question is less than rhetorical and that a python attempted to consume an alligator. Unfortunately for the python, it exploded in the process.

My favourite quote goes to:

“Clearly, if they can kill an alligator they can kill other species,” Mazzotti said. “There had been some hope that alligators can control Burmese pythons. … This indicates to me it’s going to be an even draw. Sometimes alligators are going to win and sometimes the python will win.

It is starting to remind me of a recent film with the tagline:

Whoever wins… We lose.

Comment spam hits a new level

An interesting read by Tom Coates about an advertising firm (maybe) using comment spam to promote cleaning products.

Linked via Hugh

I’ve been seeing more and more random comment spam on this blog, which Wordpress filters for me, but it still requires moderation. Fortunately, none has gotten through (so far) with vigilance taking up more time. Now even the legitimate looking comments seem to require checking.

Luckily, Cocoa development in Python is a fairly niche audience. I’m sure some of the bigger blogs are having even bigger issues with spam, which will be why comments get disabled on a lot of them.

I’m almost in agreement with Seth Godin that if you want to have the conversation, start your own blog and then link back to the article. Almost.

Update: And now an apology after the fact.

Pimp My Code, Part 5: In Python

Wil Shipley continues his Pimp My Code series with an article reviewing what should have been a very simple method to return a path inside the Application Support folder.

Well worth a read, both for the stylistic as well as the comedic commentary.

From my perspective, it is also a useful source of learning about Cocoa from one of the better programmers.

In Python, the final example looks something like:


from Foundation import *

def applicationSupportFolder():
    return NSSearchPathForDirectoriesInDomains(
        NSApplicationSupportDirectory, NSUserDomainMask, True
    )[0].stringByAppendingPathComponent_(
        NSProcessInfo.processInfo().processName())

Note: The code has been reformatted to (attempt to) fit your screen.