fix(python-uv): take into account dependencies on other workspace members#887
fix(python-uv): take into account dependencies on other workspace members#887noritada wants to merge 1 commit into
Conversation
| # For packages in the workspace, exported paths are relative to the workspace root, | ||
| # regardless of where in the workspace uv export is called | ||
| workspace_args = ["workspace", "dir"] | ||
| rc, stdout, stderr = self._uv_runner._uv.run_uv_command(workspace_args, cwd=project_dir) |
There was a problem hiding this comment.
[GENERAL] This change introduces a second run_uv_command invocation inside _build_from_lock_file (for uv workspace dir), but the existing unit tests were not updated and will fail:
tests/unit/workflows/python_uv/test_packager.py::test_build_from_lock_fileassertsself.mock_uv_runner._uv.run_uv_command.assert_called_once(), which now sees two calls (export + workspace dir).tests/unit/workflows/python_uv/test_packager.py::test_build_dependencies_pyproject_with_uv_lockhas the same assertion and will also break.
In addition, those tests set run_uv_command.return_value = (0, b"", b"") (bytes). The new line workspace_dir = stdout.strip() will yield b"" under this mock, which is then passed as cwd to install_requirements. Since install_requirements is mocked in the unit tests this does not fail there, but tests should be updated to use strings (matching production OSUtils.run_subprocess, which returns text=True output) and to explicitly cover the new uv workspace dir path — including the failure branch (rc != 0 raising LockFileError) and verifying that install_requirements is invoked with cwd=workspace_dir rather than project_dir. Without such coverage, regressions in the workspace‑resolution logic (which is the core of this fix) will go undetected.
Example update for test_build_from_lock_file:
def test_build_from_lock_file(self):
# First call: export (returns empty stdout). Second call: workspace dir.
self.mock_uv_runner._uv.run_uv_command.side_effect = [
(0, "", ""),
(0, "/workspace/root", ""),
]
self.builder._build_from_lock_file(
lock_path="/workspace/root/sam-app/uv.lock",
target_dir="/target",
scratch_dir="/scratch",
python_version="3.9",
architecture=X86_64,
config=UvConfig(),
)
self.assertEqual(self.mock_uv_runner._uv.run_uv_command.call_count, 2)
# export runs from project_dir, install runs from the workspace root
, installkwargs = self.mock_uv_runner.install_requirements.call_args
self.assertEqual(install_kwargs["cwd"], "/workspace/root")…bers This commit fixes an issue where an application created as a member of a uv workspace would fail to build if they depended on other workspace members. The following shows the problematic workspace structure and the error message. ``` workspace root ├── lib │ ├── pyproject.toml │ └── src ├── pyproject.toml ├── sam-app │ ├── __init__.py │ ├── app.py │ ├── pyproject.toml │ ├── samconfig.toml │ └── template.yaml └── uv.lock ``` ``` Build Failed Error: PythonUvBuilder:ResolveDependencies - UV package build failed: Failed to build from pyproject.toml: Lock file operation failed: Failed to install dependencies using uv: UV pip install failed: Using CPython 3.13.0 interpreter at: /path/to/workspace/.venv/bin/python3 error: Distribution not found at: file:///path/to/workspace/sam-app/lib ``` Even though `lib` and `sam-app` are in the same directory level in the workspace, the workflow attempts to install `lib` under `sam-app`. In the workflow, `uv export` outputs a list of dependency packages, which is then passed to `uv pip install` for installation. When doing so, [`uv export` outputs relative paths from the workspace root][1]. Therefore, `uv pip install` must be run from the workspace root, not from the application directory. Additionally, dependencies on other packages within the workspace are exported as editable installations (e.g., `-e ./lib`) by default. When this is passed to `uv pip install`, only the `.pth` (path configuration) file for the package will be installed without the package body. To prevent this, the `--no-editable` option needs to be used. [1]: astral-sh/uv#20238
This PR fixes an issue where an application created as a member of a
uv workspace would fail to build if they depended on other workspace
members.
The following shows the problematic workspace structure and the error
message.
Even though
libandsam-appare in the same directory level in theworkspace, the workflow attempts to install
libundersam-app.In the workflow,
uv exportoutputs a list of dependency packages,which is then passed to
uv pip installfor installation. When doingso,
uv exportoutputs relative paths from the workspace root.Therefore,
uv pip installmust be run from the workspace root, notfrom the application directory.
Additionally, dependencies on other packages within the workspace are
exported as editable installations (e.g.,
-e ./lib) by default.When this is passed to
uv pip install, only the.pth(pathconfiguration) file for the package will be installed without the
package body. To prevent this, the
--no-editableoption needs to beused.
Commands to reproduce the build failure
uv init --bare uv init --lib lib sam init --name sam-app --runtime python3.14 --architecture arm64 \ --dependency-manager pip --package-type Zip \ --app-template hello-world cd sam-app uv init uv add lib@../lib # remove requirements.txt and edit the app # edit template.yaml to configure `BuildMethod: python-uv` and `CodeUri: .` sam build --beta-features