what shebang to use for python scripts run under a pyenv virtualenv

Viewed 51293

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.

8 Answers

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())

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 #!.

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

/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

maybe you need to check the file privileges:

sudo chmod +x script.py
Related