Developing python containers, simplified

frank-mckenna-tjX_sniNzgQ-unsplash.jpg

Photo by frank mckenna on Unsplash

Developing containers

When I develop a containerized service I want to:

  • easily edit the code
  • easily run unit tests
  • easily launch interactive sessions for debug

I’ve tried various solutions in the past with different degrees of success and effort

  • create a development image which mounts an external directory
    • connect to an shell into the development container to launch application and tests
  • create an interactive development with jupyter lab
    • connect to a shell from a web browser and edit files from it
  • create an image to launch unit tests

And some more combination of this kind.

My development cycle was slow due to

  • the limited amount of tools available in the image
  • multiple image building to run test and to create the production image

This has been very frustrating until recently

Hatch

I tried Poetry for a while in my development and it worked great

I won’t go in all of the details that made me choose Hatch for this task; I will just list which features I use the most

  • automatic virtual environment creation to launch the application
  • automatic test virtual environments
  • test grids with different python versions
  • fast format (black) and lint (ruff)
  • easy upload of packages in our private pip repositories
  • stick to the basic pyproject.toml layout

This worked well also for some apps running on both windows and linux.

My workflow

I develop everything in my host machine (which is a linux box or VM) with my tools of choice (emacs)

App execution is performed via

hatch run python -m mypackage

Tests are performed with the great pytest library

App tests are performed via

hatch test

which may have also the usual pytest arguments and flags

hatch test -n auto tests/test_one_file.py -k regex_methods

Usually I wrap these commands in convenient scripts e.g.

#!env bash
# move to script directory, assuming it is
# the project root
cd $(dirname $0)

# add the src directory if this is your layout
export PYTHONPATH=src

# read an env file in case
set -o allexport
source .env
set +o allexport

# launch the app
hatch run python -m app

finally create a Dockerfile; this may look like this

FROM python:3.12-slim
COPY pyproject.toml
COPY src
RUN pip install .
# add a CMD here

and a compose.yaml

services:
  myproject:
    image: myproject:${VERSION}
    env_file: .env

to test the image in the local docker service before deploying

I can add this in the scripts to get the version from python

export VERSION=$(hatch version)

Using PIP for tests

Hatch uses UV as a default package manager when creating virtual environments for test unless you tell to do it differently

While UV is a great tool I want to use the same global pip configuration as

  • within my company’s premises it exposes a path to my CA certificates
  • add some private pip repos

I added this snippet to my project.toml to have this working

[tool.hatch.envs.hatch-test]
installer="pip"

Pip is missing in my virtual environment

Sometime these environment are created without the pip package

  1. find where your hatch environment are
    1. launch python
hatch run python
  1. chech the import paths
import sys
print(sys.path)
  1. activate your test environment
source ~/.local/share/harch/env/my_project/hatch-test.py3.12/bin/activate
  1. force pip installation
python -m enusrepip

Conclusion

This workflow fits most of the containerized applications I’m currently working on, and speeds up my development cycle.

marco.p.v.vezzoli

Self taught assembler programming at 11 on my C64 (1983). Never stopped since then -- always looking up for curious things in the software development, data science and AI. Linux and FOSS user since 1994. MSc in physics in 1996. Working in large semiconductor companies since 1997 (STM, Micron) developing analytics and full stack web infrastructures, microservices, ML solutions

You may also like...

Leave a Reply