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 eitherSnapClip orSnapSCP working. The embedded python script was crapping out very early, throwing an error when it tried to run the
import Pashualine. 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 $PATHfrom 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/binwhich 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:/sbinThis is why Jason’s macros weren’t doing what he expected. They were running the stock Python because /usr/local/bin is 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/pythonI’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.pyso 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/pythonUnfortunately, 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/pythonon 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 pythonThe -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/binor
/Users/drdrang/anaconda/bindepending 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.