Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does sys.executable determine the interpreter path?

I've installed python on mac with homebrew. Some tools (pipenv for example) have some troubles because they try to write to /lib/python3.7/site-packages/, not permitted under mac. After some investigation I found that they start a new python interpreter found in sys.executable that is effectively inconsistent with the python path installed by homebrew.

$ which python
/usr/local/bin/python
$ python -c "import sys; print(sys.executable)"
/usr/local/opt/python/bin/python3.7

I would expect that these path points to the same binary, why is it not so? How can I control sys.executable?

like image 233
MrTJ Avatar asked Nov 07 '18 12:11

MrTJ


People also ask

What is sys path in Python?

sys.path is a built-in variable within the sys module. It contains a list of directories that the interpreter will search in for the required module. Attention geek!

How do I open the Python interpreter in Windows?

To open the Python interpreter, installed in the system, search in the Start menu. Then click on Python 3.9 or other, depending on the installed version. In Windows, it looks like Command Prompt.

How do I specify a path for the interpreter?

There are three ways to specify a path : DEFAULT- By default, the interpreter looks for a module within the current directory. To make the interpreter search in some other directory you just simply have to change the current directory. The following example depicts a default path taken by the interpreter:

How to find the version of the Python interpreter in Python?

As we know, before using functions and variables provided by any module we need to import it using import keyword. This returns a string that gives information about the version of the Python interpreter, the Build no. and the compiler used. This will return a tuple that contains the information about the Python Version only.


1 Answers

It is determined at runtime (by the calculate_program_full_path() function in Module/getpath.c, to be exact. It is usually based of the argv[0] value that the OS passed in.

You can set an alternative value by setting the PYTHONEXECUTABLE environment variable.

However, on homebrew builds, a bit more is going on. Homebrew forces the issue and sets sys.executable directly in a sitecustomize.py module generated at install time:

$ tail -n2 /usr/local/Cellar/python/3.7.0/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/sitecustomize.py
    # Set the sys.executable to use the opt_prefix
    sys.executable = '/usr/local/opt/python/bin/python3.7'

This blithely ignores PYTHONEXECUTABLE even if set.

So what is going on, why does homebrew clobber sys.executable?

Homebrew Python is a MacOS framework build, so you can run GUI apps with this Python binary. A binary inside a framework bundle is placed under very strict requirements by Apple as to what you can do with it, including what the executable name is allowed to be set to. To work around these the framework binary is actually a wrapper ) that translates takes to a better path, sets the __PYVENV_LAUNCHER__ environment variable and launches the actual python binary located at Resources/Python.app/Contents/MacOS/Python, which then uses the __PYVENV_LAUNCHER__ environment variable to inform sys.executable.

The path the wrapper sets has any symlinks in the directory name resolved. Since homebrew makes /usr/local/opt/python a symlink to a specific Python bottle directory, running /usr/local/opt/python/bin/python3 results in sys.executable being set to the linked bottle path:

$ /usr/local/opt/python/bin/python3 -S -c 'import sys; print(sys.executable)'
/usr/local/Cellar/python/3.7.0/bin/python3

which defeats the purpose of the symlink, and can lead to broken pip-installed scripts each time homebrew makes a minor version update to the Python formula.

I'd like for homebrew to at least check if PYTHONEXECUTABLE is set here. You can force the issue yourself by just setting sys.executable directly:

import os, sys

if 'PYTHONEXECUTABLE' in os.environ and :
    sys.executable = os.environ['PYTHONEXECUTABLE']

I've opened a report to request the homebrew Python formula checks for PYTHONEXECUTABLE and included a suggested fix. The fix was landed on November 28, 2018, so just an update of your Python packages should get you the new version and make Homebrew Python honour PYTHONEXECUTABLE once again.

like image 154
Martijn Pieters Avatar answered Nov 04 '22 21:11

Martijn Pieters