As your FastAPI applications, particularly those serving machine learning models, evolve, they inevitably rely on a growing number of external Python packages. These include FastAPI itself, Uvicorn, Pydantic, your chosen ML libraries (like scikit-learn, TensorFlow, or PyTorch), data manipulation tools (like Pandas or NumPy), and potentially many others. Managing these dependencies effectively is fundamental to creating reproducible, stable, and maintainable applications. Without explicit management, you risk encountering version conflicts, unexpected behavior changes, and difficulties collaborating or deploying your service.
Imagine developing your ML API using the latest version of scikit-learn
on your machine. Everything works perfectly. A colleague tries to set up the project but installs an older version, leading to errors because a function you used isn't available. Or worse, the application runs but produces different prediction results due to subtle changes in the library's algorithms. When deploying to production, the server might install yet another version, causing failures or inconsistencies.
Proper dependency management addresses these issues by:
requirements.txt
The most common and straightforward method for managing dependencies in Python projects is using a requirements.txt
file. This plain text file lists the required packages, typically one per line, often with specific version constraints.
Creating requirements.txt
While you can create this file manually, it's often generated based on the packages currently installed in your project's virtual environment. After installing the necessary packages (fastapi
, uvicorn
, scikit-learn
, joblib
, etc.) using pip
, you can freeze the current state:
pip freeze > requirements.txt
This command captures all installed packages, including dependencies of your direct dependencies, and pins them to their exact versions. The resulting requirements.txt
might look something like this:
fastapi==0.104.1
uvicorn[standard]==0.23.2
pydantic==2.4.2
scikit-learn==1.3.2
joblib==1.3.2
numpy==1.26.1
# ... other dependencies
Installing from requirements.txt
To set up the project environment on a new machine or in a deployment pipeline, you use pip
to install the packages listed in the file:
pip install -r requirements.txt
This ensures that the exact versions specified in the file are installed, recreating the intended environment.
Version Specifiers
The pip freeze
command uses exact version pinning (==
). This is generally recommended for application deployments to ensure maximum stability and reproducibility. However, other specifiers exist:
package_name>=1.0
: Installs version 1.0 or higher. Use with caution, as major version updates can introduce breaking changes.package_name<2.0
: Installs any version less than 2.0.package_name~=1.1
: Compatible release. Installs version 1.1 or any later patch version (e.g., 1.1.1, 1.1.2) but not 1.2 or 2.0. This allows for bug fixes while minimizing the risk of breaking changes. Useful for library development but often too loose for application deployment.For ML applications, where subtle changes in underlying libraries can impact model predictions, pinning exact versions (==
) in your requirements.txt
for production environments is usually the safest approach.
While pip
and requirements.txt
are functional, more modern tools like Poetry offer a more integrated and robust approach to dependency and project management.
Poetry uses a pyproject.toml
file to define project metadata, dependencies, development dependencies, and build system information.
Key Features of Poetry:
poetry.lock
): When you add dependencies (poetry add package_name
) or install them (poetry install
), Poetry generates a poetry.lock
file. This file records the exact versions of all installed packages (including sub-dependencies), ensuring truly reproducible builds across different environments. This is more reliable than pip freeze
as it reflects the resolved dependency tree, not just the top-level packages installed.Example pyproject.toml
Snippet:
[tool.poetry]
name = "ml-api"
version = "0.1.0"
description = "FastAPI service for ML model predictions"
authors = ["Your Name <you@example.com>"]
[tool.poetry.dependencies]
python = "^3.9"
fastapi = "^0.104.1"
uvicorn = {extras = ["standard"], version = "^0.23.2"}
scikit-learn = "^1.3.2"
joblib = "^1.3.2"
pydantic = "^2.4.2"
[tool.poetry.group.dev.dependencies]
pytest = "^7.4.3"
httpx = "^0.25.1" # For TestClient
[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"
To install dependencies defined here, you would run:
poetry install
This reads pyproject.toml
, resolves dependencies, installs them into a virtual environment, and creates/updates poetry.lock
.
Choosing between pip/requirements.txt
and Poetry often depends on project complexity and team preference. For larger projects or teams seeking stricter dependency control and integrated tooling, Poetry offers significant advantages. For simpler applications, pip
and requirements.txt
remain a viable and widely understood option.
Regardless of the tool chosen, actively managing your dependencies is a non-negotiable aspect of building reliable and maintainable FastAPI applications, especially in the context of ML model deployment where consistency is paramount. This practice forms a solid foundation for the testing and deployment strategies discussed later in this chapter and the course.
© 2025 ApX Machine Learning