1

I don't know what to make of this. If I ask my shell (bash) to launch python without giving it an exact path, it somehow launches the wrong program (or possibly the right program, but with the wrong dylibs loaded). Yet, the output of which python and /usr/bin/env python both look just fine.

Here's an illustration of what I'm talking about:

OK, first of all, where is my python command coming from? Let's follow the symlinks. (BTW, Too bad readlink -f and realpath don't exist on OSX...)

$ which python
/usr/local/bin/python
$ ls -l /usr/local/bin/python
lrwxr-xr-x  1 root  wheel  68 Jan 18 11:44 /usr/local/bin/python@ -> ../../../Library/Frameworks/Python.framework/Versions/2.7/bin/python
$ ls -l /Library/Frameworks/Python.framework/Versions/2.7/bin/python
lrwxr-xr-x  1 root  admin  7 Jan 18 11:44 /Library/Frameworks/Python.framework/Versions/2.7/bin/python@ -> python2
$ ls -l /Library/Frameworks/Python.framework/Versions/2.7/bin/python2
lrwxr-xr-x  1 root  admin  9 Jan 18 11:44 /Library/Frameworks/Python.framework/Versions/2.7/bin/python2@ -> python2.7
$ ls -l /Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7
-rwxrwxr-x  1 root  admin  25624 Dec  5 15:57 /Library/Frameworks/Python.framework/Versions/2.7/bin/python2.7*

If I call /usr/local/bin/python, where does python itself think it is located? And what version is it?

$ /usr/local/bin/python -c "import sys; print sys.executable; print sys.version_info"
/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
sys.version_info(major=2, minor=7, micro=11, releaselevel='final', serial=0)

OK, looks legit. Instead of typing /usr/local/bin/python, what if I let /usr/bin/env locate python for me?

$ /usr/bin/env python -c "import sys; print sys.executable; print sys.version_info"
/Library/Frameworks/Python.framework/Versions/2.7/Resources/Python.app/Contents/MacOS/Python
sys.version_info(major=2, minor=7, micro=11, releaselevel='final', serial=0)

Same thing. No surprises so far. OK, what if I just type python? That should be the same as /usr/bin/env python, right?

$ python -c "import sys; print sys.executable; print sys.version_info"
/usr/local/bin/python
sys.version_info(major=2, minor=7, micro=10, releaselevel='final', serial=0)

Wait, what? sys.executable points to the symlink, and the version is wrong. What's going on?

Why would typing python be different in any way from typing /usr/local/bin/python?

BTW:

  • I double-checked my bash aliases, and python is not one of them. (alias | grep python)
Stuart Berg
  • 17,026
  • 12
  • 67
  • 99

2 Answers2

2

When you run program from $PATH ( so, like $ python ) then it's argv[0] is set to just it's name ( eg. python ), not full path. And python 2.7 version of sys.executable looks at $PATH if argv[0] doesn't have backslash on beggining ( so, eg. when you run it like in last example ). And it seems that it's not resolving symlinks, just printing first found path.

As we can see in python's code, on OS X it uses NSGetExecutablePath to itself in $PATH. And on Apple's site they say that NSGetExecutablePath

That is, the path may be a symbolic link and not the real file.

Community
  • 1
  • 1
Marqin
  • 1,096
  • 8
  • 17
  • 1
    Thanks for taking a look. I think you're on the right track. As tripleee's answer points out, I needed to run `hash -r python` to reconfigure the shell's path to python. (I had just installed a new version.) I think bash was still launching python from old location, but that location wasn't reflected in python's `sys.argv[0]`. Therefore, `python` itself was using a bogus value for `sys.executable`. Obviously, that breaks everything. – Stuart Berg Jan 18 '16 at 18:24
  • BTW, thanks for pointing to the relevant line in the python source. If that code is still the same in Python 3, it sounds like someone needs to submit a patch that check's the shell `hash` table instead of just searching `PATH`. – Stuart Berg Jan 18 '16 at 18:43
2

Bash keeps a cached lookup table in memory, which you can examine with hash. If you add a program to your PATH with the same name as a program you have used before at a different location, bash will still remember the old location. env obviously bypasses this cache; or you can remove it with hash -r python.

Also note that which is nonportable and often not the correct tool to use; type is specified by POSIX, and is built-in to any modern shell.

tripleee
  • 175,061
  • 34
  • 275
  • 318