feat: Add uv support for python packaging#723
feat: Add uv support for python packaging#723antonbabenko merged 9 commits intoterraform-aws-modules:masterfrom
Conversation
f44447c to
6a499cf
Compare
fdef682 to
88d8221
Compare
|
@rivhar - thanks for putting the work into this. Quick question - you mention in your description: "Added logic to recognize uv as a build system when a uv.lock or specific pyproject.toml configuration is present." Perhaps I am mistaken, but looking at the changes to I could be missing something obvious :) |
|
@joshshand-coop - You're right to point it out. The initial logic was a bit too strict. I've updated package.py to support that exact 'auto-lock' behavior: if uv.lock is missing but a pyproject.toml is found, the script now automatically triggers uv lock to generate it before proceeding with the build. Pushing the changes now—thanks for the feedback:) Please find "Additional Updates in this PR" section in PR description at the end in order to get idea of the changes made. |
688b4f4 to
04932fd
Compare
antonbabenko
left a comment
There was a problem hiding this comment.
Looks pretty good, and it works 95% of the times :) Please address the remaining comments before we can merge it.
package.py
Outdated
|
|
||
| if not os.path.exists(uv_lock_file) and os.path.exists(pyproject_file): | ||
| try: | ||
| check_call([uv_exec, "lock"], cwd=project_path) |
There was a problem hiding this comment.
This would write uv.lock into the source directory, which can be read-only.
We either need to require uv.lock to exist, or generate in temp directory (if it doesn't exist).
There was a problem hiding this comment.
I would suggest that it attempts to write to the source directory (if uv.lock doesn't exist) with a fallback of a temp directory.
Reasoning is that the user may wish to then commit that generated lock file to source control after running uv via this module. If it's in a temp dir it's possible it may not exist post execution.
package.py
Outdated
| with open(requirements_file, "r") as f: | ||
| for line in f: | ||
| stripped = line.strip() | ||
| if stripped == "-e .": |
There was a problem hiding this comment.
This line bites me during my own test run (I rely on this command).
Let's make this conditional or document the required workaround (I don't know it myself), and don't skip it every time.
There was a problem hiding this comment.
@antonbabenko
Thanks for the feedback, Anton:)
I’ve updated the logic to address both points:
Read-only Safety: uv lock now runs inside the temporary directory (temp_dir) instead of the source directory, ensuring it works even if the source is read-only.
Conditional Skip: The -e . stripping is now scoped specifically to Lambda packaging builds (by checking for runtime and artifacts_dir in the query). This preserves your local test runs while keeping Lambda builds safe.
Please let me know if there is something else!
There was a problem hiding this comment.
@antonbabenko Thanks for the feedback, Anton:)
I’ve updated the logic to address both points: Read-only Safety: uv lock now runs inside the temporary directory (temp_dir) instead of the source directory, ensuring it works even if the source is read-only. Conditional Skip: The -e . stripping is now scoped specifically to Lambda packaging builds (by checking for runtime and artifacts_dir in the query). This preserves your local test runs while keeping Lambda builds safe.
Please let me know if there is something else!
@rivhar see my comment above re temp dir if you haven't :-)
There was a problem hiding this comment.
@joshshand-coop,
The function now generates uv.lock in a temp directory if it doesn’t exist, and then attempts to copy it back to the source directory. Existing uv.lock is used as-is. This should cover both safe temp execution and the option to commit the lock file afterward.
There was a problem hiding this comment.
@rivhar I think always generating the uv.lock in a temporary directory makes sense to ensure stability of uv execution.
After generation, if the source directory is writeable, you could then copy from temp to source as well?
If it IS read-only - just log an informational message instead.
There was a problem hiding this comment.
@joshshand-coop
I have updated the function so that uv.lock is always generated in a temporary directory for stable execution. After generation, if the uv lock was generated and source directory is writable, it’s copied back; if it’s read-only, we just log an informational message. This ensures consistent behavior without failing on read-only source paths.
There was a problem hiding this comment.
@joshshand-coop I have updated the function so that uv.lock is always generated in a temporary directory for stable execution. After generation, if the uv lock was generated and source directory is writable, it’s copied back; if it’s read-only, we just log an informational message. This ensures consistent behavior without failing on read-only source paths.
Sounds great, nice one
366edcd to
49f7e43
Compare
d7a22ee to
6bfe283
Compare
This commit fixes 3 critical issues identified during PR review: 1. CRITICAL: Silent failure in error handling (package.py:1607-1619) - Replaced broad 'except Exception' with specific exceptions - Changed debug logging to warning level for visibility - Added actionable error messages for users - Added success logging when lock file is saved 2. CRITICAL: Unprotected file removal (package.py:1621-1631) - Wrapped os.remove() calls in try-except blocks - Prevents crashes after successful build - Follows project's TemporaryCopy pattern 3. Enhanced error handling for lock generation (package.py:1520-1548) - Pre-check uv availability before attempting to use it - Added specific CalledProcessError handling - Improved error messages with exit codes and remediation steps - Added success logging 4. Test coverage: Added 8 comprehensive tests - Tests for basic functionality with existing lock - Tests for auto-lock generation - Tests for error scenarios (missing uv, generation failures) - Tests for edge cases (read-only directories) - Tests for build system detection Impact: - Prevents silent failures in production - Provides clear, actionable error messages - Improves debuggability with proper logging - Adds test coverage for critical paths Related: terraform-aws-modules#723, terraform-aws-modules#673
antonbabenko
left a comment
There was a problem hiding this comment.
Thanks a lot to everyone for the contribution to this module!
8dc36e8
into
terraform-aws-modules:master
## [8.4.0](v8.3.0...v8.4.0) (2026-01-26) ### Features * Add uv support for python packaging ([#723](#723)) ([8dc36e8](8dc36e8))
|
This PR is included in version 8.4.0 🎉 |
|
I'm going to lock this pull request because it has been closed for 30 days ⏳. This helps our maintainers find and focus on the active issues. If you have found a problem that seems related to this change, please open a new issue and complete the issue template so we can capture all the details necessary to investigate further. |
Description
This PR adds support for the uv package manager for Python Lambda functions. It introduces a new build system autodetection logic in package.py and provides updated build environments via Docker.
Key changes include:
Autodetection: Added logic to recognize uv as a build system when a uv.lock or specific pyproject.toml configuration is present.
Docker Support: Updated the Lambda build Dockerfile to include uv (pinned to version 0.9.21) to ensure stable, containerized builds.
Layer Compatibility: Integrated uv export with the existing prefix_in_zip logic to ensure dependencies are correctly placed in the python/ folder for Lambda Layers.
Examples: Created new fixtures and examples in examples/fixtures/python-app-uv and examples/build-package to demonstrate local and Docker-based UV builds.
Motivation and Context
uv is an extremely fast Python package manager that is gaining significant traction in the community. Adding native support allows users to benefit from faster build times and reliable dependency resolution (via uv.lock) without leaving the terraform-aws-lambda ecosystem.
Closes #673
Breaking Changes
None. This change is purely additive. Existing Poetry and Pip-based workflows remain untouched. The autodetection logic is designed to fall back to existing methods if uv specific files are not found.
How Has This Been Tested?
[x] I have updated at least one of the examples/* to demonstrate and validate my change(s)
[x] I have tested and validated these changes using one or more of the provided examples/* projects
[x] I have executed pre-commit run -a on my pull request
Detailed testing performed:
Unit Testing: Ran pytest tests/test_package_toml.py. All tests for the new UV build system detection passed.
Integration (Local): Deployed module.package_dir_uv_no_docker using a local uv binary; verified the resulting ZIP contained all dependencies.
Integration (Docker): Deployed module.package_dir_uv using the updated Dockerfile; verified uv export and pip install executed correctly inside the container.
Layer Verification: Inspected the ZIP artifact for module.lambda_layer_uv using zipinfo. Verified all dependencies are correctly prefixed with python/ for Lambda runtime compatibility.
Additional Updates in this PR
Automatic uv.lock generation from pyproject.toml
Introduced a new behavior: if uv.lock is missing but pyproject.toml exists, the build automatically runs uv lock to generate it.
This ensures builds succeed without a pre-existing lock file.
Added a dedicated fixture python-app-uv-no-lock in examples/fixtures/ to validate this behavior.
Tested all the modules including the new one related to missing uv.lock