The Keyboard Maestro scripting environment
March 1, 2017 at 8:10 PM by Dr. Drang
Friend of the blog Jason Verly was up late a couple of nights ago, trying to get either SnapClip or SnapSCP working. The embedded Python script was crapping out very early, throwing an error when it tried to run the
import Pashua
line. Jason knew damned well he’d installed both the Pashua app and the Pashua Python module, but the script kept failing. Even more frustrating was that he could run a script from the Terminal with the import Pashua
line and it would work just fine.
After a bit of sleep—which often helps—Jason realized that the problem arose because he had two Pythons installed on his computer, the stock one from Apple and another installed via Homebrew. His usual shell environment, which ran under Terminal, was set up to run the Homebrew Python in /usr/local/bin
by default, and when he installed the Pashua module, it was put in /usr/local/lib/python2.7/site-packages
, which is where the Homebrew Python can find it but not where the stock Python can find it. And Keyboard Maestro was running the stock Python when the macro was invoked.
The explanation can be found in the Keyboard Maestro documentation on scripting:
Shell scripts can execute any installed scripting language, such as perl, python, ruby or whatever. Be aware that because shell scripts are executed in a non-interactive shell, typically none of the shell configuration files (like .login or .profile) will be executed, which may change the way the shell script behaves.
Here’s an example of how different the behavior can be. If I run
echo $PATH
from Terminal, I get this output:
/Users/drdrang/anaconda/bin:/usr/local/mysql/bin:
/usr/local/git/bin:/Users/drdrang/Dropbox/bin:
/opt/local/bin:/usr/local/mysql/bin:/usr/local/bin:
/usr/local/sbin:/usr/local/texlive/2010/bin/x86_64-darwin:
/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:
/usr/sbin:/sbin:/opt/X11/bin
which I’ve reformatted from one single line into a series of lines. PATH
is the environment variable that determines the directories your shell searches for executable files and the order in which they’re searched. So when I type python
at the command line in Terminal, the Python that gets executed is the one in /Users/drdrang/anaconda/bin
because that’s the first directory in the list that contains an executable named python
.
The PATH
is set through commands in any number of dotfiles that get run whenever I open a Terminal window. These include ~/.profile
, ~/.bash_profile
, ~/.bashrc
, and ~/.bash_login
. And those are only my personal configuration files. There are also system configuration files in /etc
. If you want to see how this mess works, I suggest you take a look at this post.
Now let’s set up a simple macro that runs the same echo $PATH
command, but from within Keyboard Maestro.
The output from this is quite sparse:
/usr/bin:/bin:/usr/sbin:/sbin
This is why Jason’s macros weren’t doing what he expected. They were running the stock Python because /usr/local/bin
isn’t in the PATH
under Keyboard Maestro.
The obvious solution to this, assuming you don’t want to run the stock Python, is to explicitly set the full path to the Python you want to run in the shebang line of the script, e.g.,
#!/usr/local/bin/python
I’ve always been happy to just run the stock Python inside Keyboard Maestro. This means any nonstandard libraries have to be installed via /usr/bin/pip
or
/usr/bin/python setup.py
so they go into /Library/Python/2.7/site-packages
where the stock Python can find them.
But what if I want Keyboard Maestro to run the same Anaconda Python that I typically run from the Terminal? In theory, I could start my embedded scripts with
#!/Users/drdrang/anaconda/bin/python
Unfortunately, I like to have a common set of Keyboard Maestro macros shared between my iMac at work and my MacBook Air. And because of an historical fluke, I use different user names on the two machines. So while the shebang line above would work on my MacBook Air, it would have to be
#!/Users/realname/anaconda/bin/python
on the iMac. But because they’re synced, the macros have to be the same on both machines.
The solution is to use the env
command in the shebang line. From the man page:
The env utility executes another utility after modifying the environment as specified on the command line.
Modifying the Keyboard Maestro environment is exactly what we want to do. We can use env
’s -S
and -P
options to set the PATH
for the execution of python this way:
#!/usr/bin/env -S -P${HOME}/anaconda/bin python
The -S
option allows the HOME
environment variable to be interpreted, and the -P
option sets the PATH
. The PATH will therefore be either
/Users/realname/anaconda/bin
or
/Users/drdrang/anaconda/bin
depending on which computer I’m at.
Thanks to Jason for pointing out this tricky business and for coming up with the simpler solution. He is, I believe, is too smart to have different user names on different computers.
Update 03/2/2017 9:52 PM
A few things you should read if you’re interested in Keyboard Maestro and environments in general:
- Jason’s written a more thorough description of the trouble he ran into and how he fixed it.
- You can set the
PATH
Keyboard Maestro uses to anything you like by going into KM’s Preferences and doing a little editing. Thanks to Peter Lewis (who’s kind of an expert on KM) for the tip. - For a more complete discussion of the crazy, nutso system of setting the environment on a Mac, see this post by Rob Wells.