Skip to content

Commit cf40017

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

24 files changed

Lines changed: 3161 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>
@@ -379,7 +380,30 @@ version = "1.10"
379380
log = "0.4" # trustify-da-ignore
380381
```
381382
382-
All of the 6 above examples are valid for marking a package to be ignored
383+
384+
<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>.
385+
386+
PEP 621 style (<code>[project]</code> dependencies):
387+
```toml
388+
[project]
389+
dependencies = [
390+
"flask>=2.0.3",
391+
"requests>=2.25.1",
392+
"uvicorn>=0.17.0", #exhortignore
393+
"click>=8.0.4", # trustify-da-ignore
394+
]
395+
```
396+
397+
Poetry style (<code>[tool.poetry.dependencies]</code>):
398+
```toml
399+
[tool.poetry.dependencies]
400+
flask = "^2.0.3"
401+
requests = "^2.25.1"
402+
uvicorn = "^0.17.0" #exhortignore
403+
click = "^8.0.4" # trustify-da-ignore
404+
```
405+
406+
All of the above examples are valid for marking a package to be ignored
383407
</li>
384408
385409
</ul>
@@ -410,6 +434,8 @@ let options = {
410434
'TRUSTIFY_DA_PIP3_PATH' : '/path/to/pip3',
411435
'TRUSTIFY_DA_PYTHON_PATH' : '/path/to/python',
412436
'TRUSTIFY_DA_PIP_PATH' : '/path/to/pip',
437+
'TRUSTIFY_DA_UV_PATH' : '/path/to/uv',
438+
'TRUSTIFY_DA_POETRY_PATH' : '/path/to/poetry',
413439
'TRUSTIFY_DA_GRADLE_PATH' : '/path/to/gradle',
414440
'TRUSTIFY_DA_CARGO_PATH' : '/path/to/cargo',
415441
// Workspace root for monorepos (Cargo, npm/pnpm/yarn); lock file expected here
@@ -556,6 +582,16 @@ following keys for setting custom paths for the said executables.
556582
<td>TRUSTIFY_DA_CARGO_PATH</td>
557583
</tr>
558584
<tr>
585+
<td><a href="https://docs.astral.sh/uv/">uv</a></td>
586+
<td><em>uv</em></td>
587+
<td>TRUSTIFY_DA_UV_PATH</td>
588+
</tr>
589+
<tr>
590+
<td><a href="https://python-poetry.org/">Poetry</a></td>
591+
<td><em>poetry</em></td>
592+
<td>TRUSTIFY_DA_POETRY_PATH</td>
593+
</tr>
594+
<tr>
559595
<td>Workspace root (monorepos)</td>
560596
<td></td>
561597
<td>workspaceDir / TRUSTIFY_DA_WORKSPACE_DIR</td>
@@ -608,6 +644,24 @@ TRUSTIFY_DA_GO_MVS_LOGIC_ENABLED=false
608644
609645
#### Python Support
610646
647+
The client supports two Python manifest formats:
648+
649+
- **`requirements.txt`** — uses pip/pip3 to resolve dependencies
650+
- **`pyproject.toml`** — uses [uv](https://docs.astral.sh/uv/) or [Poetry](https://python-poetry.org/) to resolve dependencies
651+
652+
##### pyproject.toml
653+
654+
For `pyproject.toml` projects, the client detects which tool manages the project by checking for lock files:
655+
- If `poetry.lock` is present and `[tool.poetry]` is defined, **Poetry** is used (`poetry show --tree` and `poetry show --all`)
656+
- If `uv.lock` is present, **uv** is used (`uv export --format requirements.txt --frozen --no-hashes`)
657+
- If neither lock file is found, an error is thrown
658+
659+
Both PEP 621 (`[project]` dependencies) and Poetry-style (`[tool.poetry.dependencies]`) are supported.
660+
661+
Custom executable paths can be set via `TRUSTIFY_DA_UV_PATH` and `TRUSTIFY_DA_POETRY_PATH`.
662+
663+
##### requirements.txt
664+
611665
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
612666
Binaries passed to environment variables. In any case, If the package is not installed , then an error will be thrown.
613667

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)