Reputation: 2286
Is there any way to execute (and obtain the results of) AppleScript code from python without using the osascript
command-line utility or appscript (which I don't really want to use (I think?) because it's no longer developed/supported/recommended)?
Rationale: in another question I've just posted, I describe a strange/undesired behaviour I'm experiencing with running some AppleScript via osascript
. As I'm actually calling it from a python script, I wondered if there was a way to route around osascript
altogether, since that seems to be where the problem lies - but appscript (the obvious choice?) looks risky now...
Upvotes: 18
Views: 12666
Reputation: 2155
If are looking to execute "Javascript for Automation" (applescript's successor) in your python code, here's how to do it:
script = None
def compileScript():
from OSAKit import OSAScript, OSALanguage
scriptPath = "path/to/file.jxa"
scriptContents = open(scriptPath, mode="r").read()
javascriptLanguage = OSALanguage.languageForName_("JavaScript")
script = OSAScript.alloc().initWithSource_language_(scriptContents, javascriptLanguage)
(success, err) = script.compileAndReturnError_(None)
# should only occur if jxa is incorrectly written
if not success:
raise Exception("error compiling jxa script")
return script
def execute():
# use a global variable to cache the compiled script for performance
global script
if not script:
script = compileScript()
(result, err) = script.executeAndReturnError_(None)
if err:
# example error structure:
# {
# NSLocalizedDescription = "Error: Error: Can't get object.";
# NSLocalizedFailureReason = "Error: Error: Can't get object.";
# OSAScriptErrorBriefMessageKey = "Error: Error: Can't get object.";
# OSAScriptErrorMessageKey = "Error: Error: Can't get object.";
# OSAScriptErrorNumberKey = "-1728";
# OSAScriptErrorRangeKey = "NSRange: {0, 0}";
# }
raise Exception("jxa error: {}".format(err["NSLocalizedDescription"]))
# assumes your jxa script returns JSON
return json.loads(result.stringValue())
Upvotes: 2
Reputation: 90521
You can use the PyObjC bridge:
>>> from Foundation import *
>>> s = NSAppleScript.alloc().initWithSource_("tell app \"Finder\" to activate")
>>> s.executeAndReturnError_(None)
Upvotes: 25
Reputation: 3259
PyPI is your friend...
http://pypi.python.org/pypi/py-applescript
Example:
import applescript
scpt = applescript.AppleScript('''
on run {arg1, arg2}
say arg1 & " " & arg2
end run
on foo()
return "bar"
end foo
on Baz(x, y)
return x * y
end bar
''')
print(scpt.run('Hello', 'World')) #-> None
print(scpt.call('foo')) #-> "bar"
print(scpt.call('Baz', 3, 5)) #-> 15
Upvotes: 24