Skip to content

IN-1298 - Spike to replace pipenv with uv #58

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

ghukill
Copy link
Contributor

@ghukill ghukill commented May 28, 2025

Purpose and background context

This PR updates the python AWS lambda template to use uv instead of pipenv.

While this code and PR were intended as a demo and spike to facilitate internal discussions about uv, it may very well serve or act as the foundation for a real update to the lambda template.

Comments in the code changes are included to point out particularly interesting uv things.

NOTE: regarding failing Github actions, see this Jira comment

How can a reviewer manually see the effects of these changes?

It is advised to completely remove your old python-lambda-template project to avoid any pipenv cross contamination. Or if you're feeling bold, before pulling down this branch pipenv --rm can be used to remove the pipenv virtual environment.

Once the branch is checked out, a "normal" installation could be followed:

make install

Some notes from the install:

  • it should create a .venv virtual environment in the same directory
  • it should install pre-commit git hooks
  • it should install all dependencies
  • the .venv virtual env and the pre-commit hooks are only installed/created if they don't exist

Then, can run some tests and linting:

make test
make lint

The API for uv is quite similar to pipenv, poetry, etc.

To start an IPython shell:

uv run ipython

A fun test - create a python file called hello.py:

print("Hello world!")

Then invoke it with uv:

# can invoke python scripts directly
uv run hello.py

# or, can include explicit python command and then the script
uv run python hello.py

To get a sense for how fast uv can be -- and not just celebrating speed for speed's sake, but considering that quick installs and builds allow for more time thinking and coding -- you could recreate the virtual environment:

rm -r .venv
make install

And much more! Additional things to try with uv can be discussed async of this PR.

Includes new or updated dependencies?

YES

Changes expectations for external applications?

YES: Before this template code can be used by a project, Github actions will need to be updated to intelligently use pipenv or uv depending on the project as we incrementally convert them.

What are the relevant tickets?

Developer

  • All new ENV is documented in README
  • All new ENV has been added to staging and production environments
  • All related Jira tickets are linked in commit message(s)
  • Stakeholder approval has been confirmed (or is not needed)

Code Reviewer(s)

  • The commit message is clear and follows our guidelines (not just this PR message)
  • There are appropriate tests covering any new functionality
  • The provided documentation is sufficient for understanding any new functionality introduced
  • Any manual tests have been performed and verified
  • New dependencies are appropriate or there were no changes

Why these changes are being introduced:

uv is a modern python project and dependency manager with increasingly
attractive features and community support.  By contrast, pipenv is
feeling a bit stale in terms of features, community support, and
even performance.

Additionally, without going into great detail here, uv is part of
a larger ecocsystem of python tooling that work very well together,
including uv, ruff, and ty (type checking).

How this addresses that need:
* Virtually anywhere pipenv was used, uv is now used
* Makefile updated to use uv
* Makefile installation recipes updated to utilize uv best
* Dockerfile updated to align with uv installation approaches

Side effects of this change:
* Before this template code can be used by a project,
Github actions will need to be updated to intelligently
use pipenv or uv depending on the project as we incrementally
convert them.

Relevant ticket(s):
* https://mitlibraries.atlassian.net/browse/IN-1298
Comment on lines +10 to +12
RUN cd ${LAMBDA_TASK_ROOT} && \
uv export --format requirements-txt --no-hashes --no-dev > requirements.txt && \
uv pip install -r requirements.txt --target "${LAMBDA_TASK_ROOT}" --system
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note the pipenv approach was using a --target argument to install the libraries in a specific place.

For uv, the equivalant is using uv pip which provides a more pip-like API for specific tasks, in this case installing to a specific location instead of the active virtual environment.

I believe there are alternatives to how we could construct the python environment for AWS lambdas, but this approach is readily understandable and conceptually mirrors our previous approach.

Some flags:

  • --no-hashes: excludes hashes from library versions where the version may be the same but the hash changes; which can wreak havoc with Docker layers
  • --no-dev: skips the dev group of dependencies

Comment on lines +15 to +30
install: .venv .git/hooks/pre-commit # Install Python dependencies and create virtual environment if not exists
uv sync --dev

.venv: # Creates virtual environment if not found
@echo "Creating virtual environment at .venv..."
uv venv .venv

.git/hooks/pre-commit: # Sets up pre-commit hook if not setup
@echo "Installing pre-commit hooks..."
uv run pre-commit install

venv: .venv # Create the Python virtual environment

update: # Update Python dependencies
uv lock --upgrade
uv sync --dev
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is 50/50 new functionality from uv and some Makefile maneuvers we could use.

When running make install, while debatable, one approach is that it creates the virtual environment for you. I'm also drawn to a very explicit make venv (which we actually still have here!) but this kind of combines both. If you run make install and a .venv directory doesn't exist, courtesy of line 18, it'll get created. This leans into the Makefile "build" mentality with built-in checks for assets.

Similar story for pre-commit.

The net effect is that make install creates a virtual env if it doesn't exist, but remains idempotent to re-run (won't clobber a pre-existing environment).


ruff: # Run 'ruff' linter and print a preview of errors
pipenv run ruff check .
uv run ruff check .
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commenting here arbitraily: nice example of uv being largely a pipenv drop-in replacement if you want to have similar ergonomics.

Comment on lines +6 to +20
dependencies = [
"sentry-sdk>=2.29.1",
]

[dependency-groups]
dev = [
"black>=25.1.0",
"coveralls>=4.0.1",
"ipython>=9.2.0",
"mypy>=1.15.0",
"pip-audit>=2.9.0",
"pre-commit>=4.2.0",
"pytest>=8.3.5",
"ruff>=0.11.11",
]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dependencies are now managed in pyproject.toml vs a Pipfile. This is a very active area in the python ecosystem. uv utilizes a uv.lock file, and will likely continue to do so (for advanced usage purposes), but there is a new proposed pylock.toml file via PEP-751 that may get used.

To me, another reason to move to uv: more than any other project tooling, it's evolving with the community around new standards (in many cases driving it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant