22

Please read the question carefully before closing as duplicate, I believe the use case to be unique.

I'm trying to create a docker image that only has python 3.7 installed, the problem is that if I try to install pip, the command also installs python 3.6 which I do not want.

The relevant part of the ideal docker file I'm tinkering is as follows

FROM ubuntu:18.04

# Upgrade installed packages
RUN apt-get update && apt-get upgrade -y && apt-get clean

# (...)

# Python package management and basic dependencies
RUN apt-get install -y python3.7 python3.7-dev python3.7-pip

# Register the version in alternatives
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1

# Set python 3 as the default python
RUN update-alternatives --set python /usr/bin/python3.7

# Upgrade pip to latest version
RUN python -m ensurepip --upgrade

# (...)

This would fail as python3.7-pip doesn't seem to exist; only python3-pip does, which is what installs python 3.6 for some reason.

I tried not installing pip at all and doing it manually, like so

# (...)

RUN apt-get install -y python3.7 python3.7-dev

# (...)

RUN curl 'https://bootstrap.pypa.io/get-pip.py' > get-pip.py

RUN python get-pip.py pip --no-setuptools --no-wheel

Which fails with this error:

Traceback (most recent call last):
  File "get-pip.py", line 21492, in <module>
    main()
  File "get-pip.py", line 197, in main
    bootstrap(tmpdir=tmpdir)
  File "get-pip.py", line 82, in bootstrap
    import pip._internal
  File "/tmp/tmpbez2vju9/pip.zip/pip/_internal/__init__.py", line 40, in <module>
  File "/tmp/tmpbez2vju9/pip.zip/pip/_internal/cli/autocompletion.py", line 8, in <module>
  File "/tmp/tmpbez2vju9/pip.zip/pip/_internal/cli/main_parser.py", line 8, in <module>
  File "/tmp/tmpbez2vju9/pip.zip/pip/_internal/cli/cmdoptions.py", line 14, in <module>
ModuleNotFoundError: No module named 'distutils.util'

Again, installing python3-distutils results in python 3.6 appearing in the system

So, is there a way to install ONLY a fully functional python 3.7 in ubuntu 18.04, WITHOUT having to install python 3.6?

juan
  • 80,295
  • 52
  • 162
  • 195
  • 1
    I found this person that has the same problem as me, but they seem to be ok with it (unlike me): https://bugs.launchpad.net/ubuntu/+source/python3-defaults/+bug/1800723 – juan May 14 '19 at 17:41

5 Answers5

25

In case someone else is ok with getting Python3.6 installed as a side effect (python3.7-distutils introduces it as pointed out by OP). This will install Python3.7 making it default and have the latest available pip using your python3.7 installation

FROM ubuntu:18.04

# Upgrade installed packages
RUN apt-get update && apt-get upgrade -y && apt-get clean

# (...)

# Python package management and basic dependencies
RUN apt-get install -y curl python3.7 python3.7-dev python3.7-distutils

# Register the version in alternatives
RUN update-alternatives --install /usr/bin/python python /usr/bin/python3.7 1

# Set python 3 as the default python
RUN update-alternatives --set python /usr/bin/python3.7

# Upgrade pip to latest version
RUN curl -s https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
    python get-pip.py --force-reinstall && \
    rm get-pip.py


# (...)
donhector
  • 875
  • 1
  • 10
  • 21
  • 2
    Do not use `python` for python 3 . Otherwise you might break apt, see https://stackoverflow.com/questions/43062608/how-to-update-alternatives-to-python-3-without-breaking-apt . It is better to `RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 1` and replace `python` with `python3` everywhere. – asmaier Apr 08 '21 at 12:58
5

The solution by @donhector has two subtle problems:

  1. It will only install the outdated version 3.7.5 of Python. You need the repo ppa:deadsnakes to get newer versions
  2. By installing alternatives for python it breaks the convention that python is used for Python 2.x. This might break apt, see How to update-alternatives to Python 3 without breaking apt?

A solution without these problems is:

FROM ubuntu:18.04

# Upgrade installed packages
RUN apt update && apt upgrade -y && apt clean

# install python 3.7.10 (or newer)
RUN apt update && \
    apt install --no-install-recommends -y build-essential software-properties-common && \
    add-apt-repository -y ppa:deadsnakes/ppa && \
    apt install --no-install-recommends -y python3.7 python3.7-dev python3.7-distutils && \
    apt clean && rm -rf /var/lib/apt/lists/*

# Register the version in alternatives (and set higher priority to 3.7)
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.6 1
RUN update-alternatives --install /usr/bin/python3 python3 /usr/bin/python3.7 2

# Upgrade pip to latest version
RUN curl -s https://bootstrap.pypa.io/get-pip.py -o get-pip.py && \
    python3 get-pip.py --force-reinstall && \
    rm get-pip.py
asmaier
  • 11,132
  • 11
  • 76
  • 103
4

I see two choices:

  1. Using a Ubuntu image, leave the Python from system untouched. Install pyenv (https://github.com/pyenv/pyenv), then download a python 3.7 install, completely separated from system's Python.

or

  1. Use the official Python image labeled 3.7.3-stretch or 3.7.3-slim-stretch (Debian)
Hildeberto
  • 414
  • 2
  • 8
2

If your only requirement is of python 3.7 and the version of the underlying os doesn't bothers you then you should go with the option of creating image from official python3.7 image. FROM python:3.7 And then install the required packages from requirements.txt

Mousam Singh
  • 675
  • 2
  • 9
  • 29
2

I'm not really sure why these dependencies exist in the first place. As a hack, you could force install python3-distutils without the dependencies using dpkg, then edit out the dependencies in /var/lib/dpkg/status so that dpkg doesn't think it's broken.

apt --download-only install --no-install-recommends -y python3-distutils && \
dpkg --force-all -i /var/cache/apt/archives/python3-distutils*.deb && \
sed -i -E "s|Depends: python3 \(>= 3.6.7-1~\), python3 \(<< 3.9\), python3-lib2to3 \(>= 3.6.4\)|Depends:|" /var/lib/dpkg/status;

Subsequently, installing other python versions will see the distutils requirement as satisfied.

This works and I've managed to use this reliably on Ubuntu Bionic, but this is fragile and there may be some landmines I'm not aware of.

Alternatively, using the deadsnakes repos also resolves this. You can use this on Ubuntu Bionic providing python version as a build arg.

ARG PYTHON_VERSION=3.9

# example
RUN add-apt-repository -y ppa:deadsnakes/ppa && \
    apt update && \
    if [ "$PYTHON_VERSION" != "3.6" ]; then \
         apt install -y --no-install-recommends python${PYTHON_VERSION}-distutils; \
    else \
        apt install -y --no-install-recommends python3-distutils; \
    fi \
    && \
    apt install -y --no-install-recommends \
        python${PYTHON_VERSION} \
        python${PYTHON_VERSION}-venv \
        python${PYTHON_VERSION}-dev \
    && \
    # optionally apt purge unnecessary packages here...
    rm -rf /var/cache/apt/* /var/lib/apt/lists/* /var/log/*


# ensure local python is preferred over distribution python
ENV PATH /usr/local/bin:$PATH

Depending how you add the deadsnakes repo to your sources list, you may also need to install software-properties-common and gnupg.

sytech
  • 29,298
  • 3
  • 45
  • 86