pysentry-rs 0.3.2__tar.gz → 0.3.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pysentry-rs might be problematic. Click here for more details.
- pysentry_rs-0.3.3/.pre-commit-hooks.yaml +10 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/Cargo.lock +1 -1
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/Cargo.toml +1 -1
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/PKG-INFO +54 -25
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/README.md +53 -24
- pysentry_rs-0.3.3/benchmarks/results/0.3.2.md +141 -0
- pysentry_rs-0.3.3/benchmarks/results/latest.md +141 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/parsers/pyproject.rs +367 -11
- pysentry_rs-0.3.2/benchmarks/results/latest.md +0 -141
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.github/FUNDING.yml +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.github/dependabot.yml +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.github/workflows/benchmark.yml +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.github/workflows/ci.yml +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.github/workflows/release.yml +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.gitignore +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.pre-commit-config.yaml +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/LICENSE +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/.gitignore +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/.python-version +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/README.md +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/main.py +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/pyproject.toml +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/results/0.2.3.md +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/results/0.3.1.md +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/src/benchmark_runner.py +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/src/performance_monitor.py +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/src/report_generator.py +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/src/tool_wrapper.py +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/test_data/large_requirements.txt +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/test_data/small_requirements.txt +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/uv.lock +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/fixtures/requirements-tests/requirements-dev.txt +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/fixtures/requirements-tests/requirements.txt +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/fixtures/requirements-tests-vulnerable/requirements.txt +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/pyproject.toml +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/python/pysentry/__init__.py +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/cache/audit.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/cache/mod.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/cache/storage.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/cli.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/config.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/dependency/mod.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/dependency/resolvers/mod.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/dependency/resolvers/pip_tools.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/dependency/resolvers/uv.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/dependency/scanner.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/error.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/lib.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/main.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/output/mod.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/output/report.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/output/sarif.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/parsers/lock.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/parsers/mod.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/parsers/poetry_lock.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/parsers/requirements.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/providers/mod.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/providers/osv.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/providers/pypa.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/providers/pypi.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/python.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/types.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/vulnerability/database.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/vulnerability/matcher.rs +0 -0
- {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/vulnerability/mod.rs +0 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
- id: pysentry
|
|
2
|
+
name: pysentry
|
|
3
|
+
description: "Fast security vulnerability scanner for Python dependencies"
|
|
4
|
+
entry: pysentry
|
|
5
|
+
language: python
|
|
6
|
+
always_run: true
|
|
7
|
+
additional_dependencies:
|
|
8
|
+
["pysentry-rs==0.3.3", "uv==0.8.9", "pip-tools==7.5.0"]
|
|
9
|
+
minimum_pre_commit_version: "2.9.2"
|
|
10
|
+
types: [python]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pysentry-rs
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.3
|
|
4
4
|
Classifier: Development Status :: 4 - Beta
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
@@ -287,6 +287,35 @@ pysentry --clear-resolution-cache --sources pypa,osv --format sarif
|
|
|
287
287
|
pysentry --no-resolution-cache --format json --output security-report.json
|
|
288
288
|
```
|
|
289
289
|
|
|
290
|
+
## Pre-commit Integration
|
|
291
|
+
|
|
292
|
+
PySentry integrates seamlessly with [pre-commit](https://pre-commit.com/) to automatically scan for vulnerabilities before commits.
|
|
293
|
+
|
|
294
|
+
### Setup
|
|
295
|
+
|
|
296
|
+
Add PySentry to your `.pre-commit-config.yaml`:
|
|
297
|
+
|
|
298
|
+
```yaml
|
|
299
|
+
repos:
|
|
300
|
+
- repo: https://github.com/nyudenkov/pysentry
|
|
301
|
+
hooks:
|
|
302
|
+
- id: pysentry # default pysentry settings
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Advanced Configuration
|
|
306
|
+
|
|
307
|
+
```yaml
|
|
308
|
+
repos:
|
|
309
|
+
- repo: https://github.com/nyudenkov/pysentry
|
|
310
|
+
hooks:
|
|
311
|
+
- id: pysentry
|
|
312
|
+
args: ["--sources", "pypa,osv", "--fail-on", "high"]
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Installation Requirements
|
|
316
|
+
|
|
317
|
+
Pre-commit will automatically install PySentry, uv and pip-tools via PyPI.
|
|
318
|
+
|
|
290
319
|
## Configuration
|
|
291
320
|
|
|
292
321
|
PySentry supports TOML-based configuration files for persistent settings management. Configuration files follow a hierarchical discovery pattern:
|
|
@@ -330,33 +359,33 @@ ids = ["CVE-2023-12345", "GHSA-xxxx-yyyy-zzzz"]
|
|
|
330
359
|
|
|
331
360
|
### Environment Variables
|
|
332
361
|
|
|
333
|
-
| Variable
|
|
334
|
-
|
|
335
|
-
| `PYSENTRY_CONFIG`
|
|
336
|
-
| `PYSENTRY_NO_CONFIG` | Disable all config file loading | `PYSENTRY_NO_CONFIG=1`
|
|
362
|
+
| Variable | Description | Example |
|
|
363
|
+
| -------------------- | ------------------------------- | -------------------------------------- |
|
|
364
|
+
| `PYSENTRY_CONFIG` | Override config file path | `PYSENTRY_CONFIG=/path/to/config.toml` |
|
|
365
|
+
| `PYSENTRY_NO_CONFIG` | Disable all config file loading | `PYSENTRY_NO_CONFIG=1` |
|
|
337
366
|
|
|
338
367
|
### Command Line Options
|
|
339
368
|
|
|
340
|
-
| Option | Description
|
|
341
|
-
| -------------------------- |
|
|
342
|
-
| `--format` | Output format: `human`, `json`, `sarif`, `markdown`
|
|
343
|
-
| `--severity` | Minimum severity: `low`, `medium`, `high`, `critical`
|
|
344
|
-
| `--fail-on` | Fail (exit non-zero) on vulnerabilities ≥ severity
|
|
345
|
-
| `--sources` | Vulnerability sources: `pypa`, `pypi`, `osv` (multiple)
|
|
346
|
-
| `--all-extras` | Include all dependencies (main + dev + optional)
|
|
347
|
-
| `--direct-only` | Check only direct dependencies
|
|
348
|
-
| `--detailed` | Show full vulnerability descriptions instead of truncated| `false` |
|
|
349
|
-
| `--ignore` | Vulnerability IDs to ignore (repeatable)
|
|
350
|
-
| `--output` | Output file path
|
|
351
|
-
| `--no-cache` | Disable all caching
|
|
352
|
-
| `--cache-dir` | Custom cache directory
|
|
353
|
-
| `--resolution-cache-ttl` | Resolution cache TTL in hours
|
|
354
|
-
| `--no-resolution-cache` | Disable resolution caching only
|
|
355
|
-
| `--clear-resolution-cache` | Clear resolution cache on startup
|
|
356
|
-
| `--verbose` | Enable verbose output
|
|
357
|
-
| `--quiet` | Suppress non-error output
|
|
358
|
-
| `--resolver` | Dependency resolver: `auto`, `uv`, `pip-tools`
|
|
359
|
-
| `--requirements` | Additional requirements files (repeatable)
|
|
369
|
+
| Option | Description | Default |
|
|
370
|
+
| -------------------------- | --------------------------------------------------------- | ----------------- |
|
|
371
|
+
| `--format` | Output format: `human`, `json`, `sarif`, `markdown` | `human` |
|
|
372
|
+
| `--severity` | Minimum severity: `low`, `medium`, `high`, `critical` | `low` |
|
|
373
|
+
| `--fail-on` | Fail (exit non-zero) on vulnerabilities ≥ severity | `medium` |
|
|
374
|
+
| `--sources` | Vulnerability sources: `pypa`, `pypi`, `osv` (multiple) | `pypa` |
|
|
375
|
+
| `--all-extras` | Include all dependencies (main + dev + optional) | `false` |
|
|
376
|
+
| `--direct-only` | Check only direct dependencies | `false` |
|
|
377
|
+
| `--detailed` | Show full vulnerability descriptions instead of truncated | `false` |
|
|
378
|
+
| `--ignore` | Vulnerability IDs to ignore (repeatable) | `[]` |
|
|
379
|
+
| `--output` | Output file path | `stdout` |
|
|
380
|
+
| `--no-cache` | Disable all caching | `false` |
|
|
381
|
+
| `--cache-dir` | Custom cache directory | Platform-specific |
|
|
382
|
+
| `--resolution-cache-ttl` | Resolution cache TTL in hours | `24` |
|
|
383
|
+
| `--no-resolution-cache` | Disable resolution caching only | `false` |
|
|
384
|
+
| `--clear-resolution-cache` | Clear resolution cache on startup | `false` |
|
|
385
|
+
| `--verbose` | Enable verbose output | `false` |
|
|
386
|
+
| `--quiet` | Suppress non-error output | `false` |
|
|
387
|
+
| `--resolver` | Dependency resolver: `auto`, `uv`, `pip-tools` | `auto` |
|
|
388
|
+
| `--requirements` | Additional requirements files (repeatable) | `[]` |
|
|
360
389
|
|
|
361
390
|
### Cache Management
|
|
362
391
|
|
|
@@ -261,6 +261,35 @@ pysentry --clear-resolution-cache --sources pypa,osv --format sarif
|
|
|
261
261
|
pysentry --no-resolution-cache --format json --output security-report.json
|
|
262
262
|
```
|
|
263
263
|
|
|
264
|
+
## Pre-commit Integration
|
|
265
|
+
|
|
266
|
+
PySentry integrates seamlessly with [pre-commit](https://pre-commit.com/) to automatically scan for vulnerabilities before commits.
|
|
267
|
+
|
|
268
|
+
### Setup
|
|
269
|
+
|
|
270
|
+
Add PySentry to your `.pre-commit-config.yaml`:
|
|
271
|
+
|
|
272
|
+
```yaml
|
|
273
|
+
repos:
|
|
274
|
+
- repo: https://github.com/nyudenkov/pysentry
|
|
275
|
+
hooks:
|
|
276
|
+
- id: pysentry # default pysentry settings
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Advanced Configuration
|
|
280
|
+
|
|
281
|
+
```yaml
|
|
282
|
+
repos:
|
|
283
|
+
- repo: https://github.com/nyudenkov/pysentry
|
|
284
|
+
hooks:
|
|
285
|
+
- id: pysentry
|
|
286
|
+
args: ["--sources", "pypa,osv", "--fail-on", "high"]
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Installation Requirements
|
|
290
|
+
|
|
291
|
+
Pre-commit will automatically install PySentry, uv and pip-tools via PyPI.
|
|
292
|
+
|
|
264
293
|
## Configuration
|
|
265
294
|
|
|
266
295
|
PySentry supports TOML-based configuration files for persistent settings management. Configuration files follow a hierarchical discovery pattern:
|
|
@@ -304,33 +333,33 @@ ids = ["CVE-2023-12345", "GHSA-xxxx-yyyy-zzzz"]
|
|
|
304
333
|
|
|
305
334
|
### Environment Variables
|
|
306
335
|
|
|
307
|
-
| Variable
|
|
308
|
-
|
|
309
|
-
| `PYSENTRY_CONFIG`
|
|
310
|
-
| `PYSENTRY_NO_CONFIG` | Disable all config file loading | `PYSENTRY_NO_CONFIG=1`
|
|
336
|
+
| Variable | Description | Example |
|
|
337
|
+
| -------------------- | ------------------------------- | -------------------------------------- |
|
|
338
|
+
| `PYSENTRY_CONFIG` | Override config file path | `PYSENTRY_CONFIG=/path/to/config.toml` |
|
|
339
|
+
| `PYSENTRY_NO_CONFIG` | Disable all config file loading | `PYSENTRY_NO_CONFIG=1` |
|
|
311
340
|
|
|
312
341
|
### Command Line Options
|
|
313
342
|
|
|
314
|
-
| Option | Description
|
|
315
|
-
| -------------------------- |
|
|
316
|
-
| `--format` | Output format: `human`, `json`, `sarif`, `markdown`
|
|
317
|
-
| `--severity` | Minimum severity: `low`, `medium`, `high`, `critical`
|
|
318
|
-
| `--fail-on` | Fail (exit non-zero) on vulnerabilities ≥ severity
|
|
319
|
-
| `--sources` | Vulnerability sources: `pypa`, `pypi`, `osv` (multiple)
|
|
320
|
-
| `--all-extras` | Include all dependencies (main + dev + optional)
|
|
321
|
-
| `--direct-only` | Check only direct dependencies
|
|
322
|
-
| `--detailed` | Show full vulnerability descriptions instead of truncated| `false` |
|
|
323
|
-
| `--ignore` | Vulnerability IDs to ignore (repeatable)
|
|
324
|
-
| `--output` | Output file path
|
|
325
|
-
| `--no-cache` | Disable all caching
|
|
326
|
-
| `--cache-dir` | Custom cache directory
|
|
327
|
-
| `--resolution-cache-ttl` | Resolution cache TTL in hours
|
|
328
|
-
| `--no-resolution-cache` | Disable resolution caching only
|
|
329
|
-
| `--clear-resolution-cache` | Clear resolution cache on startup
|
|
330
|
-
| `--verbose` | Enable verbose output
|
|
331
|
-
| `--quiet` | Suppress non-error output
|
|
332
|
-
| `--resolver` | Dependency resolver: `auto`, `uv`, `pip-tools`
|
|
333
|
-
| `--requirements` | Additional requirements files (repeatable)
|
|
343
|
+
| Option | Description | Default |
|
|
344
|
+
| -------------------------- | --------------------------------------------------------- | ----------------- |
|
|
345
|
+
| `--format` | Output format: `human`, `json`, `sarif`, `markdown` | `human` |
|
|
346
|
+
| `--severity` | Minimum severity: `low`, `medium`, `high`, `critical` | `low` |
|
|
347
|
+
| `--fail-on` | Fail (exit non-zero) on vulnerabilities ≥ severity | `medium` |
|
|
348
|
+
| `--sources` | Vulnerability sources: `pypa`, `pypi`, `osv` (multiple) | `pypa` |
|
|
349
|
+
| `--all-extras` | Include all dependencies (main + dev + optional) | `false` |
|
|
350
|
+
| `--direct-only` | Check only direct dependencies | `false` |
|
|
351
|
+
| `--detailed` | Show full vulnerability descriptions instead of truncated | `false` |
|
|
352
|
+
| `--ignore` | Vulnerability IDs to ignore (repeatable) | `[]` |
|
|
353
|
+
| `--output` | Output file path | `stdout` |
|
|
354
|
+
| `--no-cache` | Disable all caching | `false` |
|
|
355
|
+
| `--cache-dir` | Custom cache directory | Platform-specific |
|
|
356
|
+
| `--resolution-cache-ttl` | Resolution cache TTL in hours | `24` |
|
|
357
|
+
| `--no-resolution-cache` | Disable resolution caching only | `false` |
|
|
358
|
+
| `--clear-resolution-cache` | Clear resolution cache on startup | `false` |
|
|
359
|
+
| `--verbose` | Enable verbose output | `false` |
|
|
360
|
+
| `--quiet` | Suppress non-error output | `false` |
|
|
361
|
+
| `--resolver` | Dependency resolver: `auto`, `uv`, `pip-tools` | `auto` |
|
|
362
|
+
| `--requirements` | Additional requirements files (repeatable) | `[]` |
|
|
334
363
|
|
|
335
364
|
### Cache Management
|
|
336
365
|
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# PySentry - pip-audit Benchmark Report
|
|
2
|
+
|
|
3
|
+
**Generated:** 2025-08-13 12:12:39
|
|
4
|
+
**Duration:** 1m 46.86s
|
|
5
|
+
**Total Tests:** 20
|
|
6
|
+
|
|
7
|
+
## Executive Summary
|
|
8
|
+
|
|
9
|
+
**Overall Success Rate:** 100.0% (20/20 successful runs)
|
|
10
|
+
|
|
11
|
+
### Small_Requirements Dataset - Cold Cache
|
|
12
|
+
- **Fastest:** pysentry-pypi (0.179s) - 46.37x faster than slowest
|
|
13
|
+
- **Memory Efficient:** pysentry-pypi (8.52 MB) - 12.47x less memory than highest
|
|
14
|
+
|
|
15
|
+
### Small_Requirements Dataset - Hot Cache
|
|
16
|
+
- **Fastest:** pysentry-pypi (0.163s) - 48.14x faster than slowest
|
|
17
|
+
- **Memory Efficient:** pysentry-pypi (8.43 MB) - 11.45x less memory than highest
|
|
18
|
+
|
|
19
|
+
### Large_Requirements Dataset - Cold Cache
|
|
20
|
+
- **Fastest:** pysentry-pypi (0.642s) - 26.63x faster than slowest
|
|
21
|
+
- **Memory Efficient:** pysentry-osv (10.42 MB) - 9.72x less memory than highest
|
|
22
|
+
|
|
23
|
+
### Large_Requirements Dataset - Hot Cache
|
|
24
|
+
- **Fastest:** pysentry-pypi (0.594s) - 25.42x faster than slowest
|
|
25
|
+
- **Memory Efficient:** pysentry-pypi (8.40 MB) - 12.41x less memory than highest
|
|
26
|
+
|
|
27
|
+
## Test Environment
|
|
28
|
+
|
|
29
|
+
- **Platform:** Linux-6.11.0-1018-azure-x86_64-with-glibc2.39
|
|
30
|
+
- **Python Version:** 3.11.13
|
|
31
|
+
- **CPU Cores:** 4
|
|
32
|
+
- **Total Memory:** 15.62 GB
|
|
33
|
+
- **Available Memory:** 14.74 GB
|
|
34
|
+
|
|
35
|
+
## Performance Comparison
|
|
36
|
+
|
|
37
|
+
### Small_Requirements Dataset - Cold Cache
|
|
38
|
+
|
|
39
|
+
#### Execution Time Comparison
|
|
40
|
+
|
|
41
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
42
|
+
|---------------------|---------------------|---------------------|
|
|
43
|
+
| 🥇 pysentry-pypi | 0.179s | 1.00x |
|
|
44
|
+
| 🥈 pysentry-all-sources | 1.024s | 5.71x |
|
|
45
|
+
| pysentry-osv | 1.051s | 5.86x |
|
|
46
|
+
| pysentry-pypa | 1.063s | 5.93x |
|
|
47
|
+
| pip-audit-default | 8.310s | 46.37x |
|
|
48
|
+
|
|
49
|
+
#### Memory Usage Comparison
|
|
50
|
+
|
|
51
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
52
|
+
|---------------------|---------------------|---------------------|
|
|
53
|
+
| 🥇 pysentry-pypi | 8.52 MB | 1.00x |
|
|
54
|
+
| 🥈 pysentry-osv | 10.50 MB | 1.23x |
|
|
55
|
+
| pip-audit-default | 45.38 MB | 5.32x |
|
|
56
|
+
| pysentry-pypa | 65.20 MB | 7.65x |
|
|
57
|
+
| pysentry-all-sources | 106.33 MB | 12.47x |
|
|
58
|
+
|
|
59
|
+
### Small_Requirements Dataset - Hot Cache
|
|
60
|
+
|
|
61
|
+
#### Execution Time Comparison
|
|
62
|
+
|
|
63
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
64
|
+
|---------------------|---------------------|---------------------|
|
|
65
|
+
| 🥇 pysentry-pypi | 0.163s | 1.00x |
|
|
66
|
+
| 🥈 pysentry-pypa | 0.651s | 3.99x |
|
|
67
|
+
| pysentry-osv | 0.811s | 4.98x |
|
|
68
|
+
| pysentry-all-sources | 0.980s | 6.01x |
|
|
69
|
+
| pip-audit-default | 7.849s | 48.14x |
|
|
70
|
+
|
|
71
|
+
#### Memory Usage Comparison
|
|
72
|
+
|
|
73
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
74
|
+
|---------------------|---------------------|---------------------|
|
|
75
|
+
| 🥇 pysentry-pypi | 8.43 MB | 1.00x |
|
|
76
|
+
| 🥈 pysentry-osv | 10.28 MB | 1.22x |
|
|
77
|
+
| pip-audit-default | 44.97 MB | 5.33x |
|
|
78
|
+
| pysentry-pypa | 67.79 MB | 8.04x |
|
|
79
|
+
| pysentry-all-sources | 96.55 MB | 11.45x |
|
|
80
|
+
|
|
81
|
+
### Large_Requirements Dataset - Cold Cache
|
|
82
|
+
|
|
83
|
+
#### Execution Time Comparison
|
|
84
|
+
|
|
85
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
86
|
+
|---------------------|---------------------|---------------------|
|
|
87
|
+
| 🥇 pysentry-pypi | 0.642s | 1.00x |
|
|
88
|
+
| 🥈 pysentry-pypa | 1.071s | 1.67x |
|
|
89
|
+
| pysentry-all-sources | 3.248s | 5.06x |
|
|
90
|
+
| pysentry-osv | 3.644s | 5.67x |
|
|
91
|
+
| pip-audit-default | 17.106s | 26.63x |
|
|
92
|
+
|
|
93
|
+
#### Memory Usage Comparison
|
|
94
|
+
|
|
95
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
96
|
+
|---------------------|---------------------|---------------------|
|
|
97
|
+
| 🥇 pysentry-osv | 10.42 MB | 1.00x |
|
|
98
|
+
| 🥈 pysentry-pypi | 13.56 MB | 1.30x |
|
|
99
|
+
| pip-audit-default | 47.45 MB | 4.55x |
|
|
100
|
+
| pysentry-pypa | 64.17 MB | 6.16x |
|
|
101
|
+
| pysentry-all-sources | 101.29 MB | 9.72x |
|
|
102
|
+
|
|
103
|
+
### Large_Requirements Dataset - Hot Cache
|
|
104
|
+
|
|
105
|
+
#### Execution Time Comparison
|
|
106
|
+
|
|
107
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
108
|
+
|---------------------|---------------------|---------------------|
|
|
109
|
+
| 🥇 pysentry-pypi | 0.594s | 1.00x |
|
|
110
|
+
| 🥈 pysentry-pypa | 1.133s | 1.91x |
|
|
111
|
+
| pysentry-all-sources | 3.124s | 5.26x |
|
|
112
|
+
| pysentry-osv | 3.124s | 5.26x |
|
|
113
|
+
| pip-audit-default | 15.104s | 25.42x |
|
|
114
|
+
|
|
115
|
+
#### Memory Usage Comparison
|
|
116
|
+
|
|
117
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
118
|
+
|---------------------|---------------------|---------------------|
|
|
119
|
+
| 🥇 pysentry-pypi | 8.40 MB | 1.00x |
|
|
120
|
+
| 🥈 pysentry-osv | 10.40 MB | 1.24x |
|
|
121
|
+
| pip-audit-default | 47.29 MB | 5.63x |
|
|
122
|
+
| pysentry-pypa | 72.68 MB | 8.65x |
|
|
123
|
+
| pysentry-all-sources | 104.25 MB | 12.41x |
|
|
124
|
+
|
|
125
|
+
## Detailed Analysis
|
|
126
|
+
|
|
127
|
+
### Pysentry Performance
|
|
128
|
+
|
|
129
|
+
- **Execution Time:** Avg: 1.406s, Min: 0.163s, Max: 3.644s
|
|
130
|
+
|
|
131
|
+
- **Memory Usage:** Avg: 47.42 MB, Min: 8.40 MB, Max: 106.33 MB
|
|
132
|
+
|
|
133
|
+
- **Success Rate:** 100.0% (16/16)
|
|
134
|
+
|
|
135
|
+
### Pip-Audit Performance
|
|
136
|
+
|
|
137
|
+
- **Execution Time:** Avg: 12.092s, Min: 7.849s, Max: 17.106s
|
|
138
|
+
|
|
139
|
+
- **Memory Usage:** Avg: 46.27 MB, Min: 44.97 MB, Max: 47.45 MB
|
|
140
|
+
|
|
141
|
+
- **Success Rate:** 100.0% (4/4)
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# PySentry - pip-audit Benchmark Report
|
|
2
|
+
|
|
3
|
+
**Generated:** 2025-08-13 12:12:39
|
|
4
|
+
**Duration:** 1m 46.86s
|
|
5
|
+
**Total Tests:** 20
|
|
6
|
+
|
|
7
|
+
## Executive Summary
|
|
8
|
+
|
|
9
|
+
**Overall Success Rate:** 100.0% (20/20 successful runs)
|
|
10
|
+
|
|
11
|
+
### Small_Requirements Dataset - Cold Cache
|
|
12
|
+
- **Fastest:** pysentry-pypi (0.179s) - 46.37x faster than slowest
|
|
13
|
+
- **Memory Efficient:** pysentry-pypi (8.52 MB) - 12.47x less memory than highest
|
|
14
|
+
|
|
15
|
+
### Small_Requirements Dataset - Hot Cache
|
|
16
|
+
- **Fastest:** pysentry-pypi (0.163s) - 48.14x faster than slowest
|
|
17
|
+
- **Memory Efficient:** pysentry-pypi (8.43 MB) - 11.45x less memory than highest
|
|
18
|
+
|
|
19
|
+
### Large_Requirements Dataset - Cold Cache
|
|
20
|
+
- **Fastest:** pysentry-pypi (0.642s) - 26.63x faster than slowest
|
|
21
|
+
- **Memory Efficient:** pysentry-osv (10.42 MB) - 9.72x less memory than highest
|
|
22
|
+
|
|
23
|
+
### Large_Requirements Dataset - Hot Cache
|
|
24
|
+
- **Fastest:** pysentry-pypi (0.594s) - 25.42x faster than slowest
|
|
25
|
+
- **Memory Efficient:** pysentry-pypi (8.40 MB) - 12.41x less memory than highest
|
|
26
|
+
|
|
27
|
+
## Test Environment
|
|
28
|
+
|
|
29
|
+
- **Platform:** Linux-6.11.0-1018-azure-x86_64-with-glibc2.39
|
|
30
|
+
- **Python Version:** 3.11.13
|
|
31
|
+
- **CPU Cores:** 4
|
|
32
|
+
- **Total Memory:** 15.62 GB
|
|
33
|
+
- **Available Memory:** 14.74 GB
|
|
34
|
+
|
|
35
|
+
## Performance Comparison
|
|
36
|
+
|
|
37
|
+
### Small_Requirements Dataset - Cold Cache
|
|
38
|
+
|
|
39
|
+
#### Execution Time Comparison
|
|
40
|
+
|
|
41
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
42
|
+
|---------------------|---------------------|---------------------|
|
|
43
|
+
| 🥇 pysentry-pypi | 0.179s | 1.00x |
|
|
44
|
+
| 🥈 pysentry-all-sources | 1.024s | 5.71x |
|
|
45
|
+
| pysentry-osv | 1.051s | 5.86x |
|
|
46
|
+
| pysentry-pypa | 1.063s | 5.93x |
|
|
47
|
+
| pip-audit-default | 8.310s | 46.37x |
|
|
48
|
+
|
|
49
|
+
#### Memory Usage Comparison
|
|
50
|
+
|
|
51
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
52
|
+
|---------------------|---------------------|---------------------|
|
|
53
|
+
| 🥇 pysentry-pypi | 8.52 MB | 1.00x |
|
|
54
|
+
| 🥈 pysentry-osv | 10.50 MB | 1.23x |
|
|
55
|
+
| pip-audit-default | 45.38 MB | 5.32x |
|
|
56
|
+
| pysentry-pypa | 65.20 MB | 7.65x |
|
|
57
|
+
| pysentry-all-sources | 106.33 MB | 12.47x |
|
|
58
|
+
|
|
59
|
+
### Small_Requirements Dataset - Hot Cache
|
|
60
|
+
|
|
61
|
+
#### Execution Time Comparison
|
|
62
|
+
|
|
63
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
64
|
+
|---------------------|---------------------|---------------------|
|
|
65
|
+
| 🥇 pysentry-pypi | 0.163s | 1.00x |
|
|
66
|
+
| 🥈 pysentry-pypa | 0.651s | 3.99x |
|
|
67
|
+
| pysentry-osv | 0.811s | 4.98x |
|
|
68
|
+
| pysentry-all-sources | 0.980s | 6.01x |
|
|
69
|
+
| pip-audit-default | 7.849s | 48.14x |
|
|
70
|
+
|
|
71
|
+
#### Memory Usage Comparison
|
|
72
|
+
|
|
73
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
74
|
+
|---------------------|---------------------|---------------------|
|
|
75
|
+
| 🥇 pysentry-pypi | 8.43 MB | 1.00x |
|
|
76
|
+
| 🥈 pysentry-osv | 10.28 MB | 1.22x |
|
|
77
|
+
| pip-audit-default | 44.97 MB | 5.33x |
|
|
78
|
+
| pysentry-pypa | 67.79 MB | 8.04x |
|
|
79
|
+
| pysentry-all-sources | 96.55 MB | 11.45x |
|
|
80
|
+
|
|
81
|
+
### Large_Requirements Dataset - Cold Cache
|
|
82
|
+
|
|
83
|
+
#### Execution Time Comparison
|
|
84
|
+
|
|
85
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
86
|
+
|---------------------|---------------------|---------------------|
|
|
87
|
+
| 🥇 pysentry-pypi | 0.642s | 1.00x |
|
|
88
|
+
| 🥈 pysentry-pypa | 1.071s | 1.67x |
|
|
89
|
+
| pysentry-all-sources | 3.248s | 5.06x |
|
|
90
|
+
| pysentry-osv | 3.644s | 5.67x |
|
|
91
|
+
| pip-audit-default | 17.106s | 26.63x |
|
|
92
|
+
|
|
93
|
+
#### Memory Usage Comparison
|
|
94
|
+
|
|
95
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
96
|
+
|---------------------|---------------------|---------------------|
|
|
97
|
+
| 🥇 pysentry-osv | 10.42 MB | 1.00x |
|
|
98
|
+
| 🥈 pysentry-pypi | 13.56 MB | 1.30x |
|
|
99
|
+
| pip-audit-default | 47.45 MB | 4.55x |
|
|
100
|
+
| pysentry-pypa | 64.17 MB | 6.16x |
|
|
101
|
+
| pysentry-all-sources | 101.29 MB | 9.72x |
|
|
102
|
+
|
|
103
|
+
### Large_Requirements Dataset - Hot Cache
|
|
104
|
+
|
|
105
|
+
#### Execution Time Comparison
|
|
106
|
+
|
|
107
|
+
| Tool Configuration | Execution Time | Relative Performance |
|
|
108
|
+
|---------------------|---------------------|---------------------|
|
|
109
|
+
| 🥇 pysentry-pypi | 0.594s | 1.00x |
|
|
110
|
+
| 🥈 pysentry-pypa | 1.133s | 1.91x |
|
|
111
|
+
| pysentry-all-sources | 3.124s | 5.26x |
|
|
112
|
+
| pysentry-osv | 3.124s | 5.26x |
|
|
113
|
+
| pip-audit-default | 15.104s | 25.42x |
|
|
114
|
+
|
|
115
|
+
#### Memory Usage Comparison
|
|
116
|
+
|
|
117
|
+
| Tool Configuration | Peak Memory | Relative Performance |
|
|
118
|
+
|---------------------|---------------------|---------------------|
|
|
119
|
+
| 🥇 pysentry-pypi | 8.40 MB | 1.00x |
|
|
120
|
+
| 🥈 pysentry-osv | 10.40 MB | 1.24x |
|
|
121
|
+
| pip-audit-default | 47.29 MB | 5.63x |
|
|
122
|
+
| pysentry-pypa | 72.68 MB | 8.65x |
|
|
123
|
+
| pysentry-all-sources | 104.25 MB | 12.41x |
|
|
124
|
+
|
|
125
|
+
## Detailed Analysis
|
|
126
|
+
|
|
127
|
+
### Pysentry Performance
|
|
128
|
+
|
|
129
|
+
- **Execution Time:** Avg: 1.406s, Min: 0.163s, Max: 3.644s
|
|
130
|
+
|
|
131
|
+
- **Memory Usage:** Avg: 47.42 MB, Min: 8.40 MB, Max: 106.33 MB
|
|
132
|
+
|
|
133
|
+
- **Success Rate:** 100.0% (16/16)
|
|
134
|
+
|
|
135
|
+
### Pip-Audit Performance
|
|
136
|
+
|
|
137
|
+
- **Execution Time:** Avg: 12.092s, Min: 7.849s, Max: 17.106s
|
|
138
|
+
|
|
139
|
+
- **Memory Usage:** Avg: 46.27 MB, Min: 44.97 MB, Max: 47.45 MB
|
|
140
|
+
|
|
141
|
+
- **Success Rate:** 100.0% (4/4)
|
|
@@ -29,12 +29,24 @@ use std::path::Path;
|
|
|
29
29
|
use std::str::FromStr;
|
|
30
30
|
use tracing::{debug, info, warn};
|
|
31
31
|
|
|
32
|
+
#[derive(Debug, Deserialize, Clone)]
|
|
33
|
+
#[serde(untagged)]
|
|
34
|
+
enum DependencyGroupEntry {
|
|
35
|
+
/// A regular dependency specification string (PEP 508)
|
|
36
|
+
Dependency(String),
|
|
37
|
+
/// An include-group reference
|
|
38
|
+
IncludeGroup {
|
|
39
|
+
#[serde(rename = "include-group")]
|
|
40
|
+
include_group: String,
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
32
44
|
/// PyProject.toml structure for parsing dependencies
|
|
33
45
|
#[derive(Debug, Deserialize)]
|
|
34
46
|
struct PyProject {
|
|
35
47
|
project: Option<Project>,
|
|
36
48
|
#[serde(rename = "dependency-groups")]
|
|
37
|
-
dependency_groups: Option<HashMap<String, Vec<
|
|
49
|
+
dependency_groups: Option<HashMap<String, Vec<DependencyGroupEntry>>>,
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
#[derive(Debug, Deserialize)]
|
|
@@ -482,6 +494,54 @@ impl ProjectParser for PyProjectParser {
|
|
|
482
494
|
}
|
|
483
495
|
|
|
484
496
|
impl PyProjectParser {
|
|
497
|
+
/// Resolve dependency group entries recursively, handling include-group references
|
|
498
|
+
#[allow(clippy::only_used_in_recursion)]
|
|
499
|
+
fn resolve_dependency_group_entries(
|
|
500
|
+
&self,
|
|
501
|
+
entries: &[DependencyGroupEntry],
|
|
502
|
+
dep_groups: &HashMap<String, Vec<DependencyGroupEntry>>,
|
|
503
|
+
_visited: &mut HashSet<String>,
|
|
504
|
+
current_path: &mut Vec<String>,
|
|
505
|
+
) -> Result<Vec<String>> {
|
|
506
|
+
let mut resolved_deps = Vec::new();
|
|
507
|
+
|
|
508
|
+
for entry in entries {
|
|
509
|
+
match entry {
|
|
510
|
+
DependencyGroupEntry::Dependency(dep_str) => {
|
|
511
|
+
resolved_deps.push(dep_str.clone());
|
|
512
|
+
}
|
|
513
|
+
DependencyGroupEntry::IncludeGroup { include_group } => {
|
|
514
|
+
if current_path.contains(include_group) {
|
|
515
|
+
return Err(AuditError::InvalidDependency(format!(
|
|
516
|
+
"Circular dependency detected in include-group: {} -> {}",
|
|
517
|
+
current_path.join(" -> "),
|
|
518
|
+
include_group
|
|
519
|
+
)));
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
if let Some(included_entries) = dep_groups.get(include_group) {
|
|
523
|
+
current_path.push(include_group.clone());
|
|
524
|
+
|
|
525
|
+
let included_deps = self.resolve_dependency_group_entries(
|
|
526
|
+
included_entries,
|
|
527
|
+
dep_groups,
|
|
528
|
+
_visited,
|
|
529
|
+
current_path,
|
|
530
|
+
)?;
|
|
531
|
+
|
|
532
|
+
resolved_deps.extend(included_deps);
|
|
533
|
+
|
|
534
|
+
current_path.pop();
|
|
535
|
+
} else {
|
|
536
|
+
warn!("Referenced dependency group '{}' not found", include_group);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
Ok(resolved_deps)
|
|
543
|
+
}
|
|
544
|
+
|
|
485
545
|
/// Get direct dependencies with their types and version specs from pyproject.toml
|
|
486
546
|
fn get_direct_dependencies_with_info(
|
|
487
547
|
&self,
|
|
@@ -538,17 +598,34 @@ impl PyProjectParser {
|
|
|
538
598
|
// Add development dependencies if requested (PEP 735 dependency-groups)
|
|
539
599
|
if include_dev {
|
|
540
600
|
if let Some(dep_groups) = &pyproject.dependency_groups {
|
|
541
|
-
//
|
|
542
|
-
for (group_name,
|
|
601
|
+
// Resolve all dependency groups with include-group support
|
|
602
|
+
for (group_name, entries) in dep_groups {
|
|
543
603
|
debug!("Processing dependency group '{}' as Optional", group_name);
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
604
|
+
|
|
605
|
+
let mut visited = HashSet::new();
|
|
606
|
+
let mut current_path = Vec::new();
|
|
607
|
+
|
|
608
|
+
match self.resolve_dependency_group_entries(
|
|
609
|
+
entries,
|
|
610
|
+
dep_groups,
|
|
611
|
+
&mut visited,
|
|
612
|
+
&mut current_path,
|
|
613
|
+
) {
|
|
614
|
+
Ok(resolved_deps) => {
|
|
615
|
+
for dep_str in resolved_deps {
|
|
616
|
+
if let Ok(package_name) =
|
|
617
|
+
self.extract_package_name_from_dep_string(&dep_str)
|
|
618
|
+
{
|
|
619
|
+
// All dependency groups are classified as Optional
|
|
620
|
+
deps_map.insert(
|
|
621
|
+
dep_str.clone(),
|
|
622
|
+
(package_name, DependencyType::Optional, dep_str.clone()),
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
Err(e) => {
|
|
628
|
+
warn!("Failed to resolve dependency group '{}': {}", group_name, e);
|
|
552
629
|
}
|
|
553
630
|
}
|
|
554
631
|
}
|
|
@@ -950,4 +1027,283 @@ charset-normalizer==2.1.1
|
|
|
950
1027
|
);
|
|
951
1028
|
assert_eq!(dep_type, DependencyType::Main);
|
|
952
1029
|
}
|
|
1030
|
+
|
|
1031
|
+
#[test]
|
|
1032
|
+
fn test_dependency_group_entry_deserialization() {
|
|
1033
|
+
use serde_json;
|
|
1034
|
+
|
|
1035
|
+
let json = r#""pytest>=6.0""#;
|
|
1036
|
+
let entry: DependencyGroupEntry = serde_json::from_str(json).unwrap();
|
|
1037
|
+
match entry {
|
|
1038
|
+
DependencyGroupEntry::Dependency(dep) => assert_eq!(dep, "pytest>=6.0"),
|
|
1039
|
+
_ => panic!("Expected Dependency variant"),
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
let json = r#"{"include-group": "test"}"#;
|
|
1043
|
+
let entry: DependencyGroupEntry = serde_json::from_str(json).unwrap();
|
|
1044
|
+
match entry {
|
|
1045
|
+
DependencyGroupEntry::IncludeGroup { include_group } => {
|
|
1046
|
+
assert_eq!(include_group, "test")
|
|
1047
|
+
}
|
|
1048
|
+
_ => panic!("Expected IncludeGroup variant"),
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
|
|
1052
|
+
#[test]
|
|
1053
|
+
fn test_resolve_dependency_group_entries_simple() {
|
|
1054
|
+
let parser = PyProjectParser::new(None);
|
|
1055
|
+
let mut dep_groups = HashMap::new();
|
|
1056
|
+
|
|
1057
|
+
dep_groups.insert(
|
|
1058
|
+
"test".to_string(),
|
|
1059
|
+
vec![
|
|
1060
|
+
DependencyGroupEntry::Dependency("pytest".to_string()),
|
|
1061
|
+
DependencyGroupEntry::Dependency("coverage".to_string()),
|
|
1062
|
+
],
|
|
1063
|
+
);
|
|
1064
|
+
|
|
1065
|
+
let mut visited = HashSet::new();
|
|
1066
|
+
let mut current_path = Vec::new();
|
|
1067
|
+
|
|
1068
|
+
let result = parser
|
|
1069
|
+
.resolve_dependency_group_entries(
|
|
1070
|
+
&dep_groups["test"],
|
|
1071
|
+
&dep_groups,
|
|
1072
|
+
&mut visited,
|
|
1073
|
+
&mut current_path,
|
|
1074
|
+
)
|
|
1075
|
+
.unwrap();
|
|
1076
|
+
|
|
1077
|
+
assert_eq!(result.len(), 2);
|
|
1078
|
+
assert!(result.contains(&"pytest".to_string()));
|
|
1079
|
+
assert!(result.contains(&"coverage".to_string()));
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
#[test]
|
|
1083
|
+
fn test_resolve_dependency_group_entries_with_includes() {
|
|
1084
|
+
let parser = PyProjectParser::new(None);
|
|
1085
|
+
let mut dep_groups = HashMap::new();
|
|
1086
|
+
|
|
1087
|
+
dep_groups.insert(
|
|
1088
|
+
"test".to_string(),
|
|
1089
|
+
vec![
|
|
1090
|
+
DependencyGroupEntry::Dependency("pytest".to_string()),
|
|
1091
|
+
DependencyGroupEntry::Dependency("coverage".to_string()),
|
|
1092
|
+
],
|
|
1093
|
+
);
|
|
1094
|
+
|
|
1095
|
+
dep_groups.insert(
|
|
1096
|
+
"typing".to_string(),
|
|
1097
|
+
vec![
|
|
1098
|
+
DependencyGroupEntry::Dependency("mypy".to_string()),
|
|
1099
|
+
DependencyGroupEntry::Dependency("types-requests".to_string()),
|
|
1100
|
+
],
|
|
1101
|
+
);
|
|
1102
|
+
|
|
1103
|
+
dep_groups.insert(
|
|
1104
|
+
"typing-test".to_string(),
|
|
1105
|
+
vec![
|
|
1106
|
+
DependencyGroupEntry::IncludeGroup {
|
|
1107
|
+
include_group: "typing".to_string(),
|
|
1108
|
+
},
|
|
1109
|
+
DependencyGroupEntry::IncludeGroup {
|
|
1110
|
+
include_group: "test".to_string(),
|
|
1111
|
+
},
|
|
1112
|
+
DependencyGroupEntry::Dependency("additional-dep".to_string()),
|
|
1113
|
+
],
|
|
1114
|
+
);
|
|
1115
|
+
|
|
1116
|
+
let mut visited = HashSet::new();
|
|
1117
|
+
let mut current_path = Vec::new();
|
|
1118
|
+
|
|
1119
|
+
let result = parser
|
|
1120
|
+
.resolve_dependency_group_entries(
|
|
1121
|
+
&dep_groups["typing-test"],
|
|
1122
|
+
&dep_groups,
|
|
1123
|
+
&mut visited,
|
|
1124
|
+
&mut current_path,
|
|
1125
|
+
)
|
|
1126
|
+
.unwrap();
|
|
1127
|
+
|
|
1128
|
+
assert_eq!(result.len(), 5);
|
|
1129
|
+
assert!(result.contains(&"mypy".to_string()));
|
|
1130
|
+
assert!(result.contains(&"types-requests".to_string()));
|
|
1131
|
+
assert!(result.contains(&"pytest".to_string()));
|
|
1132
|
+
assert!(result.contains(&"coverage".to_string()));
|
|
1133
|
+
assert!(result.contains(&"additional-dep".to_string()));
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
#[test]
|
|
1137
|
+
fn test_resolve_dependency_group_entries_nested_includes() {
|
|
1138
|
+
let parser = PyProjectParser::new(None);
|
|
1139
|
+
let mut dep_groups = HashMap::new();
|
|
1140
|
+
|
|
1141
|
+
dep_groups.insert(
|
|
1142
|
+
"base".to_string(),
|
|
1143
|
+
vec![DependencyGroupEntry::Dependency("requests".to_string())],
|
|
1144
|
+
);
|
|
1145
|
+
|
|
1146
|
+
dep_groups.insert(
|
|
1147
|
+
"middleware".to_string(),
|
|
1148
|
+
vec![
|
|
1149
|
+
DependencyGroupEntry::IncludeGroup {
|
|
1150
|
+
include_group: "base".to_string(),
|
|
1151
|
+
},
|
|
1152
|
+
DependencyGroupEntry::Dependency("flask".to_string()),
|
|
1153
|
+
],
|
|
1154
|
+
);
|
|
1155
|
+
|
|
1156
|
+
dep_groups.insert(
|
|
1157
|
+
"full".to_string(),
|
|
1158
|
+
vec![
|
|
1159
|
+
DependencyGroupEntry::IncludeGroup {
|
|
1160
|
+
include_group: "middleware".to_string(),
|
|
1161
|
+
},
|
|
1162
|
+
DependencyGroupEntry::Dependency("pytest".to_string()),
|
|
1163
|
+
],
|
|
1164
|
+
);
|
|
1165
|
+
|
|
1166
|
+
let mut visited = HashSet::new();
|
|
1167
|
+
let mut current_path = Vec::new();
|
|
1168
|
+
|
|
1169
|
+
let result = parser
|
|
1170
|
+
.resolve_dependency_group_entries(
|
|
1171
|
+
&dep_groups["full"],
|
|
1172
|
+
&dep_groups,
|
|
1173
|
+
&mut visited,
|
|
1174
|
+
&mut current_path,
|
|
1175
|
+
)
|
|
1176
|
+
.unwrap();
|
|
1177
|
+
|
|
1178
|
+
assert_eq!(result.len(), 3);
|
|
1179
|
+
assert!(result.contains(&"requests".to_string()));
|
|
1180
|
+
assert!(result.contains(&"flask".to_string()));
|
|
1181
|
+
assert!(result.contains(&"pytest".to_string()));
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
#[test]
|
|
1185
|
+
fn test_resolve_dependency_group_entries_cycle_detection() {
|
|
1186
|
+
let parser = PyProjectParser::new(None);
|
|
1187
|
+
let mut dep_groups = HashMap::new();
|
|
1188
|
+
|
|
1189
|
+
dep_groups.insert(
|
|
1190
|
+
"a".to_string(),
|
|
1191
|
+
vec![
|
|
1192
|
+
DependencyGroupEntry::IncludeGroup {
|
|
1193
|
+
include_group: "b".to_string(),
|
|
1194
|
+
},
|
|
1195
|
+
DependencyGroupEntry::Dependency("dep-a".to_string()),
|
|
1196
|
+
],
|
|
1197
|
+
);
|
|
1198
|
+
|
|
1199
|
+
dep_groups.insert(
|
|
1200
|
+
"b".to_string(),
|
|
1201
|
+
vec![
|
|
1202
|
+
DependencyGroupEntry::IncludeGroup {
|
|
1203
|
+
include_group: "c".to_string(),
|
|
1204
|
+
},
|
|
1205
|
+
DependencyGroupEntry::Dependency("dep-b".to_string()),
|
|
1206
|
+
],
|
|
1207
|
+
);
|
|
1208
|
+
|
|
1209
|
+
dep_groups.insert(
|
|
1210
|
+
"c".to_string(),
|
|
1211
|
+
vec![
|
|
1212
|
+
DependencyGroupEntry::IncludeGroup {
|
|
1213
|
+
include_group: "a".to_string(),
|
|
1214
|
+
},
|
|
1215
|
+
DependencyGroupEntry::Dependency("dep-c".to_string()),
|
|
1216
|
+
],
|
|
1217
|
+
);
|
|
1218
|
+
|
|
1219
|
+
let mut visited = HashSet::new();
|
|
1220
|
+
let mut current_path = Vec::new();
|
|
1221
|
+
|
|
1222
|
+
let result = parser.resolve_dependency_group_entries(
|
|
1223
|
+
&dep_groups["a"],
|
|
1224
|
+
&dep_groups,
|
|
1225
|
+
&mut visited,
|
|
1226
|
+
&mut current_path,
|
|
1227
|
+
);
|
|
1228
|
+
|
|
1229
|
+
assert!(result.is_err());
|
|
1230
|
+
let error_msg = format!("{}", result.unwrap_err());
|
|
1231
|
+
assert!(error_msg.contains("Circular dependency detected"));
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
#[test]
|
|
1235
|
+
fn test_resolve_dependency_group_entries_missing_group() {
|
|
1236
|
+
let parser = PyProjectParser::new(None);
|
|
1237
|
+
let mut dep_groups = HashMap::new();
|
|
1238
|
+
|
|
1239
|
+
dep_groups.insert(
|
|
1240
|
+
"test".to_string(),
|
|
1241
|
+
vec![
|
|
1242
|
+
DependencyGroupEntry::IncludeGroup {
|
|
1243
|
+
include_group: "missing".to_string(),
|
|
1244
|
+
},
|
|
1245
|
+
DependencyGroupEntry::Dependency("pytest".to_string()),
|
|
1246
|
+
],
|
|
1247
|
+
);
|
|
1248
|
+
|
|
1249
|
+
let mut visited = HashSet::new();
|
|
1250
|
+
let mut current_path = Vec::new();
|
|
1251
|
+
|
|
1252
|
+
let result = parser
|
|
1253
|
+
.resolve_dependency_group_entries(
|
|
1254
|
+
&dep_groups["test"],
|
|
1255
|
+
&dep_groups,
|
|
1256
|
+
&mut visited,
|
|
1257
|
+
&mut current_path,
|
|
1258
|
+
)
|
|
1259
|
+
.unwrap();
|
|
1260
|
+
|
|
1261
|
+
assert_eq!(result.len(), 1);
|
|
1262
|
+
assert!(result.contains(&"pytest".to_string()));
|
|
1263
|
+
}
|
|
1264
|
+
|
|
1265
|
+
#[test]
|
|
1266
|
+
fn test_pyproject_toml_with_include_groups() {
|
|
1267
|
+
let toml_content = r#"
|
|
1268
|
+
[dependency-groups]
|
|
1269
|
+
test = ["pytest", "coverage"]
|
|
1270
|
+
typing = ["mypy", "types-requests"]
|
|
1271
|
+
typing-test = [
|
|
1272
|
+
{include-group = "typing"},
|
|
1273
|
+
{include-group = "test"},
|
|
1274
|
+
"additional-dep"
|
|
1275
|
+
]
|
|
1276
|
+
"#;
|
|
1277
|
+
|
|
1278
|
+
let pyproject: PyProject = toml::from_str(toml_content).unwrap();
|
|
1279
|
+
let dep_groups = pyproject.dependency_groups.unwrap();
|
|
1280
|
+
|
|
1281
|
+
assert_eq!(dep_groups.len(), 3);
|
|
1282
|
+
|
|
1283
|
+
let test_group = &dep_groups["test"];
|
|
1284
|
+
assert_eq!(test_group.len(), 2);
|
|
1285
|
+
match &test_group[0] {
|
|
1286
|
+
DependencyGroupEntry::Dependency(dep) => assert_eq!(dep, "pytest"),
|
|
1287
|
+
_ => panic!("Expected Dependency variant"),
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
let typing_test_group = &dep_groups["typing-test"];
|
|
1291
|
+
assert_eq!(typing_test_group.len(), 3);
|
|
1292
|
+
match &typing_test_group[0] {
|
|
1293
|
+
DependencyGroupEntry::IncludeGroup { include_group } => {
|
|
1294
|
+
assert_eq!(include_group, "typing")
|
|
1295
|
+
}
|
|
1296
|
+
_ => panic!("Expected IncludeGroup variant"),
|
|
1297
|
+
}
|
|
1298
|
+
match &typing_test_group[1] {
|
|
1299
|
+
DependencyGroupEntry::IncludeGroup { include_group } => {
|
|
1300
|
+
assert_eq!(include_group, "test")
|
|
1301
|
+
}
|
|
1302
|
+
_ => panic!("Expected IncludeGroup variant"),
|
|
1303
|
+
}
|
|
1304
|
+
match &typing_test_group[2] {
|
|
1305
|
+
DependencyGroupEntry::Dependency(dep) => assert_eq!(dep, "additional-dep"),
|
|
1306
|
+
_ => panic!("Expected Dependency variant"),
|
|
1307
|
+
}
|
|
1308
|
+
}
|
|
953
1309
|
}
|
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
# PySentry - pip-audit Benchmark Report
|
|
2
|
-
|
|
3
|
-
**Generated:** 2025-08-12 18:55:26
|
|
4
|
-
**Duration:** 1m 54.40s
|
|
5
|
-
**Total Tests:** 20
|
|
6
|
-
|
|
7
|
-
## Executive Summary
|
|
8
|
-
|
|
9
|
-
**Overall Success Rate:** 100.0% (20/20 successful runs)
|
|
10
|
-
|
|
11
|
-
### Small_Requirements Dataset - Cold Cache
|
|
12
|
-
- **Fastest:** pysentry-pypi (0.213s) - 42.00x faster than slowest
|
|
13
|
-
- **Memory Efficient:** pysentry-osv (10.02 MB) - 10.69x less memory than highest
|
|
14
|
-
|
|
15
|
-
### Small_Requirements Dataset - Hot Cache
|
|
16
|
-
- **Fastest:** pysentry-pypi (0.223s) - 35.50x faster than slowest
|
|
17
|
-
- **Memory Efficient:** pysentry-osv (10.18 MB) - 9.89x less memory than highest
|
|
18
|
-
|
|
19
|
-
### Large_Requirements Dataset - Cold Cache
|
|
20
|
-
- **Fastest:** pysentry-pypi (0.679s) - 28.20x faster than slowest
|
|
21
|
-
- **Memory Efficient:** pysentry-osv (10.27 MB) - 10.21x less memory than highest
|
|
22
|
-
|
|
23
|
-
### Large_Requirements Dataset - Hot Cache
|
|
24
|
-
- **Fastest:** pysentry-pypi (0.692s) - 23.06x faster than slowest
|
|
25
|
-
- **Memory Efficient:** pysentry-pypi (9.86 MB) - 9.55x less memory than highest
|
|
26
|
-
|
|
27
|
-
## Test Environment
|
|
28
|
-
|
|
29
|
-
- **Platform:** Linux-6.11.0-1018-azure-x86_64-with-glibc2.39
|
|
30
|
-
- **Python Version:** 3.11.13
|
|
31
|
-
- **CPU Cores:** 4
|
|
32
|
-
- **Total Memory:** 15.62 GB
|
|
33
|
-
- **Available Memory:** 14.60 GB
|
|
34
|
-
|
|
35
|
-
## Performance Comparison
|
|
36
|
-
|
|
37
|
-
### Small_Requirements Dataset - Cold Cache
|
|
38
|
-
|
|
39
|
-
#### Execution Time Comparison
|
|
40
|
-
|
|
41
|
-
| Tool Configuration | Execution Time | Relative Performance |
|
|
42
|
-
|---------------------|---------------------|---------------------|
|
|
43
|
-
| 🥇 pysentry-pypi | 0.213s | 1.00x |
|
|
44
|
-
| 🥈 pysentry-pypa | 1.004s | 4.71x |
|
|
45
|
-
| pysentry-osv | 1.006s | 4.72x |
|
|
46
|
-
| pysentry-all-sources | 1.013s | 4.75x |
|
|
47
|
-
| pip-audit-default | 8.951s | 42.00x |
|
|
48
|
-
|
|
49
|
-
#### Memory Usage Comparison
|
|
50
|
-
|
|
51
|
-
| Tool Configuration | Peak Memory | Relative Performance |
|
|
52
|
-
|---------------------|---------------------|---------------------|
|
|
53
|
-
| 🥇 pysentry-osv | 10.02 MB | 1.00x |
|
|
54
|
-
| 🥈 pysentry-pypi | 11.68 MB | 1.17x |
|
|
55
|
-
| pip-audit-default | 45.42 MB | 4.53x |
|
|
56
|
-
| pysentry-pypa | 52.72 MB | 5.26x |
|
|
57
|
-
| pysentry-all-sources | 107.07 MB | 10.69x |
|
|
58
|
-
|
|
59
|
-
### Small_Requirements Dataset - Hot Cache
|
|
60
|
-
|
|
61
|
-
#### Execution Time Comparison
|
|
62
|
-
|
|
63
|
-
| Tool Configuration | Execution Time | Relative Performance |
|
|
64
|
-
|---------------------|---------------------|---------------------|
|
|
65
|
-
| 🥇 pysentry-pypi | 0.223s | 1.00x |
|
|
66
|
-
| 🥈 pysentry-pypa | 0.723s | 3.24x |
|
|
67
|
-
| pysentry-osv | 0.969s | 4.34x |
|
|
68
|
-
| pysentry-all-sources | 1.037s | 4.65x |
|
|
69
|
-
| pip-audit-default | 7.922s | 35.50x |
|
|
70
|
-
|
|
71
|
-
#### Memory Usage Comparison
|
|
72
|
-
|
|
73
|
-
| Tool Configuration | Peak Memory | Relative Performance |
|
|
74
|
-
|---------------------|---------------------|---------------------|
|
|
75
|
-
| 🥇 pysentry-osv | 10.18 MB | 1.00x |
|
|
76
|
-
| 🥈 pysentry-pypi | 10.59 MB | 1.04x |
|
|
77
|
-
| pip-audit-default | 44.28 MB | 4.35x |
|
|
78
|
-
| pysentry-pypa | 73.74 MB | 7.24x |
|
|
79
|
-
| pysentry-all-sources | 100.68 MB | 9.89x |
|
|
80
|
-
|
|
81
|
-
### Large_Requirements Dataset - Cold Cache
|
|
82
|
-
|
|
83
|
-
#### Execution Time Comparison
|
|
84
|
-
|
|
85
|
-
| Tool Configuration | Execution Time | Relative Performance |
|
|
86
|
-
|---------------------|---------------------|---------------------|
|
|
87
|
-
| 🥇 pysentry-pypi | 0.679s | 1.00x |
|
|
88
|
-
| 🥈 pysentry-pypa | 1.142s | 1.68x |
|
|
89
|
-
| pysentry-osv | 3.365s | 4.95x |
|
|
90
|
-
| pysentry-all-sources | 3.649s | 5.37x |
|
|
91
|
-
| pip-audit-default | 19.161s | 28.20x |
|
|
92
|
-
|
|
93
|
-
#### Memory Usage Comparison
|
|
94
|
-
|
|
95
|
-
| Tool Configuration | Peak Memory | Relative Performance |
|
|
96
|
-
|---------------------|---------------------|---------------------|
|
|
97
|
-
| 🥇 pysentry-osv | 10.27 MB | 1.00x |
|
|
98
|
-
| 🥈 pysentry-pypi | 10.36 MB | 1.01x |
|
|
99
|
-
| pip-audit-default | 47.43 MB | 4.62x |
|
|
100
|
-
| pysentry-pypa | 70.21 MB | 6.84x |
|
|
101
|
-
| pysentry-all-sources | 104.85 MB | 10.21x |
|
|
102
|
-
|
|
103
|
-
### Large_Requirements Dataset - Hot Cache
|
|
104
|
-
|
|
105
|
-
#### Execution Time Comparison
|
|
106
|
-
|
|
107
|
-
| Tool Configuration | Execution Time | Relative Performance |
|
|
108
|
-
|---------------------|---------------------|---------------------|
|
|
109
|
-
| 🥇 pysentry-pypi | 0.692s | 1.00x |
|
|
110
|
-
| 🥈 pysentry-pypa | 1.119s | 1.62x |
|
|
111
|
-
| pysentry-osv | 2.963s | 4.28x |
|
|
112
|
-
| pysentry-all-sources | 4.382s | 6.33x |
|
|
113
|
-
| pip-audit-default | 15.954s | 23.06x |
|
|
114
|
-
|
|
115
|
-
#### Memory Usage Comparison
|
|
116
|
-
|
|
117
|
-
| Tool Configuration | Peak Memory | Relative Performance |
|
|
118
|
-
|---------------------|---------------------|---------------------|
|
|
119
|
-
| 🥇 pysentry-pypi | 9.86 MB | 1.00x |
|
|
120
|
-
| 🥈 pysentry-osv | 10.14 MB | 1.03x |
|
|
121
|
-
| pip-audit-default | 47.00 MB | 4.77x |
|
|
122
|
-
| pysentry-pypa | 73.75 MB | 7.48x |
|
|
123
|
-
| pysentry-all-sources | 94.11 MB | 9.55x |
|
|
124
|
-
|
|
125
|
-
## Detailed Analysis
|
|
126
|
-
|
|
127
|
-
### Pysentry Performance
|
|
128
|
-
|
|
129
|
-
- **Execution Time:** Avg: 1.511s, Min: 0.213s, Max: 4.382s
|
|
130
|
-
|
|
131
|
-
- **Memory Usage:** Avg: 47.51 MB, Min: 9.86 MB, Max: 107.07 MB
|
|
132
|
-
|
|
133
|
-
- **Success Rate:** 100.0% (16/16)
|
|
134
|
-
|
|
135
|
-
### Pip-Audit Performance
|
|
136
|
-
|
|
137
|
-
- **Execution Time:** Avg: 12.997s, Min: 7.922s, Max: 19.161s
|
|
138
|
-
|
|
139
|
-
- **Memory Usage:** Avg: 46.03 MB, Min: 44.28 MB, Max: 47.43 MB
|
|
140
|
-
|
|
141
|
-
- **Success Rate:** 100.0% (4/4)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/fixtures/requirements-tests-vulnerable/requirements.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|