I’ve experienced Docker image build installed a different version of the Werkzeug dependency package than the development editable install process. And this caused the Python project in the Docker image failed to run. Development editable install means running the “pip3 install -e .” command within an active virtual environment. I’m describing the problem and how to address it in this post.

031-feature-image.png
Python: Docker image build – “the Werkzeug” problem 🤖!

The environments in this post: ⓵ Windows 10 Pro version 10.0.19044 Build 19044. ⓶ docker CLI version 20.10.12, build e91ed57.

Please note, the Python code in this post is a continuation of the code used in the post Python: interactive shell and shell_context_processor() decorator., whom code can be cloned with:

git clone -b v1.0.1 https://github.com/behai-nguyen/flask-restx-demo.git

✿✿✿

On around the 20/July/2022, I did a Docker build for this Python project using editable install image build step: RUN pip3 install -e .. I was able to run this image successfully. In fact, I did use it to learn Docker volumes: I started and stopped the runs several times. I'm 100% certain that it worked. I removed all containers, images and volumes afterward.

I've manually recorded all essential commands and their results in a text file -- I still am doing that. On the 24/July/2022, I repeated what I've done previously: the build worked, but running the image resulted in import error!

Command to build:

F:\flask_restx_demo\>docker build --tag flask-restx-demo .

Command to run:

F:\flask_restx_demo>docker run --publish 8000:8000 --rm flask-restx-demo

ImportError: cannot import name 'parse_rule' from 'werkzeug.routing' (/usr/local/lib/python3.10/site-packages/werkzeug/routing/__init__.py).

Please see the screen capture below for the full log:

031-01-restx-demo-run-image-failed.png

Following the error, I was able to figure out what the problem was. Inside the Docker image, the version of the Werkzeug was 2.2.0, while in the development environment, it is 2.1.2. I describe how I did this in the later section How to look inside a Docker image.

The Werkzeug package, in my understanding, is a dependency package. It gets installed automatically when required by the packages that we specify. We don't usually have to explicitly specify the Werkzeug package as a required package.

Between those nearly four ( 4 ) days, the only significant change was in Python codes, where I enable cross-origin AJAX with package Flask-Cors. In my understanding, this package should not cause a different version of the Werkzeug package to be installed?

I should also mention that on my Windows 10 Pro the Python version is 3.10.1; while for image build, it is 3.10.5: FROM python:3.10.5-slim-buster.

-- But this configuration has not been changed between those nearly four ( 4 ) days.

Google searches would show that others had problems with Werkzeug dependency versions too, and they have to explicitly force the correct version in their required packages list. So I thought if I force the development environment to explicitly install version 2.2.0, then it will fail with the import error like the Docker image does.

I updated setup.py's install_requires section to include as its last entry 'werkzeug==2.2.0':

    install_requires=[
        'Flask',
        'python-dotenv',
        'Flask-RESTX',
        'Flask-SQLAlchemy',
        'Flask-Cors',		
        'werkzeug==2.2.0',
    ],

Re-run editable install:

(venv) F:\flask_restx_demo>pip3 install -e .

Then run the project:

(venv) F:\flask_restx_demo>venv\Scripts\flask.exe run

I did get the expected error. Please see the screen capture below:

031-02-restx-demo-flask-run-failed.png

ImportError: cannot import name 'parse_rule' from 'werkzeug.routing' (F:\flask_restx_demo\venv\lib\site-packages\werkzeug\routing\__init__.py) .

Based on this, I expected that if I change Werkzeug to version 2.1.2 then the Docker image should work. The development environment already works as we've seen before.

<a href="https://github.com/behai-nguyen/flask-restx-demo/blob/main/setup.py" title="setup.py"target="_blank">setup.py</a>'s install_requires is now:

    install_requires=[
        ...
        'werkzeug==2.1.2',
    ],

❶ Re-install required packages in editable mode, the project run with the Flask development server as before. ❷ Re-build and re-run Docker image -- it works.

Please note, if run with:

F:\flask_restx_demo>docker run --publish 8000:8000 --rm flask-restx-demo

Then the URLs to container are:

http://0.0.0.0:8000/api/v1/ui -- Swagger UI URL
http://0.0.0.0:8000/api/v1/trees -- API URL

The latest code for this post can be cloned with:

git clone -b v1.0.2 https://github.com/behai-nguyen/flask-restx-demo.git

Please note, I have now included two ( 2 ) test clients under test_client_app\. ⓵ test_client_app\delphi is a simple Delphi 10.4 Community project, you will have to compile it yourself. ⓶ test_client_app\jquery-ajax\TreeAPIClient.html is a JQuery page, just copy it to a web site or a virtual web directory, and run it from there.

How to look inside a Docker image

Run the container in the interactive mode with the bash process:

F:\flask_restx_demo>docker run -it --rm flask-restx-demo bash

The prompt will change to something like this:

root@64be4aeb190a:/flask_restx_demo#

We would need an editor to look at the files. vi is not installed. We can install it, but we need to know what kind of Linux distro it is, run:

root@64be4aeb190a:/flask_restx_demo# cat /etc/os-release

We get:

PRETTY_NAME="Debian GNU/Linux 10 (buster)"
NAME="Debian GNU/Linux"
VERSION_ID="10"
VERSION="10 (buster)"
VERSION_CODENAME=buster
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
root@64be4aeb190a:/flask_restx_demo#

I searched for “install vi debian”, and found this How to Install vim editor on Debian 11 -- basically, run these two ( 2 ) commands:

root@64be4aeb190a:/flask_restx_demo# apt update
root@64be4aeb190a:/flask_restx_demo# apt install vim

Now that we have vim, we can look at files now. With Werkzeug 2.2.0 installed:

root@64be4aeb190a:/flask_restx_demo# vi /usr/local/lib/python3.10/site-packages/werkzeug/routing/__init__.py

There is no parse_rule defined!

root@64be4aeb190a:/flask_restx_demo# vi /usr/local/lib/python3.10/site-packages/werkzeug/__init__.py

The last line should tell us the version:

__version__ = "2.2.0"

When finished:

root@64be4aeb190a:/flask_restx_demo# exit

✿✿✿

Although I still couldn't figure out why the different versions of Werkzeug get installed... I'm happy to have figured out that the versions caused the import error. I hope you find this post useful, thank you for reading and happy Dockering 🙀!