Lecture 23 - Dependency Management, Virtual Environments, and Containers
1. Dependency management
2. Virtual environments
venv and pip, and the requirements.txt every Dockerfile readsconda for software that is not Python, uv for speed3. Containers
4. Your first image
-c means “run this command”pip, conda, and others install and track those packagesconda, pip, uv)renv (R) or uv (Python), which record exact versionspip (Python only): requirements.txtconda (Python and R): environment.ymluv (Python only): pyproject.toml and uv.lockrequirements.txt best: every Dockerfile reads it, including your project starter’svenv and pip: an isolated Pythonvenv ships with Python. It creates a folder with its own python and site-packages.venv/ to your .gitignore. The package list goes into git; the folder itself stays on your machinewhich python tells you which interpreter you are actually using:.venv/bin/python, so the environment is activepython3 -m venv /opt/venv and sets ENV PATH="/opt/venv/bin:$PATH", so every later instruction uses that interpreter without activationrequirements.txt: the file you commitpip freeze writes it and pip install -r reads itpip freeze prints every installed package with its exact version:pip lists the dependencies of your dependencies== pins say “these exact versions”pip freeze inside a conda environment and some lines come out as name @ file:///... local paths, which install nowhere elsevenv and the problem never appears--from-history, which records only the packages you asked for:conda env create --file environment.yml, then conda activate l23-demo--from-history and it lists every transitive package instead:--no-builds flag strips it, making the file portable across platformspy39h12345) encodes metadata:
py39, py310)linux_64, osx_arm64)gcc9, clang)nomkl, cuda)libblas=3.11.0=9_h51639a9_openblas on the previous slide will not resolve on a Linux collaborator’s machineuv: a fast Python package manageruv init l23-uv-demo --python 3.14
cd l23-uv-demo
uv add polars requests
Using CPython 3.14.6 interpreter at:
/opt/homebrew/opt/python@3.14/bin/python3.14
Creating virtual environment at: .venv
Resolved 8 packages in 1.62s
Prepared 3 packages in 1m 06s
Installed 7 packages in 12ms
+ polars==1.44.0
[...]
+ requests==2.34.2uv: project files and sharingpyproject.toml holds your direct dependencies and is meant for humans. Mine after uv add:[sections]. Created by Tom Preston-Werner, a GitHub co-founder. More here
uv init also writes a .gitignore, a README.md, and a main.pyuv.lock is generated for you, recording the exact version and hash of every packagepyproject.toml and uv.lock. Your collaborator clones and runs uv sync to get the same environment:uv run executes inside the project environment, so there is nothing to activateuv manages Python versions too: uv python install 3.14 and uv python pin 3.14uv hereuv: handing a project to Dockerrequirements.txtrequirements.txt, and uv export writes one from your lock file:uv export --format requirements-txt --no-hashes
Resolved 8 packages in 2ms
# This file was autogenerated by uv via the
# following command:
# uv export --format requirements-txt
# --no-hashes
certifi==2026.7.22
# via requests
charset-normalizer==3.5.1
# via requests
polars==1.44.0
# via l23-uv-demo
requests==2.34.2
# via l23-uv-demo
[...]--no-hashes and each line also carries the wheel’s SHA-256, which pip then verifies# via comments name which package pulled each dependency in. pip freeze never doesvenv and pip workflow at uv’s speed? Drop-in replacements:venv slides, with uv doing the installLecture 10 listed four ingredients of a reproducible result: code, data, environment, documentation. A container ships the environment with the code already inside it
Dockerfiledocker command is a client. It sends every instruction to a background daemon, which does the building and the runningClient, daemon, and registry, from the Docker documentation
Every docker command on these slides ran through OrbStack. The commands are identical under Docker Desktop
hello-worlddocker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
58dee6a49ef1: Pull complete
Digest: sha256:5dd0d3e6e255913fc30f90b9f2b1d359cc2cbdb48090cc4b65f1676e203243cc
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be
working correctly.
[...]Nothing happens and the terminal complains about a daemon? The application is not running: Appendix 02
Dockerfile is a plain-text recipe that builds everything needed to recreate a projectDockerfile with no extensionlectures/lecture-23/docker/ in the course repositoryrequirements.txt, same file from the pip slides:hello.py, so the container has something to do:Dockerfile. The next slide takes it line by line# Start from an official Python image
FROM python:3.14-slim
# Everything below happens inside /app
WORKDIR /app
# Copy requirements first, so Docker can
# cache the install
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy the script
COPY hello.py .
# What runs when the container starts
CMD ["python", "hello.py"]--no-cache-dir -r means “install from the file and don’t keep a cache of the wheels”-t tags the image datasci350-example, and . is the folder holding the Dockerfile:docker build runs the instructions in sequence, layer by layer, and each one adds a layer| Instruction | What it does | Runs when |
|---|---|---|
FROM |
Picks the base image | Build |
WORKDIR |
Sets the folder for every later step | Build |
COPY |
Brings a file from your folder into the image | Build |
RUN |
Executes a command inside the image | Build |
CMD |
Names the command the container starts with | Start |
The official Python image on Docker Hub: https://hub.docker.com/_/python
docker build --progress=plain -t datasci350-example .
#5 [1/5] FROM docker.io/library/python:3.14-slim
#5 sha256:bf7af02297 30.16MB / 30.16MB 63.7s done
#5 DONE 64.4s
#6 [2/5] WORKDIR /app
#6 DONE 0.2s
#7 [3/5] COPY requirements.txt .
#7 DONE 0.0s
#8 [4/5] RUN pip install --no-cache-dir -r
requirements.txt
#8 51.68 Successfully installed numpy-2.5.2
pandas-3.0.5 [...]
#8 DONE 51.9s
#9 [5/5] COPY hello.py .
#9 DONE 0.1s
#10 exporting to image
#10 naming to datasci350-example:latest done
#10 DONE 2.7s[1/5] to [5/5], match the five Dockerfile instructions[1/5] took 64.4 seconds because Docker downloaded the 30 MB python:3.14-slim base image. That happens once per machine[4/5] is the pip install, and its 51.9 seconds is the slow part of every rebuilddocker images shows what came out:docker build -t datasci350-example .
#5 [1/5] FROM docker.io/library/python:3.14-slim
#5 DONE 0.0s
#6 [3/5] COPY requirements.txt .
#6 CACHED
#7 [4/5] RUN pip install --no-cache-dir -r
requirements.txt
#7 CACHED
#8 [2/5] WORKDIR /app
#8 CACHED
#9 [5/5] COPY hello.py .
#9 CACHED
#10 exporting to image
#10 DONE 0.1sCACHED and the build finishes in 0.754 secondsCOPY requirements.txt sits above COPY hello.py: editing your script invalidates only the last layer, and the slow pip install stays cachedCOPY . . at the top and any edit re-installs every packageDockerfile uses the same ordering, for the same reason#6 as step [3/5]docker run to Docker Hub--rm flag deletes the container when it exits, so you do not accumulate hundreds of dead ones:docker build and docker run on a clean machine, and either your report appears or it does notdocker/ folder from the course repository: https://github.com/danilofreire/datasci350/tree/main/lectures/lecture-23/dockerdocker build -t datasci350-example .docker run --rm datasci350-example. Three lines appearrequirements.txt. Add the line polars==1.44.0hello.py. Import polars and print pl.__version__docker build -t datasci350-example . againCACHEDdocker run --rm datasci350-example. Four lines nowCOPY lines and rebuildSolution: Appendix 01
print statement showed the failure: the script was fine and the environment movedvenv and pip: an isolated Python, pip freeze writing == pins, pip install -r reading them back--from-history for a file that travels, build strings for one that stays homepyproject.toml and uv.lock, uv sync for collaborators, uv export when a Dockerfile needs requirements.txtpython:3.14-slim in about two minutes, and layer caching brought the rebuild down to 0.754 secondsThe container you push is the deliverable. On my Macbook I will run docker build and then docker run, and your report either appears or it does not 😅
Quiz 04: web APIs and JSON, from Lectures 18 and 19. No new material that day
Lecture 25, on 24 November, is Docker for Data Science. We take the project starter’s Dockerfile instruction by instruction, and it installs Quarto as well as Python
Everything today was the small version of that file
Before then:
docker run hello-worlddocker build once on your own wifiQuiz 05 covers Lectures 21, 22, 23, and 25. The final project is due on the last day of classes
Steps 5 and 6, the two edited files:
Step 9, the run:
Steps 7 and 8, the rebuild on my laptop:
#7 forced every later layer to run againpolars, which the first build never fetchedCOPY lines swapped, editing hello.py alone invalidates COPY hello.py and re-runs pip install for nothingCannot connect to the Docker daemon
Docker Desktop is not running. Start the application, wait for the whale icon to settle, and try again. On my laptop OrbStack was stopped, and docker info answered failed to connect to the docker API at unix:///Users/dafreir/.orbstack/run/docker.sock.
permission denied ... /var/run/docker.sock
On Linux, your user is not in the docker group. Run sudo usermod -aG docker $USER, then log out and back in.
pip: command not found
Your virtual environment is not active. Run source .venv/bin/activate and check with which python, which should print a path ending in .venv/bin/python.
No matching distribution found for numpy==2.5.2
That version does not exist for your Python. Check the pin against PyPI, or loosen it to numpy>=2.0.
toomanyrequests: You have reached your pull rate limit
Docker Hub allows 100 anonymous pulls per hour from one address. Run docker login with your free account and pull again.
failed to read dockerfile: open Dockerfile: no such file or directory
You are in the wrong folder. The . at the end of docker build -t name . is where Docker looks, so cd into the folder holding the Dockerfile