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.

Files changed (65) hide show
  1. pysentry_rs-0.3.3/.pre-commit-hooks.yaml +10 -0
  2. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/Cargo.lock +1 -1
  3. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/Cargo.toml +1 -1
  4. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/PKG-INFO +54 -25
  5. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/README.md +53 -24
  6. pysentry_rs-0.3.3/benchmarks/results/0.3.2.md +141 -0
  7. pysentry_rs-0.3.3/benchmarks/results/latest.md +141 -0
  8. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/parsers/pyproject.rs +367 -11
  9. pysentry_rs-0.3.2/benchmarks/results/latest.md +0 -141
  10. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.github/FUNDING.yml +0 -0
  11. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.github/dependabot.yml +0 -0
  12. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.github/workflows/benchmark.yml +0 -0
  13. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.github/workflows/ci.yml +0 -0
  14. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.github/workflows/release.yml +0 -0
  15. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.gitignore +0 -0
  16. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/.pre-commit-config.yaml +0 -0
  17. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/LICENSE +0 -0
  18. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/.gitignore +0 -0
  19. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/.python-version +0 -0
  20. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/README.md +0 -0
  21. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/main.py +0 -0
  22. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/pyproject.toml +0 -0
  23. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/results/0.2.3.md +0 -0
  24. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/results/0.3.1.md +0 -0
  25. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/src/benchmark_runner.py +0 -0
  26. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/src/performance_monitor.py +0 -0
  27. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/src/report_generator.py +0 -0
  28. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/src/tool_wrapper.py +0 -0
  29. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/test_data/large_requirements.txt +0 -0
  30. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/test_data/small_requirements.txt +0 -0
  31. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/benchmarks/uv.lock +0 -0
  32. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/fixtures/requirements-tests/requirements-dev.txt +0 -0
  33. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/fixtures/requirements-tests/requirements.txt +0 -0
  34. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/fixtures/requirements-tests-vulnerable/requirements.txt +0 -0
  35. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/pyproject.toml +0 -0
  36. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/python/pysentry/__init__.py +0 -0
  37. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/cache/audit.rs +0 -0
  38. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/cache/mod.rs +0 -0
  39. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/cache/storage.rs +0 -0
  40. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/cli.rs +0 -0
  41. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/config.rs +0 -0
  42. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/dependency/mod.rs +0 -0
  43. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/dependency/resolvers/mod.rs +0 -0
  44. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/dependency/resolvers/pip_tools.rs +0 -0
  45. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/dependency/resolvers/uv.rs +0 -0
  46. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/dependency/scanner.rs +0 -0
  47. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/error.rs +0 -0
  48. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/lib.rs +0 -0
  49. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/main.rs +0 -0
  50. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/output/mod.rs +0 -0
  51. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/output/report.rs +0 -0
  52. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/output/sarif.rs +0 -0
  53. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/parsers/lock.rs +0 -0
  54. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/parsers/mod.rs +0 -0
  55. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/parsers/poetry_lock.rs +0 -0
  56. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/parsers/requirements.rs +0 -0
  57. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/providers/mod.rs +0 -0
  58. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/providers/osv.rs +0 -0
  59. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/providers/pypa.rs +0 -0
  60. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/providers/pypi.rs +0 -0
  61. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/python.rs +0 -0
  62. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/types.rs +0 -0
  63. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/vulnerability/database.rs +0 -0
  64. {pysentry_rs-0.3.2 → pysentry_rs-0.3.3}/src/vulnerability/matcher.rs +0 -0
  65. {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]
@@ -1121,7 +1121,7 @@ dependencies = [
1121
1121
 
1122
1122
  [[package]]
1123
1123
  name = "pysentry"
1124
- version = "0.3.2"
1124
+ version = "0.3.3"
1125
1125
  dependencies = [
1126
1126
  "anyhow",
1127
1127
  "async-trait",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "pysentry"
3
- version = "0.3.2"
3
+ version = "0.3.3"
4
4
  edition = "2021"
5
5
  rust-version = "1.79"
6
6
  description = "Security vulnerability auditing for Python packages"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pysentry-rs
3
- Version: 0.3.2
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 | Description | Example |
334
- |----------|-------------|---------|
335
- | `PYSENTRY_CONFIG` | Override config file path | `PYSENTRY_CONFIG=/path/to/config.toml` |
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 | Default |
341
- | -------------------------- | ------------------------------------------------------- | ----------------- |
342
- | `--format` | Output format: `human`, `json`, `sarif`, `markdown` | `human` |
343
- | `--severity` | Minimum severity: `low`, `medium`, `high`, `critical` | `low` |
344
- | `--fail-on` | Fail (exit non-zero) on vulnerabilities ≥ severity | `medium` |
345
- | `--sources` | Vulnerability sources: `pypa`, `pypi`, `osv` (multiple) | `pypa` |
346
- | `--all-extras` | Include all dependencies (main + dev + optional) | `false` |
347
- | `--direct-only` | Check only direct dependencies | `false` |
348
- | `--detailed` | Show full vulnerability descriptions instead of truncated| `false` |
349
- | `--ignore` | Vulnerability IDs to ignore (repeatable) | `[]` |
350
- | `--output` | Output file path | `stdout` |
351
- | `--no-cache` | Disable all caching | `false` |
352
- | `--cache-dir` | Custom cache directory | Platform-specific |
353
- | `--resolution-cache-ttl` | Resolution cache TTL in hours | `24` |
354
- | `--no-resolution-cache` | Disable resolution caching only | `false` |
355
- | `--clear-resolution-cache` | Clear resolution cache on startup | `false` |
356
- | `--verbose` | Enable verbose output | `false` |
357
- | `--quiet` | Suppress non-error output | `false` |
358
- | `--resolver` | Dependency resolver: `auto`, `uv`, `pip-tools` | `auto` |
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 | Description | Example |
308
- |----------|-------------|---------|
309
- | `PYSENTRY_CONFIG` | Override config file path | `PYSENTRY_CONFIG=/path/to/config.toml` |
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 | Default |
315
- | -------------------------- | ------------------------------------------------------- | ----------------- |
316
- | `--format` | Output format: `human`, `json`, `sarif`, `markdown` | `human` |
317
- | `--severity` | Minimum severity: `low`, `medium`, `high`, `critical` | `low` |
318
- | `--fail-on` | Fail (exit non-zero) on vulnerabilities ≥ severity | `medium` |
319
- | `--sources` | Vulnerability sources: `pypa`, `pypi`, `osv` (multiple) | `pypa` |
320
- | `--all-extras` | Include all dependencies (main + dev + optional) | `false` |
321
- | `--direct-only` | Check only direct dependencies | `false` |
322
- | `--detailed` | Show full vulnerability descriptions instead of truncated| `false` |
323
- | `--ignore` | Vulnerability IDs to ignore (repeatable) | `[]` |
324
- | `--output` | Output file path | `stdout` |
325
- | `--no-cache` | Disable all caching | `false` |
326
- | `--cache-dir` | Custom cache directory | Platform-specific |
327
- | `--resolution-cache-ttl` | Resolution cache TTL in hours | `24` |
328
- | `--no-resolution-cache` | Disable resolution caching only | `false` |
329
- | `--clear-resolution-cache` | Clear resolution cache on startup | `false` |
330
- | `--verbose` | Enable verbose output | `false` |
331
- | `--quiet` | Suppress non-error output | `false` |
332
- | `--resolver` | Dependency resolver: `auto`, `uv`, `pip-tools` | `auto` |
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<String>>>,
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
- // Include all dependency groups for graph analysis
542
- for (group_name, deps) in dep_groups {
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
- for dep_str in deps {
545
- if let Ok(package_name) = self.extract_package_name_from_dep_string(dep_str)
546
- {
547
- // All dependency groups are classified as Optional
548
- deps_map.insert(
549
- dep_str.clone(),
550
- (package_name, DependencyType::Optional, dep_str.clone()),
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