Skip to content

Commit dc617d0

Browse files
committed
feat: add support for pyproject.toml via uv & poetry
Implements: [TC-3850](https://redhat.atlassian.net/browse/TC-3850)
1 parent de12f6a commit dc617d0

24 files changed

Lines changed: 3172 additions & 6 deletions

.github/workflows/test.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,12 @@ jobs:
6060
run: |
6161
echo "python-bin-location=$(echo $pythonLocation)/bin" >> $GITHUB_OUTPUT
6262
63+
- name: Install uv
64+
uses: astral-sh/setup-uv@v6
65+
66+
- name: Install Poetry
67+
run: pipx install poetry
68+
6369
- name: setup go
6470
uses: actions/setup-go@v6
6571
with:

README.md

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,8 @@ $ trustify-da-javascript-client license /path/to/package.json
224224
<li><a href="https://www.javascript.com/">JavaScript</a> - <a href="https://pnpm.io/">pnpm</a></li>
225225
<li><a href="https://www.javascript.com/">JavaScript</a> - <a href="https://classic.yarnpkg.com/">Yarn Classic</a> / <a href="https://yarnpkg.com/">Yarn Berry</a></li>
226226
<li><a href="https://go.dev/">Golang</a> - <a href="https://go.dev/blog/using-go-modules/">Go Modules</a></li>
227-
<li><a href="https://www.python.org/">Python</a> - <a href="https://pypi.org/project/pip/">pip Installer</a></li>
227+
<li><a href="https://www.python.org/">Python</a> - <a href="https://pypi.org/project/pip/">pip Installer</a> (<code>requirements.txt</code>)</li>
228+
<li><a href="https://www.python.org/">Python</a> - <a href="https://python-poetry.org/">Poetry</a> / <a href="https://docs.astral.sh/uv/">uv</a> (<code>pyproject.toml</code>)</li>
228229
<li><a href="https://gradle.org/">Gradle (Groovy and Kotlin DSL)</a> - <a href="https://gradle.org/install/">Gradle Installation</a></li>
229230
<li><a href="https://www.rust-lang.org/">Rust</a> - <a href="https://doc.rust-lang.org/cargo/">Cargo</a></li>
230231
</ul>
@@ -391,7 +392,30 @@ version = "1.10"
391392
log = "0.4" # trustify-da-ignore
392393
```
393394
394-
All of the 6 above examples are valid for marking a package to be ignored
395+
396+
<em>Python pyproject.toml</em> users can add a comment with <code>#exhortignore</code> (or <code># trustify-da-ignore</code>) next to the dependency in <code>pyproject.toml</code>.
397+
398+
PEP 621 style (<code>[project]</code> dependencies):
399+
```toml
400+
[project]
401+
dependencies = [
402+
"flask>=2.0.3",
403+
"requests>=2.25.1",
404+
"uvicorn>=0.17.0", #exhortignore
405+
"click>=8.0.4", # trustify-da-ignore
406+
]
407+
```
408+
409+
Poetry style (<code>[tool.poetry.dependencies]</code>):
410+
```toml
411+
[tool.poetry.dependencies]
412+
flask = "^2.0.3"
413+
requests = "^2.25.1"
414+
uvicorn = "^0.17.0" #exhortignore
415+
click = "^8.0.4" # trustify-da-ignore
416+
```
417+
418+
All of the above examples are valid for marking a package to be ignored
395419
</li>
396420
</ul>
397421
@@ -421,6 +445,8 @@ let options = {
421445
'TRUSTIFY_DA_PIP3_PATH' : '/path/to/pip3',
422446
'TRUSTIFY_DA_PYTHON_PATH' : '/path/to/python',
423447
'TRUSTIFY_DA_PIP_PATH' : '/path/to/pip',
448+
'TRUSTIFY_DA_UV_PATH' : '/path/to/uv',
449+
'TRUSTIFY_DA_POETRY_PATH' : '/path/to/poetry',
424450
'TRUSTIFY_DA_GRADLE_PATH' : '/path/to/gradle',
425451
'TRUSTIFY_DA_CARGO_PATH' : '/path/to/cargo',
426452
// Workspace root for monorepos (Cargo, npm/pnpm/yarn); lock file expected here
@@ -567,6 +593,16 @@ following keys for setting custom paths for the said executables.
567593
<td>TRUSTIFY_DA_CARGO_PATH</td>
568594
</tr>
569595
<tr>
596+
<td><a href="https://docs.astral.sh/uv/">uv</a></td>
597+
<td><em>uv</em></td>
598+
<td>TRUSTIFY_DA_UV_PATH</td>
599+
</tr>
600+
<tr>
601+
<td><a href="https://python-poetry.org/">Poetry</a></td>
602+
<td><em>poetry</em></td>
603+
<td>TRUSTIFY_DA_POETRY_PATH</td>
604+
</tr>
605+
<tr>
570606
<td>Workspace root (monorepos)</td>
571607
<td></td>
572608
<td>workspaceDir / TRUSTIFY_DA_WORKSPACE_DIR</td>
@@ -619,6 +655,24 @@ TRUSTIFY_DA_GO_MVS_LOGIC_ENABLED=false
619655
620656
#### Python Support
621657
658+
The client supports two Python manifest formats:
659+
660+
- **`requirements.txt`** — uses pip/pip3 to resolve dependencies
661+
- **`pyproject.toml`** — uses [uv](https://docs.astral.sh/uv/) or [Poetry](https://python-poetry.org/) to resolve dependencies
662+
663+
##### pyproject.toml
664+
665+
For `pyproject.toml` projects, the client detects which tool manages the project by checking for lock files:
666+
- If `poetry.lock` is present and `[tool.poetry]` is defined, **Poetry** is used (`poetry show --tree` and `poetry show --all`)
667+
- If `uv.lock` is present, **uv** is used (`uv export --format requirements.txt --frozen --no-hashes`)
668+
- If neither lock file is found, an error is thrown
669+
670+
Both PEP 621 (`[project]` dependencies) and Poetry-style (`[tool.poetry.dependencies]`) are supported.
671+
672+
Custom executable paths can be set via `TRUSTIFY_DA_UV_PATH` and `TRUSTIFY_DA_POETRY_PATH`.
673+
674+
##### requirements.txt
675+
622676
By default, For python support, the api assumes that the package is installed using the pip/pip3 binary on the system PATH, or using the customized
623677
Binaries passed to environment variables. In any case, If the package is not installed , then an error will be thrown.
624678

src/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@ export {
7474
* TRUSTIFY_DA_CONTINUE_ON_ERROR?: string | undefined,
7575
* batchMetadata?: boolean | undefined,
7676
* TRUSTIFY_DA_BATCH_METADATA?: string | undefined,
77+
* TRUSTIFY_DA_UV_PATH?: string | undefined,
78+
* TRUSTIFY_DA_POETRY_PATH?: string | undefined,
7779
* [key: string]: string | number | boolean | string[] | undefined,
7880
* }} Options
7981
*/

src/provider.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import Javascript_npm from './providers/javascript_npm.js';
88
import Javascript_pnpm from './providers/javascript_pnpm.js';
99
import Javascript_yarn from './providers/javascript_yarn.js';
1010
import pythonPipProvider from './providers/python_pip.js'
11+
import pythonPyprojectProvider from './providers/python_pyproject.js'
1112
import rustCargoProvider from './providers/rust_cargo.js'
1213

1314
/** @typedef {{ecosystem: string, contentType: string, content: string}} Provided */
@@ -26,6 +27,7 @@ export const availableProviders = [
2627
new Javascript_npm(),
2728
golangGomodulesProvider,
2829
pythonPipProvider,
30+
pythonPyprojectProvider,
2931
rustCargoProvider]
3032

3133
/**

src/providers/python_pip.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ function addAllDependencies(source, dep, sbom) {
8787
sbom.addDependency(source, targetPurl)
8888
let directDeps = dep["dependencies"]
8989
if (directDeps !== undefined && directDeps.length > 0) {
90-
directDeps.forEach((dependency) => { addAllDependencies(toPurl(dep["name"],dep["version"]), dependency, sbom) })
90+
directDeps.forEach((dependency) => { addAllDependencies(toPurl(dep["name"], dep["version"]), dependency, sbom) })
9191
}
9292
}
9393

@@ -123,7 +123,7 @@ async function getIgnoredDependencies(manifest) {
123123
async function handleIgnoredDependencies(manifest, sbom, opts = {}) {
124124
let ignoredDeps = await getIgnoredDependencies(manifest)
125125
let matchManifestVersions = getCustom("MATCH_MANIFEST_VERSIONS", "true", opts);
126-
if(matchManifestVersions === "true") {
126+
if (matchManifestVersions === "true") {
127127
const ignoredDepsVersion = ignoredDeps.filter(dep => dep.version !== undefined);
128128
sbom.filterIgnoredDepsIncludingVersion(ignoredDepsVersion.map(dep => dep.toString()))
129129
} else {
@@ -150,7 +150,7 @@ function getPythonPipBinaries(binaries, opts) {
150150
invokeCommand(python, ['--version'])
151151
invokeCommand(pip, ['--version'])
152152
} catch (error) {
153-
throw new Error(`Failed checking for python/pip binaries from supplied environment variables`, {cause: error})
153+
throw new Error(`Failed checking for python/pip binaries from supplied environment variables`, { cause: error })
154154
}
155155
}
156156
binaries.pip = pip
@@ -240,6 +240,6 @@ async function getSbomForComponentAnalysis(manifest, opts = {}) {
240240
* @param version
241241
* @return {PackageURL}
242242
*/
243-
function toPurl(name,version) {
243+
function toPurl(name, version) {
244244
return new PackageURL('pypi', undefined, name, version, undefined, undefined);
245245
}

0 commit comments

Comments
 (0)