Mathieu Fenniak's Weblog

2003/09/22

Scripting AppleScriptable Applications with Python

Filed under: programming,python — admin @ 1:51 pm

Python 2.3 comes with a number of utilities which make it possible to use the same interface for accessing software as AppleScript uses. It isn’t much more difficult than AppleScript, but using it effectively is very hard due to the lack of documentation. I present to you a mini-tutorial on how to script iTunes with Python through the Open Scripting Architecture (OSA), using gensuitemodule.py.

First of all, you must run the program gensuitemodule.py on your target application (in this case, iTunes) to generate a set of class wrappers for that program. gensuitemodule.py is installed in your the plat-mac library directory of your Python installation, and can be used as a module or as a stand-alone application. In order to generate an iTunes package containing the wrapper classes, we run the program like this:

pythonw {...}/lib/python2.3/plat-mac/gensuitemodule.py \
    --output iTunes --resource --creator hook \
    /Applications/iTunes.app/Contents/Resources/iTunes.rsrc

That should automatically create an iTunes directory wherever you run the command which contains the files Internet_suite.py, Standard_Suite.py, __init__.py, and iTunes_Suite.py. If you’re familiar with AppleScript, the three suite files should sound familiar to how classes and functions are seperated in an application’s script dictionary.

From then on, scripting iTunes is fairly easy. However, accessing properties and iterating through containers takes a bit of extra work. I’ll demonstrate a few different functions. Please note that these scripts must be run with the pythonw executable because they need access to the window manager. Running them with python will cause an error.

In order to do most anything, you should create an instance of the iTunes class inside the iTunes module. This instance is responsible for sending messages to iTunes, and receiving the responses. Essentially, all communication with the application will go through this instance. Here are a few simple operations:

import iTunes
app = iTunes.iTunes()
app.start()          # fires up iTunes
app.play()           # whack the play button
app.stop()           # stop playing

# Retrieve the current track property.
trk = app.get(app.current_track)
# repr(trk) == "file_track(5721,...)"

# Retrieve the track's 'artist' property.
print app.get(trk.artist)

Iterating over a collection requires a bit more work. Here’s an example of iterating through every song in iTunes’ library:

import iTunes
app = iTunes.iTunes()
library = iTunes.library_playlist(1)
# (Note, this iteration sucks.  See 'fixed_indexing' later.)
for i in range(1, app.count(library, each = iTunes.track) + 1):
    trk = playlist.track(i)
    print app.get(trk.artist)

Notice that indicies start at 1, not 0. When we create a track object to represent the track from the playlist, we could create an iTunes.track, iTunes.file_track, iTunes.url_track, or iTunes.shared_track. Each of the more specialized track objects has the same properties as iTunes.track, but also has more specialized properties. They’re inherited classes. The easiest way to see the relationship between different classes, as well as the properties and methods they have, is to open up the Script Editor application (/Applications/AppleScript/Script Editor.app), choose the ‘Open Dictionary…’ menu option, and select the iTunes application.

I noticed that the iteration shown above would sometimes return the same tracks for different indicies. A bit of research showed that iTunes had a ‘fixed indexing’ property which is usually set to false. I suggest that before iterating through a playlist, this property be set to true:

old_fixed_indexing = app.get(app.fixed_indexing)
app.set(app.fixed_indexing, to = 1)
try:
    code()
finally:
    app.set(app.fixed_indexing, to = old_fixed_indexing)

I did all this research while trying to write an application to set the track numbers of every ogg file in my iTunes library properly. The application is short, but contains everything I know about scripting iTunes with Python. It requires that pyogg and pyvorbis be installed. One of the more difficult parts was taking the iTunes.file_track‘s location property, which is an instance of Carbon.File.Alias, and getting the path that it points to:

#!/usr/bin/env pythonw2.3

import iTunes
import ogg.vorbis

app = iTunes.iTunes()
playlist = iTunes.library_playlist(1)

# Ensure that the track order will not reset during this script.
old_fixed_indexing = app.get(app.fixed_indexing)
app.set(app.fixed_indexing, to = 1)

try:
    for i in range(1, app.count(playlist, each=iTunes.track) + 1):
        trk = playlist.file_track(i)
        location = app.get(trk.location)
        fsref, wasChanged = location.FSResolveAlias(None)
        path = fsref.FSRefMakePath()
        if path.endswith(".ogg"):
            vc = ogg.vorbis.VorbisFile(path).comment()
            for key, value in vc.items():
                if key.upper() == 'TRACKNUMBER':
                    app.set(trk.track_number, to=int(value))
                    break
finally:
    app.set(app.fixed_indexing, to=old_fixed_indexing)

And so ends my first experience with using Python and OSA. It was not as easy as it should have been, but hey… it was easier than writing an Ogg Vorbis comment decoder in AppleScript.

2003/07/16

Formatting a Simple Function in Python

Filed under: programming,python — admin @ 6:11 pm

I use Python often for writing web based applications, such as this weblog you’re reading right now. I love using Python, but I occasionally have problems with making the code asthetically pleasing. This article presents a short case study into formatting a simple function from my website’s picture gallery.

Original code (artifically wrapped for web viewing):

def getThumbnailLink(self):
    return '<a href="%s"><img class="thumbnail" src="%s" width="%s" height="%s"
/></a><br /><span class="thumbnail-title">%s</span>' % (self.getHandPageURL(),
self.getWebPath(self.getThumbnailPath()), self.thumbnailDimensions[0],
self.thumbnailDimensions[1], self.title)

This code presents a simple problem. It has large line of code. I like to keep lines of code to shorter than 79 columns when possible. Additionally, this code is hard to modify. I approached this because I wanted to add an alt attribute into the img tag, but found that where I put the attribute would actually affect the order of elements in the tuple. That’s really unfortunate.

The first attempt to clean up this code was simply by putting a linebreak in between the string literal and the tuple (also artificially wrapped for web viewing):

def getThumbnailLink(self):
    return '<a href="%s"><img class="thumbnail" src="%s" width="%s" height="%s"
/></a><br /><span class="thumbnail-title">%s</span>' % \
        (self.getHandPageURL(), self.getWebPath(self.getThumbnailPath()),
self.thumbnailDimensions[0], self.thumbnailDimensions[1], self.title)

This helped a bit, but both lines were still greater than 79 characters, and it failed entirely to address the problem of the tuple ordering. I whacked at it with the enter and tab keys for a while, but didn’t get anywhere. Fearing I was low on visionary power, I consulted with Lynx. She threw out a chunk of code with shorter lines:

def getThumbnailLink(self):
    ret1 = '<a href="%s"><img class="thumbnail" src="%s" width="%s"'
    ret2 = ' height="%s" /></a><br /><span class="thumbnail-title">%s</span>'
    t = self.getWebPath(self.getThumbnailPath())
    ret1 = ret1 % (self.getHandPageURL(), t, self.thumbnailDimensions[0])
    ret2 = ret2 % (self.thumbnailDimensions[1], self.title)
    return "%s%s" % (ret1, ret2)

This code was a bit of an improvement. It’s a bit more flexible because there aren’t any tuples of arguments that are quite as long as the original, and the code lines are shorter. The function has grown a bunch of extra operations (but who cares?), and a number of extra lines (oh, my poor hard drive…), but it’s definately an improvement.

I wondered whether there wasn’t a better way to write such a small function. I thought about common templating systems and how they work, such as [A-Z]SP, but I didn’t want to do anything that complex. I like the idea of seperating code and HTML, and this current function and server-page technologies do the opposite of that. I pounded at it for a bit, and came up with this function:

def getThumbnailLink(self):
    htmlText = """
        <a href="%(handURL)s">
            <img class="thumbnail" src="%(thumbnailImageURL)s" alt="[%(title)s]"
            width="%(thumbnailImageWidth)s" height="%(thumbnailImageHeight)s" />
        </a>
        <br />
        <span class="thumbnail-title">%(title)s</span>"""
    return htmlText % \
        {'handURL': self.getHandPageURL(),
        'thumbnailImageURL': self.getWebPath(self.getThumbnailPath()),
        'thumbnailImageWidth': self.thumbnailDimensions[0],
        'thumbnailImageHeight': self.thumbnailDimensions[1],
        'title': self.title}

This function shortens the lines of code, addresses the issue of the tuple ordering, and it also seperates the HTML from the Python code. It would be easy to get the htmlText variable from a module full of HTML strings which define the entire web page. It wouldn’t be as flexible as a full-out template system, but I like it more.

2003/04/29

Playing with Weblogs

Filed under: programming — admin @ 6:10 pm

I’ve completed a massive overhaul of my growlmurrdurr weblog system. It is now exactly a thousand times more nicely written, expandable, and just generally clean and happy. No longer do I have to cringe every time I want to make a change to it.

Now that it is more generic and clean and happy, after a bit of testing I’ll be releasing it upon the world on my software page. I’ll even write some documentation and stuff, explaining how to set it up. It’s nice and robust now, such that modifications to the source files shouldn’t be required just to get it up and running. Give or take a bit.

I’ve been pondering the best way to create some kind of configuration system for it. Some kind of more robust administration than it currently has, so that it could be expanded to do more in the future. Multiple author support, for example, will require some way for the administrator to create multiple author ‘accounts’ and so on. Anyways, I’m happy with the new setup. It’s up-to-date in the Subversion repository, if you’re interested in taking a look-see.

2003/04/16

Google Search Term Highlighting

Filed under: programming,python — admin @ 12:33 am

Earlier today I was searching around Google for some technical information, and was given Expert Exchange as a link. Experts Exchange has this nifty little feature, implemented as an Apache module, whereby the HTTP referer is examined for search engine footprints. When it is found that you were linked by a search engine, this Apache module named mod_suru will highlight those search terms inside the resultant web page.

“Cool,” I thought. So I went to look at mod_suru and discovered that it is only available at a relatively high cost. Certainly not a cost that I’m willing to pay for my own personal website.

So I took another path. I re-implemented the same idea as mod_suru through primarily JavaScript. My stomphighlight.js file defines a function, highlightWord, which does the magic of actually highlighting the text. The magic of determining which words to highlight is highly dependant upon the setup that the web site has. In the case of stompstompstomp.com, every footer on every page is served by a Python function. I added the following code to my footer:

import os, re, cgi

# Highlight google-ed for words!
if os.environ.has_key("HTTP_REFERER"):
    words = None

    m = re.search("google.[a-z.]+/search\\?(.*)", os.environ['HTTP_REFERER'])
    if m:
        googleQuery = cgi.parse_qs(m.group(1))
        words = ' '.join(googleQuery['q']).split(' ')
        words = filter(lambda x: x.find(":") == -1, words) # remove words like 'site:blahblah.com'

    if words != None:
        print "&lt;!-- Begin magical search term highlighting. --&gt;"
        print """&lt;script language="JavaScript" type="text/javascript" src="/stomphighlight.js"&gt;&lt;/script&gt;"""
        colors = ("#00eeee", "#eeee00", "#ee00ee", "#ee0000", "#00ee00", "#0000ee")
        print """&lt;script language="JavaScript" type="text/javascript"&gt;&lt;!--"""
        for i in range(len(words)):
            try:
                print "highlightWord(%r, %r, document.documentElement);" % (words[i], colors[i])
            except IndexError:
                pass
        print """//--&gt;&lt;/script&gt;"""
        print "&lt;!-- End magical search term highlighting. --&gt;"

Hopefully, it should be pretty clear how you could take this and use the same idea on your own web site, or modify my code to support more than just Google. Yay!

2003/02/24

Backup Script in Bash/Python

Filed under: programming,python — admin @ 8:34 pm

It’s been a while since I’ve written anything on ye olde technical weblog, but I thought that a code snippet I wrote a couple days ago might be generally useful. Have you ever had to do simple, incremental backups? I had to write a simple utility to backup a MySQL database server to a network drive. I wanted to do a backup every night, but also keep the daily backups for the past 7 days. Additionally, I thought it’d be nice to keep a copy for every month so I can go way back in time, if necessary.

My mind was in a Python frame today, and so I wrote the shell script partially in Python… in fact, shell variables are substituted into Python code, which is quite interesting:

#!/bin/sh

datestr=`date +%Y-%m-%d`
filename='mysql_database'
netwerk='/netwerk'
directory='${netwerk}/BlahBlah Backup'

mount "${netwerk}"

/usr/bin/mysqldump -u backup_user --databases mysql db1 db2 \
        &gt; "${directory}/${filename}-${datestr}.sql"

python &lt;&lt;END_PYTHON
import os
import re
logfiles = [x for x in os.listdir("${directory}"} \
        if re.search("${filename}-[0-9]{4}-[0-9]{2}-[0-9]{2}", x)]
# don't delete logfiles on the first of the month
deletablelogfiles = [x for x in logfiles \
        if not re.search("${filename}-[0-9]{4}-[0-9]{2}-01", x)]
deletablelogfiles.sort()
deletablelogfiles.reverse()
while len(deletablelogfiles) &gt; 7:
    file = deletablelogfiles.pop()
    print "Deleting old log file %s...\n" % (file,)
    os.remove("${directory}/%s" % (file,))
END_PYTHON

umount "${netwerk}"

There’s always something intrinsically cool about code writing code. This script seamlessly integrates shell scripting and Python. Shell scripting is useful for controlling and accessing systems. Python allows a more clear implementation of relatively complex operations.

2003/01/28

More Weblogging

Filed under: programming — admin @ 3:19 am

I restructured growlmurrdurr’s weblogging today to be a bit more sensical in directory usage, and exploit the power of Apache server config files more. Also, I added RSS feed capability into it. Wheeha. Still need to check all this into SVN, though.

2003/01/24

COMImageLib

Filed under: c++,programming — admin @ 8:14 pm

Once upon a time, in a galaxy far far away, I was writting a small piece of code to capture a screenshot in Windows. It wasn’t difficult to capture the screenshot, nor convert it to a device independant bitmap, nor output it as a .bmp file. But bmp files are huge, and these screenshots were going to be uploaded to a software issue tracking website. So, I decided to convert them to the much more compressable PNG image file format.

It would have been socially inacceptable for me to link libpng and zlib into the very low-level dll that this image capturing was being put into. I decided to make a COM module which would wrap libpng and zlib together. This way, the existence of the COM module could be detected and PNG files used, otherwise BMP files could be used as a last resort measure.

All this COM module can do is convert a BMP file to a PNG file. I hardly wrote any actual code, though. I yoinked the BMP -> PNG conversion from the web page of Jason Summers, and wrote a dead simple interface:

interface IPngImage : IDispatch
{
    [id(1), helpstring("method ConvertBMPToPNG")]
        HRESULT ConvertBMPToPNG([in] BSTR bsBMP, [in] BSTR bsPNG);
};

Happily this was all I needed. However, if you need something different, this COM module provides a nice framework for doing any work with PNG files since it statically links with pre-built libpng and zlib libraries (included in the source).

Source, Binary

Dictionary String Sorting

Filed under: c++,programming — admin @ 6:51 pm

The following two functions define a method of comparing two strings. The method is similar to that implemented by strcmp, but differs in two ways. Whitespace within the strings is ignored, and numbers are compared numerically rather than character by character. ’10′ is greater than ’5′, for example.

/**
 * Determines whether a character is whitespace or not.
 */
int iswhitespace(char x)
{
    return x == ' ' || x == '\\n' || x == '\\r' || x == '\\t';
}

/**
 * qsort comparison function for 'dictionary' like string sorting.  Differs
 * from strcmp in two important ways.  1) "Baby Zoe" comes after "Babyface",
 * whitespace is ignored.  2) "5" comes before "10", numbers are sorted
 * numerically.
 *
 * @param       pStr1   Sort parameter 1. (ptr to char)
 * @param       pStr2   Sort parameter 2. (ptr to char)
 * @return      {-1, 0, 1} if pStr1 is {less than, equal to, greather than}
 *              pStr2.
 */
int dictionarySort(void* pStr1, void* pStr2)
{
    char* a = pStr1;
    char* b = pStr2;

    for (;;)
    {
        if (*a == *b && *a == '\\0')
            return 0;
        else if (*a == '\\0')
            return -1;
        else if (*b == '\\0')
            return 1;

        while (iswhitespace(*a) && *a != '\\0') a++;
        while (iswhitespace(*b) && *b != '\\0') b++;

        if (*a >= '0' && *a <= '9' &&
            *b >= '0' && *b <= '9')
        {
            /* a and b look to be pointing towards the start of numeric
             * strings! :)
             */
            int nNumericA = atoi(a);
            int nNumericB = atoi(b);

            if (nNumericA < nNumericB)
                return -1;
            else if (nNumericA > nNumericB)
                return 1;

            while (*a >= '0' && *a <= '9' && *a != '\\0') a++;
            while (*b >= '0' && *b <= '9' && *b != '\\0') b++;
        }

        if (*a < *b)
            return -1;
        else if (*a > *b)
            return 1;

        a++;
        b++;
    }

    /* shut off compiler warnings. */
    return 0;
}

2002/11/28

XPCOM Enabled MOO

Filed under: moo,programming — admin @ 6:22 pm

On April 29th, 2002, I began to look at integrating the standalone XPCOM with the LambdaMOO server.

XPCOM is a lightweight cross-platform implementation of the concepts behind Microsoft’s COM. It has been implemented in the process of the development of Mozilla. XPCOM is essentially COM, but cross-platform, hence the XP.

The integration inside the MOO server is not necessarily for any specific purpose, but I have a few plans in mind for what this integration will permit:

  • XPCOM can be used to allow builtin functions to be compiled into shared modules and dropped into the MOO ‘components’. These builtin functions will be registered with the ‘regxpcom’ program, and be accessible from inside the MOO instantly. There are multiple advantages over doing this rather than the traditional way of modifying the MOO server to add builtin functions. The most major advantage is that XPCOM components can be written in languages other than C. It’s useful to program in an object-oriented language like C++ when you’re programming a builtin function that will be used in an object-oriented environment in the first place. Additionally, plug-in DLLs can be isolated in such a way that the crash of a builtin function will not affect the running of the MOO server.

    This has been implemented and is currently undergoing testing. The implementation involved the creation of an ‘IBuiltinFunction’ interface. The plug-in DLL exports an IBuiltinFunction derived interface. The MOO server allows access to its data to the XPCOM component via a set of ‘IMOOData’-derived interfaces.

    structure diagram of xpcom interfaces in the moo

  • Another possible use for XPCOM inside the MOO is to allow MOO-code to access XPCOM objects. I expect that originally this will be implemented by creating an XPCOM plug-in DLL that exports builtin functions providing xpcom_createinstance(), xpcom_queryinterface(), xpcom_callfunction(), xpcom_get_attribute(), xpcom_set_attribute(), xpcom_releaseinstance(), and xpcom_isvalidinstance().

    xpcom_createinstance() will create an instance of a given contract id, if possible, and return a hash string that represents that instance. Eg:

    ;xpcom_createinstance("@mozilla.org/file/directory_service;1")
    => "##XPCOM#bbf8cab0-d43a-11d3-8cc2-00609792278c#59e7e77a##"

    The string representing that interface is a concationation of the CID, and a pointer address to it to make it unique for each instance. A hash table of these strings will be stored in the server so that implementation of the other xpcom_*() functions is relatively efficient.

    Implementation of this idea is pending further thought.

  • The MOOzilla MOO client is written in JavaScript using Mozilla, and utilizes XPCOM heavily. It should be possible to create a plug-in architecture for MOOzilla, using some kind of common interfaces that the MOO server is aware of. Using an implementation of SOAP, (implemented as an XPCOM module, of course), it should be possible for the MOO server to ‘script’ MOOzilla, or vice-versa, using out-of-band binary SOAP communication streams. Woo! The purpose? … Well, why not create an XPCOM module that wraps Gtk+, and then an interface for it on the client side and an interface for message handling on the server side could allow a Gtk+ application to be MOO-server-driven. Of course, this doesn’t apply just to Gtk+, it could be used for any toolkit… theoretically.

My efforts of XPCOM integration have yield a MOO which can successfully use XPCOM component builtin functions. My work has been based upon the MOO Canada server source, which is a rather hacked up LambdaMOO 1.8.1. The source has some unfortunate usability problems. It is basically customised just for my building and use of it. It should be possible to adapt it to build on your machine. The Makefile-xpcom-lao file is my Makefile. The automake and autoconf sources are not up to date. With some modifications to the Makefile, it should be possible to get it to build for you. Currently, I don’t have any example modules that are fit for publication.

The XPCOM enabled MOO server source can also be accessed by Subversion clients, or browsed with a web browser, at http://stompstompstomp.com/svn/moo/mc/xpcom-enabled-branch.

XML, XPath, Weblog

Filed under: programming — admin @ 5:02 pm

Reworked weblog-growlmurrdurr to use XPath for finding XML nodes inside the entry data files. It reduced the uglyness of much of the comment related code. Also moved the source into the subversion repository.

Will probably do the same kind of modifications to the picture system later today. I’ve been alternating between playing with XML and working on the MOOzilla documentation. The two projects have collided in such a way that the MOOzilla documentation is all being written in docbook format now, and I’m developing tools of my own to translate it into HTML. It’s being fun. It’s probably slowing down writing the documentation, but I’m not exactly on a fixed schedule.

« Newer PostsOlder Posts »

Powered by WordPress