6

Background

I have some Python scripts which use libraries only available to Python 2.7 and so I want to be able to run those in Python version 2.7.

I was thinking that it would be great if I could put some code at the top of my Python file which would detect if it was being run in Python 3 and if so execute itself in version 2.7. Is this possible?

For example, the pseudo code would be:

if (self.getPythonVersion != 2.7):
    os.execute('python27 ' + os.cwd + 'script.py')
    exit()

Edit: This is for my personal use, not for distributing.

Answer

I used mgilson's answer below to get this to work for me. I was not able to get os.exec() to work, but I didn't spend a long time on that. The second script worked for me. Here is what I used and worked for me:

if sys.version_info[:2] > (2, 7):
    code = subprocess.call(['python27', sys.argv[0] ])
    raise SystemExit(code)
Klik
  • 1,757
  • 1
  • 21
  • 38
  • 2
    Why are you trying to do this? – Tadhg McDonald-Jensen Jul 12 '16 at 16:57
  • I have both python versions installed and some scripts use libraries only available to Python2.7 so I need to run those in Python 2.7 – Klik Jul 12 '16 at 17:00
  • 2
    then how are you running them now? usually I do `python2 script.py` to run my scripts so I don't see how this would be an issue. – Tadhg McDonald-Jensen Jul 12 '16 at 17:13
  • I use a .bat file to specify python27. I would like to make it able to be run just by double clicking. The reason is because I share these scripts with colleagues and it is preferable that they do not have to take any additional steps to get the script to run. – Klik Jul 12 '16 at 17:20
  • "share these scripts with colleagues" would be to some degree distributing, the default program that `.py` files are opened with are completely based on the settings of the computer they are being run on. Are these libraries you are using 3rd party? If so how are you certain that your collegues also have those libraries installed. If you want to make a "click to run" application then just pack it into a `.exe`. – Tadhg McDonald-Jensen Jul 12 '16 at 17:29
  • @TadhgMcDonald-Jensen Packing it into an `.exe` is a great idea, but also not ideal in my situation because those who I distribute this to would like access to the script. I was thinking about making a `.py2` extension for python27, but this method is by far the most preferable. – Klik Jul 12 '16 at 17:33
  • In any case, I am just endeavoring to make this more convenient for the guys I work with. – Klik Jul 12 '16 at 17:33

4 Answers4

5

Nope, this isn't possible for the simple reason that a user could have python3.x installed and not have python2.x installed.

If you know that they have python2.7 installed, then you can use something like your work-around above, however, in that case, you'll have to make sure that you can support both python3.x and python2.x in the same source (which is generally a non-trivial task)

You can detect the python version via sys.version_info and I think you can swap out the process using something in the os.exec* family of functions... e.g.:

import os, sys

if sys.version_info[:2] > (2, 7):
    os.execve('python27', sys.argv, os.environ)

Here's another variant that you can try (it'll create a new process instead of replacing the old one however):

import sys, subprocess

if sys.version_info[:2] > (2, 7):
    code = subprocess.call(['python27'] + sys.argv)
    raise SystemExit(code)

print(sys.version_info)
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • I'm not thinking about distributing this to other people though. This is just for my personal use and I do have both versions installed. `python27` is the name of my Python 2.7 executable in my PATH. – Klik Jul 12 '16 at 16:57
  • I'm getting an error saying that Python is crashing. I will play around and mark this as the answer if I can get it to work. – Klik Jul 12 '16 at 17:09
  • Got it to work. I had to use the second solution, the first method of using os.execve (or execlp/execvp) would not work or it would cause Python to crash. Anyway, this is great. Thanks! I'll post the script I used in my question so others can see. – Klik Jul 12 '16 at 17:29
5

You can try adding the python2.7 shebang line at the top of your script:

#!/usr/bin/env python2.7

Make sure it is in your path though, and this should work.

ifma
  • 3,673
  • 4
  • 26
  • 38
  • 2
    This only means that when executed directly it will use python 2.7, it doesn't stop users from running with python 3 directly. – Tadhg McDonald-Jensen Jul 12 '16 at 16:58
  • @TadhgMcDonald-Jensen I see, thank you for this useful piece of information. – ifma Jul 12 '16 at 16:58
  • I think that it is more reliable to look in `/usr/bin/env` -- `/bin/env` doesn't exist for me (OS-X El-Capitan). – mgilson Jul 12 '16 at 17:10
  • Is there an equivalent for Windows? I didn't specify the OS I'm using in my question; sadly, it's not Linux :P. – Klik Jul 12 '16 at 20:52
  • 1
    @Klik unless you are running Cygwin on Windows, no there is not. See http://stackoverflow.com/questions/7574453/shebang-notation-python-scripts-on-windows-and-linux – ifma Jul 12 '16 at 20:59
0

This ugly hack should work on most UNIX-like systems exec-magic. It relies on the triple-quote handling difference between python and sh. First sh runs the script, then reruns the script with a suitable python binary, if found.

#!/bin/sh
_HACK_='''________BEGIN_SH_CODE_____________'
ispy2() {
  case $1$2$3$4$5$6$7$8$9 in
    *ython2.*) return 0 ;;
    *) return 1 ;;
  esac
}
for c in python python2 python3 \
         /usr/local/bin/python* \
         /usr/bin/python* \
         /bin/python*
do
  ispy2 `$c -V 2>&1` && exec $c "$0" "$@"
done
echo "could not find python 2 binary"
exit 1
_HACK_='________BEGIN_PYTHON_CODE___________'''

import sys

print sys.version
print sys.argv

The nasty ispy2() function is a hack to remove whitespace from the python -V output in case of different word-splitting behaviors (I did not want to rely on any binary besides /bin/sh).

nabin-info
  • 289
  • 1
  • 7
-1

I would advise against this for reasons raised by mgilson. However you can check the python version with:

import sys
sys.version_info[0]

In case you still want to do this.

magu_
  • 4,766
  • 3
  • 45
  • 79
  • Then how do I run the script in Python 2.7 upon detecting that it was run in version 3? – Klik Jul 12 '16 at 17:04