Using Python and AppleScript Together
Pages: 1, 2
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 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 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 3: Automatic Login Enabled
Step 8: Create a login item for your profile.
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.
Showing messages 1 through 17 of 17.
-
Better way to call Python from Applescript?
2007-05-11 08:20:35 msocias [Reply | View]
-
Better way to call Python from Applescript?
2007-05-11 10:07:34 Noah Gift |
[Reply | View]
Yes. "do shell script" is a great way to call python from applescript. I have an article I may write about writing a Cocoa GUI with just Applescript Studio and python. I wrote a decent Cocoa Application using just that!
If your interested let me know...
-
Better way to call Python from Applescript?
2007-05-13 17:47:43 msocias [Reply | View]
Hi,
Yes, I am very interested in that article. I think it would be a great way to put a Cocoa GUI on top of Pyhton.
I have also heard that the scripting to framework library in Leopard will allow writing Pyhton apps with the Cocoa GUI.
Hopefully you write that article.
Thanks,
..m
-
Doesn't work
2007-05-10 17:34:07 bfr [Reply | View]
Tried you ose example. Result: command not found. You must be leaving something out. -
Doesn't work
2007-05-12 15:55:55 Noah Gift |
[Reply | View]
My first guess would be the escaping somehow did not translate correctly for you. Try this:
osascript -e 'tell app "Script Editor" to quit'
If that works than you can break each piece of the command into a variable, test it by doing an echo $variable_name(name of your variable) one by one and then concatenate them together when you know the individual variables work.
Another option would be to do this in python and just call a python script, which I in fact do quite a bit:
An idea:
import os
ls = 'ls -l '
dir = '/home'
cmd = ls + dir
def long_list():
os.system(cmd)
long_list()
This type of format is a good way to wrap up shell command into python. Hope that helps!
-
AppleScript limitations
2007-05-10 10:14:50 salamon [Reply | View]
AppleScript has a number of limitations when run remotely, via the command line. For example, I logged into my home machine and tried the sleep command and here's what I got:
Ripley:~ andrew$ osascript -e 'tell app "Finder" to sleep'
kCGErrorRangeCheck : Window Server communications from outside of session allowed for root and console user only
INIT_Processeses(), could not establish the default connection to the WindowServer.Abort trap
If i try to run it as root, as the error message suggests, I get:
21:26: execution error: Finder got an error: Application isn't running. (-600)
I get the same errors when running your python version.
There is a small app called SleepNow (see http://sleepnow.darwinports.com/) that will work under those conditions. -
AppleScript limitations
2007-05-10 10:26:37 Noah Gift |
[Reply | View]
There is actually a sneeky little way around this. The apple mail program will let you execute applescript on the receipt of a certain type of mail message that you define.
You could always craft a very comprehensive filter that accepts only email messages from a certain address with a certain keyphrase in the the subject line. You could then trigger the sleep code...
One other thing that works remotely as root:
killall loginwindow
This will immediately log you out. Be kind with this command!
-
A generic problem with this
2007-05-10 09:29:03 jimschimpf [Reply | View]
I've read a number of articles about using Applescript with various languages, your article and articles using Ruby but none of these ever work on my box (1Ghz 12" Powerbook with 10.4.9). There is something wrong with osascript on my box, if I attempt something simple like
osascript -e "tell app TextEdit to quit"
I get:
2007-05-10 12:19:44.573 osascript[26045] CFLog (21): Error loading /System/Library/Components/AppleScript.component/Contents/MacOS/AppleScript: error code 4, error number 0 (Symbol not found: __cg_png_destroy_write_struct
Referenced from: /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks/ImageIO.framework/Versions/A/ImageIO
Expected in: /usr/local/lib/libPng.dylib
)
osascript: can't open default scripting component.
What have I broken and how could I fix this ?
--jim
-
A generic problem with this
2007-05-11 18:41:43 BareFeet [Reply | View]
You need to instead write:
osascript -e 'tell app "TextEdit" to quit' -
A generic problem with this
2007-05-12 15:27:11 Noah Gift |
[Reply | View]
That is the correct way to escape from the command line. Good comment. Your way might be a little less confusing for people than my way as well.
Of course the poster above has two problems which is the worst kind. A bad installation possibly, plus, the wrong quoting(syntax).
-
A generic problem with this
2007-05-10 09:55:02 Noah Gift |
[Reply | View]
Sometimes things like this can be a real pain to debug. I have gotten so used to reimaging my machine that if it takes more than 10 minutes...I use the asr tool to reimage my Operating System from an image I keep on a second partition.
I have my User's folder symbolically linked to the second hard drive so I can easily erase my main partition by booting into the second partition.
I have some example code that is more geared toward doing this from a netboot image, but you could implement this on a second partition:
http://smartosxdeploy.googlecode.com/svn/trunk/nuke.py
-
Shorter alias
2007-05-10 08:48:18 ulrichp [Reply | View]
Instead of
alias ose='open /Applications/AppleScript/Script\ Editor.app/'
Wouldn't it be easier to use
alias ose='open -a "Script Editor"'
Then it doesn't really matter where Script Editor is located at; I'm pretty sure 'open -a' works on stuff not directly in the root of the /Applications folder.
Granted, it's probably six of one, half dozen of another, since I doubt Apple's likely to move the Script Editor anytime soon. -
Shorter alias
2007-05-10 09:57:22 Noah Gift |
[Reply | View]
I have been abducted by python brainwashing experts and now I always am forced to use the zen of python in everything I do.
I personally like to be explicit as much as possible, but I also like the short cut way too. -
the zen of python...
2007-05-11 06:50:44 airdrummer [Reply | View]
also requires the reduction of redundancy: instead of repeating
app(u'/Applications/DiskWarrior.app')...
u should declare a variable:
dw=app('DiskWarrior')
dw.activate(timeout=3600)
dw.disk['main'].rebuild(replacing=k.yes_,
timeout=3600)
dw.quit(timeout=3600)






In the first page you mention that:
"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."
But then you go on describing how you can call Applescript from Python. Are there any better ways to call Python from Applescript than the one you mentioned (i.e., via "do shell script "python ... ?
Thanks,
..m