Python 2.7+ on CentOS/RedHat 6

Python 2.7+ on CentOS/RedHat 6

If you still use an older version of CentOS/RedHat, the newest Python version available for it is 2.6, which is not supported anymore by Python team (and many other tools as well). In this (CentOS/RedHat 6) case it is not as easy as to just do a yum upgrade python, because newer (2.7 and up) Python versions are incompatible with system utilities that rely on Python 2.6, namely yum itself, so upgrade of the existing version is also not the easiest way. To work around that, we could set up newer Python version (either from 2.7.* or 3.* branches) in parallel with the default, older 2.6.* version. One of the ways to do that is to compile and install it yourself, or even make an RPM package to be able to repeat it on multiple systems.

Preparing the system

To start, we would need a running CentOS/RedHat 6 system, and to set up a few dependencies, to allow compiling of the source code and later preparing an RPM package for easier distribution:

$> sudo yum -y install openssl-devel readline-devel \
bzip2-devel sqlite-devel zlib-devel \
ncurses-devel db4-devel expat-devel \
rubygems rpm-build

Preparing the source

Make a dummy directory for temporarily installing the newer Python version:

$> mkdir /tmp/installdir

Get the source of the required Python version. We'll use 2.7.11 in this example. Un-tar it, configure, make and make install it in the dummy directory:

$> curl -LO
tar -xf Python-2.7.11.tgz
cd Python-2.7.11
$> ./configure --prefix=/usr/local \
--enable-unicode=ucs4 \
--enable-shared \
LDFLAGS="-Wl,-rpath /usr/local/lib"
$> make
$> make install DESTDIR=/tmp/installdir

Important thing to note here is the LDFLAGS parameter for the configure script - as we are installing Python as a shared library, we need to specify, where it will reside. In order to do that, there are 2 options: compile it in (as we are doing in this case), or manually add /usr/local/lib path to /etc/ and do an /sbin/ldconfig after you install. prefix too should be set to /usr/local, not to interfere with the old python version.

Building an RPM package

When the source has been successfully compiled and installed in the temporary directory, we can proceed to making an RPM package for easier distribution. One of the tools to make that happen is "Effing package management" or simply fpm, which really is effing good.

On CentOS/RHEL 6 we need an older version of fpm. The reason is described in this GitHub issue. We also need a newer ruby version (1.9.3). We will get both with rvm.

First, import ruby archive signing key and install rvm, ruby 1.9.3 and fpm 1.4.0

$> curl -sSL | gpg2 --import -
$> curl -L | bash -s stable --ruby
$> source ~/.rvm/scripts/rvm
$> rvm install 1.9.3
$> rvm use 1.9.3
$> gem install fpm -v 1.4.0

Next comes the actual package creation. I got inspiration from this Gist, plus a few tweaks:

$> fpm -s dir -t rpm -n python27 -v 2.7.11 -C /tmp/installdir \
-d 'openssl' \
-d 'bzip2' \
-d 'zlib' \
-d 'expat' \
-d 'db4' \
-d 'sqlite' \
-d 'ncurses' \
-d 'readline' \
--directories=/usr/local/lib/python2.7/ \
--directories=/usr/local/include/python2.7/ \

After that there should be a single python27-2.7.11-1.x86_64.rpm file for Python 2.7 package, which can be installed where necessary:

$> sudo yum install python27-2.7.11-1.x86_64.rpm
$> yum list python python27
Installed Packages
python.x86_64    2.6.6-66.el6_8  @updates
python27.x86_64  2.7.11-1        @/python27-2.7.11-1.x86_64

If /usr/local/bin (or the path you installed it in) precedes /usr/bin in your PATH variable, executing python would show Python 2.7.11, while yum will still use /usr/bin/python:

$> python --version
Python 2.7.11
$> /usr/bin/python --version
Python 2.6.6