feat(python): add uv package manager support (TC-3855)#428
feat(python): add uv package manager support (TC-3855)#428a-oren wants to merge 4 commits intoguacsec:mainfrom
Conversation
…ackage manager support TC-3855 Add automatic detection and support for Python projects managed by the uv package manager. When uv.lock is present alongside pyproject.toml, the new PythonProviderFactory selects PythonUvProvider (using uv pip list/show for dependency resolution) instead of the pip-based PythonPyprojectProvider. - Add PythonProviderFactory with Map-based lock file detection pattern - Add PythonUvProvider with CycloneDX SBOM generation (stack + component) - Add PyprojectTomlUtils shared utility to deduplicate TOML parsing logic - Refactor PythonPyprojectProvider to use PyprojectTomlUtils - Fix PEP 508 extras stripping in getDependencyName (e.g. requests[security]) - Update Ecosystem.resolveProvider() to delegate to PythonProviderFactory - Add test fixtures and 15 unit tests for the uv provider - Document uv support, TRUSTIFY_DA_UV_PATH, and CLI examples in README Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Assisted-by: Claude Code
TC-3855 The PythonUvProvider unit tests require the uv binary to be available, since the constructor eagerly validates it via Operations.getExecutable(). This follows the same pattern as other providers (cargo, go, gradle). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Assisted-by: Claude Code
Signed-off-by: Adva Oren <aoren@redhat.com>
|
/review |
1 similar comment
|
/review |
ruromero
left a comment
There was a problem hiding this comment.
Thanks for the PR this one is not trivial. I made some comments.
| return deps; | ||
| } | ||
|
|
||
| static String canonicalize(String name) { |
There was a problem hiding this comment.
this method is duplicated in PythonPyprojectProvider. It can be extracted to PyprojectTomlUtils
| collectedIgnoredDeps = PyprojectTomlUtils.collectIgnoredDeps(manifestPath, getToml()); | ||
| } | ||
|
|
||
| static final class UvPackage { |
There was a problem hiding this comment.
you could implement this as a Record
| } | ||
| } | ||
|
|
||
| static final class UvDependencyData { |
There was a problem hiding this comment.
Same, this is better implemented as a Record.
|
|
||
| ##### uv Support | ||
|
|
||
| When a `uv.lock` file is present alongside `pyproject.toml`, the client automatically uses the [uv](https://docs.astral.sh/uv/) package manager for dependency resolution instead of pip. Dependency data is collected via `uv pip list --format=json` and `uv pip show`. No additional configuration is required — the provider is selected automatically based on lock file detection. |
There was a problem hiding this comment.
In the JS implementation we use uv export --format requirements.txt --frozen --no-hashes --no-dev instead of uv pip list + uv pip show
https://github.com/guacsec/trustify-da-javascript-client/blob/main/src/providers/python_uv.js#L47
By using the --no-dev you skip dev deps which we don't scan and also you will use the uv.lock file
Summary
PythonUvProviderandPythonProviderFactoryfor automatic uv package manager detection viauv.lockpresenceuv pip list --format=json+uv pip showfor CycloneDX SBOM generation (stack + component)PyprojectTomlUtils, refactor bothPythonPyprojectProviderandPythonUvProviderto use itgetDependencyName()(e.g.requests[security]→requests) affecting all Python providersTRUSTIFY_DA_UV_PATH, and CLI examples in READMETest plan
mvn test -Dtest="Python_Uv_Provider_Test" -Dskip.junit_platform=true— 15 uv provider tests passmvn test -Dtest="Python_Pyproject_Provider_Test" -Dskip.junit_platform=true— 22 existing pyproject tests pass (regression)mvn test -Dtest="PythonControllerRealEnvTest#get_Dependency_Name*" -Dskip.junit_platform=true— extras fix tests passmvn spotless:check— formatting passesjava -jar trustify-da-java-client-cli.jar sbom /path/to/uv-project/pyproject.tomlproduces correct CycloneDX SBOMAcceptance Criteria
PythonProviderFactory.create()returnsPythonUvProviderwhenuv.lockexistsPythonProviderFactory.create()falls back toPythonPyprojectProviderwhen nouv.lockPythonUvProviderdiscovers uv binary viaOperations.getExecutable("uv", "--version")TRUSTIFY_DA_UV_PATHvalidateLockFile()throwsIllegalStateExceptionwhenuv.lockmissingprovideStack()andprovideComponent()generate valid CycloneDX SBOMsrequirements.txtandpyproject.tomlflows unchanged#trustify-da-ignore/#exhortignorepatterns work in uv context🤖 Generated with Claude Code