'what shebang to use for python scripts run under a pyenv virtualenv
When a python script is supposed to be run from a pyenv virtualenv what is the correct shebang for the file?
As an example test case, the default python on my system (OSX) does not have pandas installed. The pyenv virtualenv venv_name does. I tried getting the path of the python executable from the virtualenv.
$ pyenv activate venv_name
(venv_name)$ which python
/Users/username/.pyenv/shims/python
So I made my example script.py:
#!/Users/username/.pyenv/shims/python
import pandas as pd
print 'success'
But when I tried running the script, I got an error:
(venv_name) $ ./script.py
./script.py: line 2: import: command not found
./script.py: line 3: print: command not found
Although running that path on the command line works fine:
(venv_name) $ /Users/username/.pyenv/shims/python script.py
success
(venv_name) $ python script.py # also works
success
What is the proper shebang for this? Ideally, I want something generic so that it will point at the python of whatever my current venv is.
Solution 1:[1]
As you expected, you should be able to use the full path to the virtual environment's python in the shebang to choose/control the environment the script runs in regardless of the environment of the controlling script.
In the comments on your question, VPfB & you find that the /Users/username/.pyenv/shims/python is a shell script that does an exec $pyenv_python. You should be able to echo $pyenv_python to determine the real python and use that as your shebang.
Try pyenv virtualenvs to find a list of virtual environment directories.
And then you might find a using shebang something like this:
#!/Users/username/.pyenv/python/versions/venv_name/bin/python
import pandas as pd
print 'success'
... will enable the script to work using the chosen virtual environment in other (virtual or not) environments:
(venv_name) $ ./script.py
success
(venv_name) $ pyenv activate non_pandas_venv
(non_pandas_venv) $ ./script.py
success
(non_pandas_venv) $ . deactivate
$ ./script.py
success
$
The trick is that if you call out the virtual environment's python binary specifically, python looks around that binary's path location for the supporting files and ends up using the surrounding virtual environment. (See per How does virtualenv work? )
Solution 2:[2]
If you need to use more shell than you can put in the #! shebang line, you can start the file with a simple shell script which launches Python on the same file.
#!/bin/bash
"exec" "pyenv" "exec" "python" "$0" "$@"
# the rest of your Python script can be written below
Because of the quoting, Python doesn't execute the first line, and instead joins the strings together for the module docstring... which effectively ignores it.
You can see more here.
Solution 3:[3]
It's not exactly answering the Q, but this suggestion by ephiement I think is a much better way to do what you want. I've elaborated a bit and added some more of an explanation as to how this works and how you can dynamically select the python to use:
#!/bin/sh
#
# Choose the python we need. Explanation:
# a) '''\' translates to \ in shell, and starts a python multi-line string
# b) "" strings are treated as string concat by python, shell ignores them
# c) "true" command ignores its arguments
# c) exit before the ending ''' so the shell reads no further
# d) reset set docstrings to ignore the multiline comment code
#
"true" '''\'
PREFERRED_PYTHON=/Library/Frameworks/Python.framework/Versions/2.7/bin/python
ALTERNATIVE_PYTHON=/Library/Frameworks/Python.framework/Versions/3.6/bin/python3
FALLBACK_PYTHON=python3
if [ -x $PREFERRED_PYTHON ]; then
echo Using preferred python $ALTERNATIVE_PYTHON
exec $PREFERRED_PYTHON "$0" "$@"
elif [ -x $ALTERNATIVE_PYTHON ]; then
echo Using alternative python $ALTERNATIVE_PYTHON
exec $ALTERNATIVE_PYTHON "$0" "$@"
else
echo Using fallback python $FALLBACK_PYTHON
exec python3 "$0" "$@"
fi
exit 127
'''
__doc__ = """What this file does"""
print(__doc__)
import platform
print(platform.python_version())
Solution 4:[4]
To expand to this answer, yes in 99% of case if you have a python in your environment you can just use:
#!/usr/bin/env python
However for a custom venv on linux following the same syntax did not work for me since the venv created a link to the python interpreter which the venv was created from so I had to do the following:
#!/path/to/the/venv/bin/python
Essentially however you are able to call python on your terminal is what you would put after #!.
Solution 5:[5]
if you want just a single script with a simple selection of your pyenv virtualenv you may use a bash script with your source as 'here document' as follows:
#!/bin/bash
PYENV_VERSION=<your_pyenv_virtualenv_name> python - $@ <<EOF
import sys
print(sys.argv)
exit
EOF
I did some additional testing. The following works too:
#!/usr/bin/env -S PYENV_VERSION=<virtual_env_name> python
Solution 6:[6]
/usr/bin/env python won't work, since it doesn't know about the virtual environment.
Assuming that you have main.py living next to a ./venv directory, you need to use Python from the venv directory. Or in other words, use this shebang:
#!venv/bin/python
Now you can do:
./main.py
Solution 7:[7]
maybe you need to check the file privileges:
sudo chmod +x script.py
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | ephemient |
| Solution 3 | Goblinhack |
| Solution 4 | PydPiper |
| Solution 5 | |
| Solution 6 | Jameson |
| Solution 7 | sadkeanu |
