Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions aws_lambda_builders/workflows/python_uv/packager.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,10 @@ def install_requirements(
# Add requirements file
args.extend(["-r", requirements_path])

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

# Add configuration arguments
args.extend(config.to_uv_args())
Expand Down
8 changes: 5 additions & 3 deletions aws_lambda_builders/workflows/python_uv/workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,13 +139,15 @@ def _setup_build_actions(self, source_dir, artifacts_dir, scratch_dir, manifest_
)
)

# Advanced case: Copy dependencies from dependencies_dir to artifacts_dir if configured
# Advanced case: copy dependencies from dependencies_dir to artifacts_dir if configured.
# Dependencies were installed into dependencies_dir above, so that is the copy source
# (artifact_dir) and artifacts_dir is the destination.
if self.dependencies_dir and self.combine_dependencies:
self.actions.append(
CopyDependenciesAction(
source_dir=source_dir,
artifact_dir=artifacts_dir,
destination_dir=self.dependencies_dir,
artifact_dir=self.dependencies_dir,
destination_dir=artifacts_dir,
maintain_symlinks=False,
)
)
Expand Down
3 changes: 3 additions & 0 deletions tests/integration/workflows/python_uv/test_python_uv.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ def test_workflow_with_dependencies_dir(self):
dependencies_files = set(os.listdir(self.dependencies_dir))
self.assertEqual(expected_dependencies.intersection(dependencies_files), expected_dependencies)

# combine_dependencies defaults to True, so dependencies must also be copied into the artifacts dir.
self.assertEqual(expected_dependencies.intersection(output_files), expected_dependencies)

@skipIf(which("uv") is None, "uv not available")
def test_workflow_builds_numpy_successfully(self):
"""Test that UV can build numpy (same as PIP workflow test)"""
Expand Down
16 changes: 16 additions & 0 deletions tests/unit/workflows/python_uv/test_packager.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,22 @@ def test_install_requirements_success(self):
self.assertIn("-r", args_called)
self.assertIn("/path/to/requirements.txt", args_called)

def test_install_requirements_resolves_relative_target_to_absolute(self):
# UV runs with cwd=project_dir, so a relative --target must be resolved to an absolute path
# first, otherwise dependencies land under the source dir instead of the build root.
self.mock_subprocess_uv.run_uv_command.return_value = (0, "success", "")

self.uv_runner.install_requirements(
requirements_path="/path/to/requirements.txt",
target_dir=os.path.join(".aws-sam", "deps", "abc-123"),
scratch_dir="/scratch",
)

args_called = self.mock_subprocess_uv.run_uv_command.call_args[0][0]
target_value = args_called[args_called.index("--target") + 1]
self.assertTrue(os.path.isabs(target_value), f"--target should be absolute, got: {target_value}")
self.assertEqual(target_value, os.path.abspath(os.path.join(".aws-sam", "deps", "abc-123")))

def test_install_requirements_failure(self):
self.mock_subprocess_uv.run_uv_command.return_value = (1, "", "error message")

Expand Down
7 changes: 7 additions & 0 deletions tests/unit/workflows/python_uv/test_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ def test_workflow_sets_up_actions_with_dependencies_dir(self):
self.assertIsInstance(self.workflow.actions[2], CopyDependenciesAction)
self.assertIsInstance(self.workflow.actions[3], CopySourceAction)

# Dependencies are installed into dependencies_dir, then copied into artifacts_dir;
# artifact_dir is the copy source and dest_dir the destination (must not be swapped).
copy_deps_action = self.workflow.actions[2]
self.assertEqual(copy_deps_action.source_dir, "source")
self.assertEqual(copy_deps_action.artifact_dir, "deps")
self.assertEqual(copy_deps_action.dest_dir, "artifacts")

def test_workflow_sets_up_actions_without_download_dependencies_and_dependencies_dir(self):
self.workflow = PythonUvWorkflow(
"source",
Expand Down