DEV Community

Cover image for Day 3: Fix a Broken uv Lockfile Specification
Thu Kha Kyawe
Thu Kha Kyawe

Posted on

Day 3: Fix a Broken uv Lockfile Specification

Lab Information

The xFusionCorp Industries ML team uses uv and lockfiles to keep Python dependencies reproducible across machines. A teammate has left behind a requirements.in specification that does not match the team's standard. Correct it and compile it into a pinned lockfile.

A high-level dependency specification exists at /root/code/fraud-detection/requirements.in. uv is already installed.

The corrected specification must meet the following requirements:
    it lists exactly these four top-level packages: scikit-learn, mlflow, pandas, and numpy;
    every package carries a version constraint that uv can actually satisfy against PyPI.

Review the existing requirements.in, and correct everything that does not match the requirements above.

From the project directory, compile the corrected specification into a pinned lockfile:
Enter fullscreen mode Exit fullscreen mode

uv pip compile requirements.in -o requirements.txt

The resulting requirements.txt must pin each of the four top-level packages to an exact version using ==, and must also include the transitive dependencies that uv resolved.
Enter fullscreen mode Exit fullscreen mode

Lab Solutions

🧭 Part 1: Lab Step-by-Step Guidelines

Run the following commands on the controlplane host.

Step 1 — Move to the project directory

cd /root/code/fraud-detection
Enter fullscreen mode Exit fullscreen mode

Step 2 — Inspect the existing requirements.in

cat requirements.in
Enter fullscreen mode Exit fullscreen mode

Look for:

wrong package names
missing packages
extra packages
invalid version constraints

Step 3 — Edit requirements.in

Open the file:

vi requirements.in
Enter fullscreen mode Exit fullscreen mode

Replace the contents with valid package constraints such as:

scikit-learn>=1.4
mlflow>=2.12
pandas>=2.2
numpy>=1.26

The file must contain:

exactly 4 top-level packages

all valid PyPI-installable constraints

Step 4 — Compile the lockfile using uv

Run:

uv pip compile requirements.in -o requirements.txt
Enter fullscreen mode Exit fullscreen mode

This generates:

requirements.txt

with:

exact pinned versions (==)

all transitive dependencies

Step 5 — Verify the generated lockfile

Check the file:

cat requirements.txt

You should see entries like:

Resolved 90 packages in 626ms
# This file was autogenerated by uv via the following command:
#    uv pip compile requirements.in -o requirements.txt
aiohappyeyeballs==2.6.2
    # via aiohttp
aiohttp==3.14.0
    # via mlflow
aiosignal==1.4.0
    # via aiohttp
alembic==1.18.4
    # via mlflow
annotated-doc==0.0.4
    # via fastapi
annotated-types==0.7.0
    # via pydantic
anyio==4.13.0
    # via starlette
attrs==26.1.0
    # via aiohttp
blinker==1.9.0
    # via flask
cachetools==7.1.4
    # via
    #   mlflow-skinny
    #   mlflow-tracing
certifi==2026.5.20
    # via requests
cffi==2.0.0
    # via cryptography
charset-normalizer==3.4.7
    # via requests
click==8.4.1
    # via
    #   flask
    #   mlflow-skinny
    #   uvicorn
cloudpickle==3.1.2
    # via mlflow-skinny
contourpy==1.3.3
    # via matplotlib
cryptography==48.0.0
    # via
    #   google-auth
    #   mlflow
cycler==0.12.1
    # via matplotlib
databricks-sdk==0.114.0
    # via
    #   mlflow-skinny
    #   mlflow-tracing
docker==7.1.0
    # via mlflow
fastapi==0.136.3
    # via mlflow-skinny
flask==3.1.3
    # via
    #   flask-cors
    #   mlflow
flask-cors==6.0.2
    # via mlflow
fonttools==4.63.0
    # via matplotlib
frozenlist==1.8.0
    # via
    #   aiohttp
    #   aiosignal
gitdb==4.0.12
    # via gitpython
gitpython==3.1.50
    # via mlflow-skinny
google-auth==2.53.0
    # via databricks-sdk
graphene==3.4.3
    # via mlflow
graphql-core==3.2.8
    # via
    #   graphene
    #   graphql-relay
graphql-relay==3.2.0
    # via graphene
greenlet==3.5.1
    # via sqlalchemy
gunicorn==26.0.0
    # via mlflow
h11==0.16.0
    # via uvicorn
huey==3.0.1
    # via mlflow
idna==3.17
    # via
    #   anyio
    #   requests
    #   yarl
importlib-metadata==9.0.0
    # via mlflow-skinny
itsdangerous==2.2.0
    # via flask
jinja2==3.1.6
    # via flask
joblib==1.5.3
    # via scikit-learn
kiwisolver==1.5.0
    # via matplotlib
mako==1.3.12
    # via alembic
markupsafe==3.0.3
    # via
    #   flask
    #   jinja2
    #   mako
    #   werkzeug
matplotlib==3.10.9
    # via mlflow
mlflow==3.13.0
    # via -r requirements.in
mlflow-skinny==3.13.0
    # via mlflow
mlflow-tracing==3.13.0
    # via mlflow
multidict==6.7.1
    # via
    #   aiohttp
    #   yarl
narwhals==2.22.0
    # via scikit-learn
numpy==2.4.6
    # via
    #   -r requirements.in
    #   contourpy
    #   matplotlib
    #   mlflow
    #   pandas
    #   scikit-learn
    #   scipy
    #   skops
opentelemetry-api==1.42.1
    # via
    #   mlflow-skinny
    #   mlflow-tracing
    #   opentelemetry-sdk
    #   opentelemetry-semantic-conventions
opentelemetry-proto==1.42.1
    # via
    #   mlflow-skinny
    #   mlflow-tracing
opentelemetry-sdk==1.42.1
    # via
    #   mlflow-skinny
    #   mlflow-tracing
opentelemetry-semantic-conventions==0.63b1
    # via opentelemetry-sdk
packaging==26.2
    # via
    #   gunicorn
    #   matplotlib
    #   mlflow-skinny
    #   mlflow-tracing
    #   skops
pandas==2.3.3
    # via
    #   -r requirements.in
    #   mlflow
pillow==12.2.0
    # via matplotlib
prettytable==3.17.0
    # via skops
propcache==0.5.2
    # via
    #   aiohttp
    #   yarl
protobuf==6.33.6
    # via
    #   databricks-sdk
    #   mlflow-skinny
    #   mlflow-tracing
    #   opentelemetry-proto
pyarrow==24.0.0
    # via mlflow
pyasn1==0.6.3
    # via pyasn1-modules
pyasn1-modules==0.4.2
    # via google-auth
pycparser==3.0
    # via cffi
pydantic==2.13.4
    # via
    #   fastapi
    #   mlflow-skinny
    #   mlflow-tracing
pydantic-core==2.46.4
    # via pydantic
pyparsing==3.3.2
    # via matplotlib
python-dateutil==2.9.0.post0
    # via
    #   graphene
    #   matplotlib
    #   pandas
python-dotenv==1.2.2
    # via mlflow-skinny
pytz==2026.2
    # via pandas
pyyaml==6.0.3
    # via mlflow-skinny
requests==2.34.2
    # via
    #   databricks-sdk
    #   docker
    #   mlflow-skinny
scikit-learn==1.9.0
    # via
    #   -r requirements.in
    #   mlflow
    #   skops
scipy==1.17.1
    # via
    #   mlflow
    #   scikit-learn
    #   skops
six==1.17.0
    # via python-dateutil
skops==0.14.0
    # via mlflow
smmap==5.0.3
    # via gitdb
sqlalchemy==2.0.50
    # via
    #   alembic
    #   mlflow
sqlparse==0.5.5
    # via mlflow-skinny
starlette==1.2.1
    # via
    #   fastapi
    #   mlflow-skinny
threadpoolctl==3.6.0
    # via scikit-learn
typing-extensions==4.15.0
    # via
    #   aiohttp
    #   aiosignal
    #   alembic
    #   anyio
    #   fastapi
    #   graphene
    #   mlflow-skinny
    #   opentelemetry-api
    #   opentelemetry-sdk
    #   opentelemetry-semantic-conventions
    #   pydantic
    #   pydantic-core
    #   sqlalchemy
    #   starlette
    #   typing-inspection
typing-inspection==0.4.2
    # via
    #   fastapi
    #   pydantic
tzdata==2026.2
    # via pandas
urllib3==2.7.0
    # via
    #   docker
    #   requests
uvicorn==0.48.0
    # via mlflow-skinny
wcwidth==0.7.0
    # via prettytable
werkzeug==3.1.8
    # via
    #   flask
    #   flask-cors
yarl==1.24.2
    # via aiohttp
zipp==4.1.0
    # via importlib-metadata
Enter fullscreen mode Exit fullscreen mode

🧠 Part 2: Simple Beginner-Friendly Explanation

What is requirements.in?

requirements.in is a high-level dependency file.

It describes:

“These are the packages my project needs.”

Example:

numpy>=1.26

This means:

install numpy
any compatible version newer than 1.26

What is a Lockfile?

The generated:

requirements.txt

is a fully pinned dependency lockfile.

It contains:

exact package versions
every dependency required underneath

Example:

numpy==1.26.4

This guarantees:

reproducible installs
same environment on every machine

Why Use uv pip compile?

Command:

uv pip compile requirements.in -o requirements.txt

does the following:

reads requirements.in
resolves compatible versions
locks exact versions
includes transitive dependencies

Difference Between Top-Level and Transitive Dependencies

Top-Level Dependencies

Packages you explicitly request:

numpy
pandas
mlflow
scikit-learn

Transitive Dependencies

Packages automatically required by those libraries.

Example:

pandas may require:
python-dateutil
pytz
mlflow may require many extra libraries

These appear automatically in:

requirements.txt

Why Exact == Versions Matter

Example:

numpy==1.26.4

This prevents:

unexpected upgrades
dependency conflicts
“works on my machine” problems

It ensures all developers use identical versions.


Resources & Next Steps
📦 Full Code Repository: KodeKloud Learning Labs
💬 Join Discussion: DEV Community - Share your thoughts and questions
💼 Let's Connect: LinkedIn - I'd love to connect with you

Credits
• All labs are from: KodeKloud
• I sincerely appreciate your provision of these valuable resources.

Top comments (0)