Skip to content

Commit 5bc95fa

Browse files
tomaDevclaude
andcommitted
fix: python-uv cached build copies dependencies to the build dir
`sam build --cached` (incremental build) invokes the python-uv workflow with a `dependencies_dir` set. Two bugs made that path fail with `CopyDependencies - [Errno 2] No such file or directory`: 1. `PythonUvWorkflow._setup_build_actions` constructed `CopyDependenciesAction` with `artifact_dir` and `destination_dir` swapped. Dependencies are installed into `dependencies_dir`, so that is the source of the copy and `artifacts_dir` the destination; as written the action listed the not-yet-created artifacts directory and never populated it. 2. `UvRunner.install_requirements` passed `--target` through verbatim. UV runs with `cwd` set to the project directory, so the relative `.aws-sam/deps/<uuid>` dependencies dir was created under the source directory instead of the build root. Resolve `--target` to an absolute path before invoking UV. Adds regression coverage: workflow unit test asserts the `CopyDependenciesAction` source/artifact/dest dirs; packager unit test asserts a relative `--target` is made absolute; the `dependencies_dir` integration test now asserts dependencies are present in the artifacts dir. Fixes aws#864 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c62aade commit 5bc95fa

5 files changed

Lines changed: 35 additions & 5 deletions

File tree

aws_lambda_builders/workflows/python_uv/packager.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,10 @@ def install_requirements(
136136
# Add requirements file
137137
args.extend(["-r", requirements_path])
138138

139-
# Add target directory
140-
args.extend(["--target", target_dir])
139+
# Resolve --target to an absolute path: UV runs with cwd set to the project directory, so a
140+
# relative target (e.g. the incremental-build dependencies dir) would otherwise be created
141+
# under the source directory instead of the build root.
142+
args.extend(["--target", os.path.abspath(target_dir)])
141143

142144
# Add configuration arguments
143145
args.extend(config.to_uv_args())

aws_lambda_builders/workflows/python_uv/workflow.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -139,13 +139,15 @@ def _setup_build_actions(self, source_dir, artifacts_dir, scratch_dir, manifest_
139139
)
140140
)
141141

142-
# Advanced case: Copy dependencies from dependencies_dir to artifacts_dir if configured
142+
# Advanced case: copy dependencies from dependencies_dir to artifacts_dir if configured.
143+
# Dependencies were installed into dependencies_dir above, so that is the copy source
144+
# (artifact_dir) and artifacts_dir is the destination.
143145
if self.dependencies_dir and self.combine_dependencies:
144146
self.actions.append(
145147
CopyDependenciesAction(
146148
source_dir=source_dir,
147-
artifact_dir=artifacts_dir,
148-
destination_dir=self.dependencies_dir,
149+
artifact_dir=self.dependencies_dir,
150+
destination_dir=artifacts_dir,
149151
maintain_symlinks=False,
150152
)
151153
)

tests/integration/workflows/python_uv/test_python_uv.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,9 @@ def test_workflow_with_dependencies_dir(self):
121121
dependencies_files = set(os.listdir(self.dependencies_dir))
122122
self.assertEqual(expected_dependencies.intersection(dependencies_files), expected_dependencies)
123123

124+
# combine_dependencies defaults to True, so dependencies must also be copied into the artifacts dir.
125+
self.assertEqual(expected_dependencies.intersection(output_files), expected_dependencies)
126+
124127
@skipIf(which("uv") is None, "uv not available")
125128
def test_workflow_builds_numpy_successfully(self):
126129
"""Test that UV can build numpy (same as PIP workflow test)"""

tests/unit/workflows/python_uv/test_packager.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,22 @@ def test_install_requirements_success(self):
125125
self.assertIn("-r", args_called)
126126
self.assertIn("/path/to/requirements.txt", args_called)
127127

128+
def test_install_requirements_resolves_relative_target_to_absolute(self):
129+
# UV runs with cwd=project_dir, so a relative --target must be resolved to an absolute path
130+
# first, otherwise dependencies land under the source dir instead of the build root.
131+
self.mock_subprocess_uv.run_uv_command.return_value = (0, "success", "")
132+
133+
self.uv_runner.install_requirements(
134+
requirements_path="/path/to/requirements.txt",
135+
target_dir=os.path.join(".aws-sam", "deps", "abc-123"),
136+
scratch_dir="/scratch",
137+
)
138+
139+
args_called = self.mock_subprocess_uv.run_uv_command.call_args[0][0]
140+
target_value = args_called[args_called.index("--target") + 1]
141+
self.assertTrue(os.path.isabs(target_value), f"--target should be absolute, got: {target_value}")
142+
self.assertEqual(target_value, os.path.abspath(os.path.join(".aws-sam", "deps", "abc-123")))
143+
128144
def test_install_requirements_failure(self):
129145
self.mock_subprocess_uv.run_uv_command.return_value = (1, "", "error message")
130146

tests/unit/workflows/python_uv/test_workflow.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,13 @@ def test_workflow_sets_up_actions_with_dependencies_dir(self):
7474
self.assertIsInstance(self.workflow.actions[2], CopyDependenciesAction)
7575
self.assertIsInstance(self.workflow.actions[3], CopySourceAction)
7676

77+
# Dependencies are installed into dependencies_dir, then copied into artifacts_dir;
78+
# artifact_dir is the copy source and dest_dir the destination (must not be swapped).
79+
copy_deps_action = self.workflow.actions[2]
80+
self.assertEqual(copy_deps_action.source_dir, "source")
81+
self.assertEqual(copy_deps_action.artifact_dir, "deps")
82+
self.assertEqual(copy_deps_action.dest_dir, "artifacts")
83+
7784
def test_workflow_sets_up_actions_without_download_dependencies_and_dependencies_dir(self):
7885
self.workflow = PythonUvWorkflow(
7986
"source",

0 commit comments

Comments
 (0)