20

I am trying to embed an AppleScript in a Python script. I don't want to have to save the AppleScript as a file and then load it in my Python script. Is there a way to enter the AppleScript as a string in Python and have Python execute the AppleScript? Thanks a bunch.

Here is my script: import subprocess import re import os

def get_window_title():
    cmd = """osascript<<END
    tell application "System Events"
        set frontApp to name of first application process whose frontmost is true
    end tell
    tell application frontApp
        if the (count of windows) is not 0 then
            set window_name to name of front window
        end if
    end tell
    return window_name
    END"""

    p = subprocess.Popen(cmd, shell=True)
    p.terminate()
    return p

def get_class_name(input_str):
    re_expression = re.compile(r"(\w+)\.java")
    full_match = re_expression.search(input_str)
    class_name = full_match.group(1)
    return class_name

print get_window_title()
1

9 Answers 9

28

Use subprocess:

from subprocess import Popen, PIPE

scpt = '''
    on run {x, y}
        return x + y
    end run'''
args = ['2', '2']

p = Popen(['osascript', '-'] + args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate(scpt)
print(p.returncode, stdout, stderr)
Sign up to request clarification or add additional context in comments.

2 Comments

In python 3 this would not work, an additional parameter universal_newlines=True on the popen is needed. See example below stackoverflow.com/a/45133926/1534775
@gbonetti this is not necessarily true. In python 3 this can actually introduce more errors if your script text contains non-ascii characters. You might need to leave the universal_newlines off and encode the script as necessary. Ex. stdout, stderr = p.communicate(scpt.encode('utf-8')) and then stdout.decode('utf-8').
10

In python 3 it would be slightly different:

script = 'tell "some application" to do something'
p = Popen(['osascript', '-'], stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
stdout, stderr = p.communicate(script)

Popen now expects a byte-like object, to pass a string, the universal_newlines=True parameter is needed.

2 Comments

In python 3 this will fail if the script contains non-ascii characters. A better way would be to do universal_newlines=False and then encode the script and decode stdout as necessary.
Unfortunately this answer removes the "+ args" for no apparent reason. (a mistake?) Anyway, what worked for me was using code from user "Has"'s accepted answer, and just adding universal_newlines=True as last parameter in function call.
6

Example 3 in this article suggests:

#!/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()

These days, however, subsystem.Popen is usually preferred over os.system (the article is from three years ago, when nobody screamed on seeing an os.system call;-).

1 Comment

I looked at that link before, and I couldn't quite get my script working. I edited my question to include the script I am trying to perform. I think the error is that I can't retrieve the return value from the AppleScript. I looked into the Popen object, but I couldn't find anything to operate on return values.
4

Here's a simple python3 synchronous example, if you want your python code not to wait for Applescript to finish. In this example, both say commands are executed in parallel.

from subprocess import Popen

def exec_applescript(script):
    p = Popen(['osascript', '-e', script])

exec_applescript('say "I am singing la la la la" using "Alex" speaking rate 140 pitch 60')
exec_applescript('say "Still singing, hahaha" using "Alex" speaking rate 140 pitch 66')

Comments

4

subprocess.run() is now preferred over subprocess.popen(). Here is a pretty simple way to run AppleScript code and get the results back.

import subprocess

def get_window_title():
    cmd = """
        tell application "System Events"
            set frontApp to name of first application process whose frontmost is true
        end tell
        tell application frontApp
            if the (count of windows) is not 0 then
                set window_name to name of front window
            end if
        end tell
        return window_name
    """
    result = subprocess.run(['osascript', '-e', cmd], capture_output=True)
    return result.stdout

print(get_window_title())

1 Comment

Python 3.6 required the , capture_output=True portion to be removed from subrpocess.run for this to work
3

See https://pypi.org/project/applescript/

import applescript
resp = applescript.tell.app("System Events",'''
set frontApp to name of first application process whose frontmost is true
return "Done"
''')
assert resp.code == 0, resp.err
print(resp.out)

etc. Most of suggestions, including "applescript" I quoted, are missing one important setting to osascript -- setting an -s option to "s", otherwise you will be having difficulty parsing the output.

Comments

1

Rather than embedding AppleScript, I would instead use appscript. I've never used the Python version, but it was very nice in Ruby. And make sure that, if you're installing it on Snow Leopard, you have the latest version of XCode. However, I've so far been unable to install it on Snow Leopard. But I've only had Snow Leopard for ~1 day, so your mileage may vary.

4 Comments

Sorry to hear you're having problems; it should work (it does here). Have you tried posting on rb-appscript-discuss (rubyforge.org/mail/?group_id=2346) for help?
has: Thanks for the link, but I only installed Snow Leopard 1.5 days ago, and I spent today traveling; I haven't really tried very hard yet. Still, I'll check it out if I can't make it work; thanks again.
has: And indeed, all I needed to do was update XCode. Thanks again, though!
Glad you got it sorted. And yes, if the OP doesn't mind a third-party install, then py-appscript is a much nicer solution. e.g. This'll get the names of all windows in the frontmost process: app('System Events').application_processes[its.frontmost==True].first.windows.name()
1

You can use os.system:

import os
os.system('''
    osascript -e 
     '[{YOUR SCRIPT}]'
     '[{GOES HERE}]'
    ''')

or, as suggested by Alex Martelli you can use a variable:

import os
script = '''
    [{YOUR SCRIPT}]
    [{GOES HERE}]
'''
os.system('osascript -e ' + script)

Comments

1

Here's a generic function in python. Just pass your applescript code with/without args and get back the value as a string. Thanks to this answer.

from subprocess import Popen, PIPE

def run_this_scpt(scpt, args=[]):
    p = Popen(['osascript', '-'] + args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
    stdout, stderr = p.communicate(scpt)
    return stdout

#Example of how to run it.
run_this_scpt("""tell application "System Events" to keystroke "m" using {command down}""")

#Example of how to run with args.
run_this_scpt('''
    on run {x, y}
        return x + y
    end run''', ['2', '2'])

1 Comment

Python 3.6 runs without error if universal_newlines=True is added at the end of Popen parameters

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.