Home > Mobile >  Handling relative imports with Flask, gunicorn, and Docker
Handling relative imports with Flask, gunicorn, and Docker

Time:03-03

Similar questions have been asked but I've never found a working solution to this issue.

I have a very basic Docker and Flask setup as such:

directory structure:

├── Dockerfile
├── app
│   ├── __init__.py
│   └── constants.py
├── docker-compose.yml
└── requirements.txt

Contents of init.py

from flask import Flask

from .constants import MYCONSTANT


def create_app():
    """Create the app."""
    app = Flask(__name__)
    @app.route('/', methods=['GET'])
    def index():
        return 'hello world' 

    return app

Dockerfile

FROM python:3.6-alpine
COPY requirements.txt /
RUN pip install Flask gunicorn
COPY app/ /app
WORKDIR /app
EXPOSE 8000
CMD gunicorn --bind=0.0.0.0:8000 "app.__init__.create_app()"

docker-compose.yml

docker-compose:

version: '3'
services:
  web:
    build: .
    ports: ['5000:5000']
    volumes: ['./app:/app']

When I run docker-compose up --build, the app complains that module 'app' cannot be found:

web_1 | ModuleNotFoundError: No module named 'app'

Due to test suites and and some other factors, I can't change to from constants import MYCONSTANT, so I'm not really sure how to resolve this. I've tried changing the gunicorn line to CMD gunicorn --bind=0.0.0.0:8000 "__init__:create_app()" but then I get this import error:

web_1  |     from .constants import MYCONSTANT
web_1  | ImportError: attempted relative import with no known parent package

CodePudding user response:

The core of your problem here is that you've lost a layer of directory. You're running GUnicorn from the /app directory, and trying to import the Python app module, but there's no ./app/__init__.py relative to the directory you're starting from.

Your first step should be to delete the volumes: block from the docker-compose.yml file. Especially if you're having directory-layout problems, mounting something over the entire application code makes it hard to debug the issue, and you'll get different results depending on what exactly the current system has checked out. The application code is built into the image so there's no need to inject it separately.

In the Dockerfile, I'd avoid copying files into the container root directory. The important thing is to make sure you're copying the app subdirectory from the build context into an app subdirectory of the current directory.

FROM python:3.6-alpine

# Switch out of the root directory before copying anything in.
# (This directory is frequently named /app; picking something
# different for clarity.)
WORKDIR /web

# Install Python dependencies.  (Keep this as a separate early step
# so repeated `docker build` goes faster.)
COPY requirements.txt .
RUN pip install -r requirements.txt

# Copy in the main application source.
COPY app/ ./app/ # <-- *** note, ./app relative to WORKDIR

# Set metadata for how to run the container.
EXPOSE 8000
CMD gunicorn --bind=0.0.0.0:8000 "app.__init__.create_app()"
version: '3.8'
services:
  web:
    build: .
    ports: ['5000:5000']
    # no volumes: required
  • Related