MacDevCenter    
 Published on MacDevCenter (http://www.macdevcenter.com/)
 See this if you're having trouble printing code examples


Using Python and AppleScript Together

by Noah Gift
05/10/2007

Introduction

Applescript and Python are quite different, but can be used together to make very efficient tools without compromising the modularity of Python. This is a quick introduction to the major concepts with some real examples. I will also briefly cover using bash aliases with your code to make it even more effective.

Before we can even start I need to introduce you to being lazy with bash aliases and osascript, via the Script Editor application. The Script Editor is an IDE for AppleScript that works pretty well. But first, be lazy! Alias Script Editor to your .profile or .bashrc., here is an example of mine:

alias ose='open /Applications/AppleScript/Script\ Editor.app/'

(Note to add this to your .profile using vim: vim ~/.profile)

Now from the command line, in a new shell type in: (Command + N for you keyboard shortcut guys like me)

ose

Pretty cool right? Next, let's get even lazier. Let's write an embedded AppleScript to close Script Editor from bash and put it in your bash profile.

First, export two variables in ~/.profile:

export OSA='osascript -e '
export SE='"Script Editor"'

Next, alias a close command:

alias cse='$OSA "tell app $SE to quit"'

Try it out, open a new shell and open the Script Editor again:

ose

Now be lazy and close that shell from the terminal:

cse

Pretty nifty; but you might be wondering, "isn't this about Python and AppleScript?" We're getting there. Let's use these shortcuts to actually write a simple AppleScript with embedded Python.

Python and AppleScript

Here is our first example of calling Python from AppleScript:

Example 1: Call Python Command from AppleScript

do shell script "python -c 'print \"Hello World\"'"

(Note: \ is used for double quote escape)

Click run and you will see the expected ouput at the bottom of the window. Not a very exciting example, I hate hello world, but tradition, tradition...

It does bring up one of the limitations of this method. Python was not designed to be run from the command line like Perl. The only way to "trick" Python into executing big, complicated one liners is to import a module that will execute code upon import.

This is generally a bad idea, but I can show you one fun example:

Example 2: Using Python import Command from AppleScript

do shell script "python -c 'import this'"

(This is the famous "Zen of Python" by Tim Peters. I will supress the output for the sake of the article, but you will see the "Zen of Python")

So remember, this is one option to execute complex commands via the command line with Python, but it's generally a bad idea as there are better ways to use Python to solve your problems.

You might be thinking that if you can all Python from AppleScript, you can probably call AppleScript from Python. You would be correct in thinking this is possible.

First make a sandbox if you don't have one:

mkdir ~/sandbox
cd ~/sandbox

Example 3: Call AppleScript from Python with osascript

(You can cut and paste this into your favorite text editor. I like GVim quite a bit.)

#!/usr/bin/env python
#sleepy-mac.py
#makes my mac very sleepy

import os
cmd = """osascript -e 'tell app "Finder" to sleep'"""
def stupidtrick():
     os.system(cmd)
stupidtrick()

Now let's make it executable:

chmod +x ~/sandbox/sleepy-mac.py

And we can call this from the command line:

./sleepy-mac.py

This should have just put your Mac to sleep. Pretty cool, and it can actually be very useful. Let's alias this to your bash profile so whenever you feel like telling your Mac to sleep, you just type in a command. In ~/.profile put:

alias sleepymac='~/sandbox/sleepy-mac.py'

Now, whenever you feel the urge to put your Mac to sleep you can just type in sleepymac from the command line.

sleepymac

Here is another way to call AppleScript within Python code.

Example 4: Call AppleScript from Python with HEREDOC

import os

#An example of the HEREDOC method
cmd = """osascript<<END
tell application "iTunes"
play playlist "Party Shuffle"
end tell
END"""

def play_iTunes():
     os.system(cmd)

play_iTunes()

Notice that with osascript you can triple quote osascript and embed a huge complicated AppleScript. This can be a pretty effective way to use AppleScript in Python as you could even throw several osascripts into a list or dictionary and call them throughout a Python script.

Let's make this example a little more cool though. What if you just bought an expensive, fully loaded, MacBook Pro, you have no more money in the bank, and your alarm clock breaks. Well, you can write a script for your daily alarm clock. Here's how:

Step 1: Open up Automater and select "Run Shell Script"
Step 2: Select the Python shell.
Step 3: Paste in code from example 4.

figure

Figure 1: Running a shell script in Automator

Step 4: Press Run and test it out.
Step 5: Save it as an application called automater-itunes-pmix inside of your home directory sandbox.

That's all it takes to create a full application using Automator. Now let's be smart and think of how to we can reuse this application again for more than just an alarm clock. If you are thinking, "Let's make an alias to this in .profile," you're right!

I created another section just for automator applications:

### Automator-commands
alias pmix='open ~/sandbox/itunes-itunes-pmix.app/'

figure

Figure 2: bash Profile

Now if you type this in a new shell (remember every change to .profile requires a new shell to see the change):

pmix
alias pmix='open ~/sandbox/itunes-itunes-pmix.app/'

your Mac will automatically launch iTunes and play a party mix. Now we're talking...that is sweet!

Step 6: Open up System Preferences-Energy Saver, and select the Schedule button. You will see a startup or wake at dialog. Select the time you want to wake up.

In order for your machine to act as an alarm clock you must do two other things.

Step 7: Turn on Autologin for you account. Go to System Preferences>Accounts>your username>login options.

figure

Figure 3: Automatic Login Enabled

Step 8: Create a login item for your profile.

figure

Figure 4: Login for Alarm Clock

Step 9: Test it! Shut down your computer and turn the power back on. You should have a party mix automatically play.

Step 10:; Test it again! Change the wake up/power on clock time to a few minutes in the future and turn your computer off.

Once that works, you should pat yourself on the back. You just built your own customized alarm clock and command party mix tool in one shot!

Another intersting way to interact with Python is to pass a variable from Python into AppleScript:

Type in a shell (remember you created the bash alias, if you're following along from home):

ose

Example 5: Pass a Python value to an AppleScript variable

This one line Python script will find your hostname and pass it to AppleScript, which will then create a pop-up dialog box with your hostname. Paste inside of the Script Editor and run:

set hostname to (do shell script "python -c 'from socket import gethostname; print gethostname()'")
display dialog "Your hostname is: " & hostname

Note: I borrowed this one liner from the python wiki.

So we have some fun, useable techniques, but I think in some cases there is a better way. It is time to introduce appscript.

What Is Appscript?

Appscript is a Python bridge to AppleScript. It translates the functionality of AppleScript into the Python language. Basically, it makes AppleScript Pythonic, by allowing it to execute as Python code and eliminating much of the verbosity of the language. Appscript, in turn, allows you interact with a higher level API to the Apple User Interface that would be possible from the python standard library. Finally, appscript is a viable alternative to AppleScript.

You can download appscript from http://appscript.sourceforge.net/

To use appscript you need to download appscript, HTMLDictionary, and ASTranslate. Appscript is the actual bridge from Python to AppleScript. HTMLDictionary formats AppleScript dictionaries to appscript documentation for that object which is quite handy for reference! ASTranslate will try to translate AppleScript code into an appscript version. The good news is that is mostly works.

Below is an example of an appscript I wrote:

Example 6: Fully scripting a third-arty application with appscript

#!/usr/bin/env pythonw
#Automates Diskwarrior Application through appscript

from appscript import *
import time

#A function to fully automate Diskwarrior Application
def diskwarrior():
    t0 = time.time()
    #Tells Diskwarrior to start with a timeout of 3600 sec.
    app(u'/Applications/DiskWarrior.app').activate(timeout=3600)
    #Tells diskwarrior to rebuild disk.
    app(u'/Applications/DiskWarrior.app').disk['main'].rebuild(replacing=k.yes_,   
        timeout=3600)
    #Tells Diskwarrior to quit
    app(u'/Applications/DiskWarrior.app').quit(timeout=3600)
    print "It took this many seconds to run Diskwarrior on your hard drive: \n"
    print time.time()-t0

diskwarrior()

Example 7: AppleScript code comparison of DiskWarrior script

with timeout of 3600 seconds
    tell application "DiskWarrior"
        activate
        rebuild disk "main" replacing yes
    end tell
    tell application "DiskWarrior"
        quit
    end tell
end timeout

Driving appscript with IPython

If you don't have IPython installed then you should do so now. You can download the source from http://ipython.scipy.org/moin/Download. Or you can do like I do and use easy_install, download the easy_install script from http://peak.telecommunity.com/DevCenter/EasyInstall

Then just type:

easy_install ipython

And from a shell type:

ipython

Now you are in IPython. At the IPython prompt, enter:

from appscript import *

Now you are ready to experiment with appscript using IPython. Let's start a slideshow:

app('iPhoto').start_slideshow()

We just launched a slideshow with one line of code. Touch a key on keyboard to come back to IPython.

That was fun. Now let's quit iPhoto. I am bored of seeing my vacation pics for the 1,200th time.

app('iPhoto').quit()

Poof! iPhoto is gone. Hmmm...what else can we play with? I always wonder what day of the week I was born on. I bet iCal can show me:

import datetime
app(u'/Applications/iCal.app').view_calendar(at=datetime.datetime(1975, 5, 21, 0, 0))

So I was born on a Wednesday in 1975.

AppleScript can be clumsy and I think appscript is a little better, but the best approach to solve problems may be to combine appscript or AppleScript within your Python scripts. Please watch out for AppleScript event timeouts, as they can kill a script you thought would work.

In addition, since you need to deal with AppleScript inside of Python, special thought needs to go into how you write programs, as they won't behave as you would expect regular Python code to behave, due to the nature of Apple events.

Summary

We made some fun toys using AppleScript, appscript, and Python. We now have a way to open and close applications from the shell by typing in a three character alias. We created a sneaky, sleepymac command-line tool. And we made a full-fledged alarm clock application using several of the techniques we learned. Finally, we got into IPython and appscript to really mix it up.

Python is very powerful and robust, so only use appscript or AppleScript when you have to. When you write AppleScript code, think of how to make it generic enough that you can reuse it and call if from a bash alias.

IPython is really fun to play with and very powerful. IPython and appscript are a good combination. I hope this article has given you some good ideas. Now go automate things!

Here is your final exam. Download PagePacker and use appscript to automate the printing of several .png files onto one page. I will post one example of how to do so later on my blog.

Noah Gift is the co-author of Python For Unix and Linux by O'Reilly. He is an author, speaker, consultant, and community leader, writing for publications such as IBM Developerworks, Red Hat Magazine, O'Reilly, and MacTech, and Manning.


Return to MacDevCenter.com.

Copyright © 2009 O'Reilly Media, Inc.