Lesson 046: Lazy Imports for Deployment Compatibility

Lesson 046: Lazy Imports for Deployment Compatibility

The Lesson

Import heavy dependencies inside the function that uses them, not at module scope. A module-level import numpy means every consumer of that module — including lightweight build scripts, CI pipelines, and serverless functions — must have numpy installed, even if they never call the code path that needs it.

Context

A FastAPI web application serves image browsing, cluster analysis, and calendar selection for a 12,000-image collection. The cluster routes module added a "spotlight" endpoint that uses numpy for greedy max-min diversity selection on CLIP embeddings. The web app also has a static site generator that imports the same FastAPI app to enumerate routes and pre-render API responses as JSON files. This static build runs in a GitHub Actions CI environment that installs only core dependencies (pip install .[web]), not the ML extras (pip install .[ml]).

What Happened

  1. Added import numpy as np at the top of web/routes/clusters.py because the new _compute_spotlight function used numpy for embedding distance calculations.
  2. The local dev server worked fine — numpy was already installed for the ML pipeline.
  3. Pushed to GitHub. The CI workflow ran: checkout → install .[web] → download warehouse → build static site. The build script imported create_app(), which imported all route modules, which imported numpy. Build failed with ModuleNotFoundError: No module named 'numpy'.
  4. The fix was one line: move import numpy as np from module scope into the _compute_spotlight function body. The route module now loads without numpy; numpy is only needed when someone actually calls the spotlight endpoint.
  5. The static build never calls the spotlight endpoint (it pre-renders paginated cluster listings, not the diversity calculation), so it never hits the import.

Key Insights

Applicability

This pattern applies whenever:

Does NOT apply when:

Related Lessons