nab-python 0.0.2__tar.gz → 0.0.4__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.
Files changed (180) hide show
  1. nab_python-0.0.4/.gitignore +20 -0
  2. nab_python-0.0.4/LICENSE +1 -0
  3. {nab_python-0.0.2 → nab_python-0.0.4}/PKG-INFO +4 -4
  4. nab_python-0.0.4/benchmarks/README.md +53 -0
  5. nab_python-0.0.4/benchmarks/_profile_runner.py +139 -0
  6. nab_python-0.0.4/benchmarks/cache/.gitignore +1 -0
  7. nab_python-0.0.4/benchmarks/canary.py +470 -0
  8. nab_python-0.0.4/benchmarks/canary_results/.gitignore +2 -0
  9. nab_python-0.0.4/benchmarks/compare.py +132 -0
  10. nab_python-0.0.4/benchmarks/results/.gitignore +2 -0
  11. nab_python-0.0.4/benchmarks/scenarios/ai-stack-lowest-direct.toml +864 -0
  12. nab_python-0.0.4/benchmarks/scenarios/ai-stack-lowest.toml +864 -0
  13. nab_python-0.0.4/benchmarks/scenarios/ai-stack.toml +814 -0
  14. nab_python-0.0.4/benchmarks/scenarios/airflow-lowest-direct.toml +257 -0
  15. nab_python-0.0.4/benchmarks/scenarios/airflow-lowest.toml +255 -0
  16. nab_python-0.0.4/benchmarks/scenarios/airflow.toml +234 -0
  17. nab_python-0.0.4/benchmarks/scenarios/big-packages-lowest-direct.toml +78 -0
  18. nab_python-0.0.4/benchmarks/scenarios/big-packages-lowest.toml +78 -0
  19. nab_python-0.0.4/benchmarks/scenarios/big-packages.toml +67 -0
  20. nab_python-0.0.4/benchmarks/scenarios/cross-tracker-lowest-direct.toml +110 -0
  21. nab_python-0.0.4/benchmarks/scenarios/cross-tracker-lowest.toml +110 -0
  22. nab_python-0.0.4/benchmarks/scenarios/cross-tracker.toml +101 -0
  23. nab_python-0.0.4/benchmarks/scenarios/ecosystem-lowest-direct.toml +531 -0
  24. nab_python-0.0.4/benchmarks/scenarios/ecosystem-lowest.toml +531 -0
  25. nab_python-0.0.4/benchmarks/scenarios/ecosystem.toml +490 -0
  26. nab_python-0.0.4/benchmarks/scenarios/forums-lowest-direct.toml +1349 -0
  27. nab_python-0.0.4/benchmarks/scenarios/forums-lowest.toml +1349 -0
  28. nab_python-0.0.4/benchmarks/scenarios/forums.toml +1251 -0
  29. nab_python-0.0.4/benchmarks/scenarios/pdm-lowest-direct.toml +336 -0
  30. nab_python-0.0.4/benchmarks/scenarios/pdm-lowest.toml +336 -0
  31. nab_python-0.0.4/benchmarks/scenarios/pdm.toml +315 -0
  32. nab_python-0.0.4/benchmarks/scenarios/pip-lowest-direct.toml +2132 -0
  33. nab_python-0.0.4/benchmarks/scenarios/pip-lowest.toml +2132 -0
  34. nab_python-0.0.4/benchmarks/scenarios/pip.toml +2027 -0
  35. nab_python-0.0.4/benchmarks/scenarios/poetry-lowest-direct.toml +545 -0
  36. nab_python-0.0.4/benchmarks/scenarios/poetry-lowest.toml +545 -0
  37. nab_python-0.0.4/benchmarks/scenarios/poetry.toml +504 -0
  38. nab_python-0.0.4/benchmarks/scenarios/pytorch-lowest-direct.toml +175 -0
  39. nab_python-0.0.4/benchmarks/scenarios/pytorch-lowest.toml +175 -0
  40. nab_python-0.0.4/benchmarks/scenarios/pytorch.toml +163 -0
  41. nab_python-0.0.4/benchmarks/scenarios/quick-lowest-direct.toml +13 -0
  42. nab_python-0.0.4/benchmarks/scenarios/quick-lowest.toml +13 -0
  43. nab_python-0.0.4/benchmarks/scenarios/quick.toml +11 -0
  44. nab_python-0.0.4/benchmarks/scenarios/rip-lowest-direct.toml +121 -0
  45. nab_python-0.0.4/benchmarks/scenarios/rip-lowest.toml +121 -0
  46. nab_python-0.0.4/benchmarks/scenarios/rip.toml +109 -0
  47. nab_python-0.0.4/benchmarks/scenarios/universal.toml +351 -0
  48. nab_python-0.0.4/benchmarks/scenarios/unsupported-lowest-direct.toml +215 -0
  49. nab_python-0.0.4/benchmarks/scenarios/unsupported-lowest.toml +215 -0
  50. nab_python-0.0.4/benchmarks/scenarios/unsupported.toml +204 -0
  51. nab_python-0.0.4/benchmarks/scenarios/uv-lowest-direct.toml +1764 -0
  52. nab_python-0.0.4/benchmarks/scenarios/uv-lowest.toml +1764 -0
  53. nab_python-0.0.4/benchmarks/scenarios/uv.toml +1640 -0
  54. nab_python-0.0.4/benchmarks/scenarios.py +685 -0
  55. nab_python-0.0.4/benchmarks/strategy_sweep.py +365 -0
  56. nab_python-0.0.4/benchmarks/strategy_sweep_results/summary.json +21931 -0
  57. nab_python-0.0.4/benchmarks/strategy_sweep_summary.py +147 -0
  58. nab_python-0.0.4/benchmarks/universal_scenarios.py +262 -0
  59. nab_python-0.0.4/benchmarks/universal_summary.py +49 -0
  60. {nab_python-0.0.2 → nab_python-0.0.4}/pyproject.toml +4 -4
  61. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_build/env.py +1 -1
  62. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_build/runner.py +14 -3
  63. nab_python-0.0.4/src/nab_python/_conflict_kind.py +20 -0
  64. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_lockfile/builder.py +222 -27
  65. nab_python-0.0.4/src/nab_python/_lockfile/disjointness.py +407 -0
  66. nab_python-0.0.4/src/nab_python/_lockfile/pylock.py +588 -0
  67. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_lockfile/requirements.py +8 -7
  68. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_provider/extras.py +7 -7
  69. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_provider/listing.py +29 -9
  70. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_provider/metadata_resolver.py +31 -13
  71. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_provider/sources.py +3 -0
  72. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_testing/coordinator_fake.py +17 -6
  73. nab_python-0.0.4/src/nab_python/_toml.py +16 -0
  74. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/PROVENANCE.md +2 -2
  75. nab_python-0.0.4/src/nab_python/_vendor/packaging/_range_utils.py +998 -0
  76. nab_python-0.0.4/src/nab_python/_vendor/packaging/_version_utils.py +90 -0
  77. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/metadata.py +19 -4
  78. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/pylock.py +1 -0
  79. nab_python-0.0.4/src/nab_python/_vendor/packaging/ranges.py +2112 -0
  80. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/specifiers.py +436 -121
  81. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/tags.py +8 -6
  82. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/config.py +647 -9
  83. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/download.py +39 -9
  84. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/fetch.py +60 -15
  85. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/lockfile.py +88 -2
  86. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/metadata.py +23 -0
  87. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/provider.py +82 -33
  88. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/requirements_file.py +129 -15
  89. nab_python-0.0.4/src/nab_python/resolve.py +926 -0
  90. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/universal/matrix.py +130 -33
  91. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/universal/provider.py +20 -5
  92. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/universal/reresolve.py +1 -0
  93. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/universal/resolve.py +266 -34
  94. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/universal/validate.py +8 -15
  95. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/universal/wheel_selection.py +92 -57
  96. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/workspace.py +8 -2
  97. nab_python-0.0.4/tests/__init__.py +1 -0
  98. nab_python-0.0.4/tests/property_python/__init__.py +0 -0
  99. nab_python-0.0.4/tests/property_python/strategies.py +205 -0
  100. nab_python-0.0.4/tests/property_python/test_extras_pep685.py +288 -0
  101. nab_python-0.0.4/tests/property_python/test_lockfile_pep751.py +486 -0
  102. nab_python-0.0.4/tests/property_python/test_marker_overlay.py +135 -0
  103. nab_python-0.0.4/tests/property_python/test_multi_index_pep503.py +207 -0
  104. nab_python-0.0.4/tests/property_python/test_resolver_pep440.py +189 -0
  105. nab_python-0.0.4/tests/ruff.toml +30 -0
  106. nab_python-0.0.4/tests/test_async_transports.py +460 -0
  107. nab_python-0.0.4/tests/test_build_backend.py +360 -0
  108. nab_python-0.0.4/tests/test_build_runner.py +925 -0
  109. nab_python-0.0.4/tests/test_cache.py +260 -0
  110. nab_python-0.0.4/tests/test_cached_client.py +948 -0
  111. nab_python-0.0.4/tests/test_config.py +1860 -0
  112. nab_python-0.0.4/tests/test_download.py +371 -0
  113. nab_python-0.0.4/tests/test_fetch.py +1167 -0
  114. nab_python-0.0.4/tests/test_local_index.py +350 -0
  115. nab_python-0.0.4/tests/test_lockfile.py +3020 -0
  116. nab_python-0.0.4/tests/test_metadata.py +105 -0
  117. nab_python-0.0.4/tests/test_multi_index.py +273 -0
  118. nab_python-0.0.4/tests/test_provider.py +4775 -0
  119. nab_python-0.0.4/tests/test_requirements_file.py +407 -0
  120. nab_python-0.0.4/tests/test_resolve.py +2534 -0
  121. nab_python-0.0.4/tests/test_resolver_packaging.py +503 -0
  122. nab_python-0.0.4/tests/test_simple_client_filenames.py +107 -0
  123. nab_python-0.0.4/tests/test_simple_client_hashes.py +25 -0
  124. nab_python-0.0.4/tests/test_vcs.py +515 -0
  125. nab_python-0.0.4/tests/test_vcs_admission.py +283 -0
  126. nab_python-0.0.4/tests/test_workspace.py +354 -0
  127. nab_python-0.0.4/tests/universal/__init__.py +0 -0
  128. nab_python-0.0.4/tests/universal/property_universal/__init__.py +0 -0
  129. nab_python-0.0.4/tests/universal/property_universal/strategies.py +41 -0
  130. nab_python-0.0.4/tests/universal/property_universal/test_alignment.py +229 -0
  131. nab_python-0.0.4/tests/universal/property_universal/test_matrix.py +302 -0
  132. nab_python-0.0.4/tests/universal/property_universal/test_provider_pep425.py +248 -0
  133. nab_python-0.0.4/tests/universal/property_universal/test_validate.py +143 -0
  134. nab_python-0.0.4/tests/universal/test_matrix.py +519 -0
  135. nab_python-0.0.4/tests/universal/test_reresolve.py +391 -0
  136. nab_python-0.0.4/tests/universal/test_resolve.py +1375 -0
  137. nab_python-0.0.4/tests/universal/test_universal_provider.py +554 -0
  138. nab_python-0.0.4/tests/universal/test_validate.py +951 -0
  139. nab_python-0.0.4/tests/universal/test_wheel_selection.py +461 -0
  140. nab_python-0.0.2/LICENSE +0 -21
  141. nab_python-0.0.2/src/nab_python/_lockfile/disjointness.py +0 -207
  142. nab_python-0.0.2/src/nab_python/_lockfile/pylock.py +0 -323
  143. nab_python-0.0.2/src/nab_python/_vendor/packaging/ranges.py +0 -1803
  144. nab_python-0.0.2/src/nab_python/resolve.py +0 -497
  145. {nab_python-0.0.2 → nab_python-0.0.4}/README.md +0 -0
  146. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/__init__.py +0 -0
  147. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_build/__init__.py +0 -0
  148. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_build/errors.py +0 -0
  149. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_lockfile/__init__.py +0 -0
  150. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_packaging_provider.py +0 -0
  151. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_provider/__init__.py +0 -0
  152. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_provider/build_remote.py +0 -0
  153. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_provider/lookahead.py +0 -0
  154. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_provider/priority.py +0 -0
  155. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_testing/__init__.py +0 -0
  156. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vcs_admission.py +0 -0
  157. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/__init__.py +0 -0
  158. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/LICENSE +0 -0
  159. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/LICENSE.APACHE +0 -0
  160. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/LICENSE.BSD +0 -0
  161. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/__init__.py +0 -0
  162. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/_elffile.py +0 -0
  163. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/_manylinux.py +0 -0
  164. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/_musllinux.py +0 -0
  165. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/_parser.py +0 -0
  166. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/_structures.py +0 -0
  167. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/_tokenizer.py +0 -0
  168. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/dependency_groups.py +0 -0
  169. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/direct_url.py +0 -0
  170. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/errors.py +0 -0
  171. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/licenses/__init__.py +0 -0
  172. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/licenses/_spdx.py +0 -0
  173. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/markers.py +0 -0
  174. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/py.typed +0 -0
  175. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/requirements.py +0 -0
  176. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/utils.py +0 -0
  177. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/_vendor/packaging/version.py +0 -0
  178. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/build_backend.py +0 -0
  179. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/py.typed +0 -0
  180. {nab_python-0.0.2 → nab_python-0.0.4}/src/nab_python/universal/__init__.py +0 -0
@@ -0,0 +1,20 @@
1
+ __pycache__/
2
+ *.pyc
3
+ *.egg-info/
4
+ dist/
5
+ build/
6
+ .pytest_cache/
7
+ .hypothesis/
8
+ .coverage*
9
+ .ruff_cache/
10
+ .hatch/
11
+ docs/_build/
12
+
13
+ *token*.txt
14
+ *.pypirc
15
+ .pypirc
16
+
17
+ # Profiling artefacts. By convention these live in `profiling/` at
18
+ # the workspace root and never inside `docs/`, so the Sphinx build
19
+ # never has to scan them.
20
+ profiling/
@@ -0,0 +1 @@
1
+ ../LICENSE
@@ -1,12 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nab-python
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: Index-backed provider, lockfile emitter, and downloader for nab
5
5
  Project-URL: Homepage, https://github.com/notatallshaw/nab
6
6
  Project-URL: Documentation, https://nab.readthedocs.io/
7
7
  Project-URL: Issues, https://github.com/notatallshaw/nab/issues
8
8
  Project-URL: Source, https://github.com/notatallshaw/nab
9
- Project-URL: Changelog, https://github.com/notatallshaw/nab/blob/main/CHANGELOG.md
9
+ Project-URL: Changelog, https://github.com/notatallshaw/nab/releases
10
10
  Author-email: Damian Shaw <damian.peter.shaw@gmail.com>
11
11
  License-Expression: MIT
12
12
  License-File: LICENSE
@@ -20,8 +20,8 @@ Classifier: Typing :: Typed
20
20
  Requires-Python: >=3.10
21
21
  Requires-Dist: build>=1.2
22
22
  Requires-Dist: installer>=0.7
23
- Requires-Dist: nab-index==0.0.2
24
- Requires-Dist: nab-resolver==0.0.2
23
+ Requires-Dist: nab-index==0.0.4
24
+ Requires-Dist: nab-resolver==0.0.4
25
25
  Requires-Dist: pyproject-hooks>=1.2
26
26
  Requires-Dist: tomli-w>=1.2
27
27
  Requires-Dist: tomli>=2.0
@@ -0,0 +1,53 @@
1
+ # Benchmarks
2
+
3
+ nab ships two benchmark suites that exercise the single-environment
4
+ and the universal resolver against real-world scenarios.
5
+
6
+ ## Single-environment scenarios
7
+
8
+ `nab-python/benchmarks/scenarios.py` runs scenarios drawn from
9
+ real-world resolver issues across pip, uv, poetry, pex, and
10
+ pip-tools. Scenario TOML files live under
11
+ `nab-python/benchmarks/scenarios/`.
12
+
13
+ ```bash
14
+ python nab-python/benchmarks/scenarios.py
15
+ ```
16
+
17
+ The runner records wall-clock, decision-count, and round-count
18
+ metrics, and writes a JSON summary under
19
+ `nab-python/benchmarks/results/<commit>/`.
20
+
21
+ ## Universal-resolution scenarios
22
+
23
+ `nab-python/benchmarks/universal_scenarios.py` runs
24
+ universal-resolution scenarios sourced from public uv/poetry/pex
25
+ slowness reports. The cases stress cross-tuple alignment, marker
26
+ divergence, and pre-release handling.
27
+
28
+ ```bash
29
+ python nab-python/benchmarks/universal_scenarios.py
30
+ python nab-python/benchmarks/universal_summary.py
31
+ ```
32
+
33
+ `universal_summary.py` walks the latest results directory and
34
+ prints a markdown table.
35
+
36
+ ## Scenario shape
37
+
38
+ Each scenario is a top-level TOML table keyed by name, with at least
39
+ `requirements` and a fixed `datetime` (used as the
40
+ `uploaded-prior-to` cutoff). Optional knobs include constraints,
41
+ marker overlay, dist policy, and build policy.
42
+
43
+ ## What the suites cover
44
+
45
+ * Tight version-cluster cases (e.g. boto3, awscli).
46
+ * Conflict graphs that have hit pip's default resolver budget
47
+ (numpy/scipy/scikit-learn matrices).
48
+ * Universal-mode fork-explosion cases (xinference, vllm,
49
+ ultralytics, copick).
50
+
51
+ The suites are diagnostic harnesses that flag regressions on pull
52
+ requests. Walltime is noisy; decision count and round count are
53
+ the load-bearing numbers.
@@ -0,0 +1,139 @@
1
+ r"""Single-scenario profiling runner.
2
+
3
+ Usage:
4
+ # sampling profiler (requires .venv-3.15)
5
+ .venv-3.15/bin/python -m profiling.sampling run -r 5khz --flamegraph \
6
+ -o profile.html nab-python/benchmarks/_profile_runner.py <scenario>
7
+
8
+ # cProfile (any venv)
9
+ python nab-python/benchmarks/_profile_runner.py <scenario> --cprofile
10
+
11
+ <scenario> is a bare name (first TOML match wins) or ``toml_stem:name``,
12
+ e.g. ``pip-lowest:cburroughs-v3``.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import argparse
18
+ import cProfile
19
+ import pstats
20
+ import sys
21
+ from pathlib import Path
22
+
23
+ if sys.version_info >= (3, 11):
24
+ import tomllib
25
+ else:
26
+ import tomli as tomllib # type: ignore[no-redef]
27
+
28
+ sys.path.insert(0, str(Path(__file__).parent))
29
+
30
+ import scenarios as sc
31
+
32
+
33
+ def find_scenario(spec: str) -> tuple[str, dict]:
34
+ if ":" in spec:
35
+ toml_stem, name = spec.split(":", 1)
36
+ toml_path = sc.SCENARIOS_DIR / f"{toml_stem}.toml"
37
+ try:
38
+ with toml_path.open("rb") as f:
39
+ data = tomllib.load(f)
40
+ except FileNotFoundError:
41
+ sys.exit(f"no TOML file for stem {toml_stem!r}")
42
+ if name not in data:
43
+ sys.exit(f"scenario {name!r} not found in {toml_stem}.toml")
44
+ return name, data[name]
45
+ for path in sorted(sc.SCENARIOS_DIR.glob("*.toml")):
46
+ with path.open("rb") as f:
47
+ data = tomllib.load(f)
48
+ if spec in data:
49
+ return spec, data[spec]
50
+ sys.exit(f"scenario {spec!r} not found")
51
+
52
+
53
+ def build_inputs(name: str, scenario: dict) -> dict:
54
+ python_version = scenario["python_version"]
55
+ requirement_strings = list(scenario["requirements"])
56
+ constraint_strings = scenario.get("constraints", [])
57
+ marker_environment = sc.parse_marker_environment(name, scenario)
58
+ build_policy_overrides = sc.parse_build_packages(name, scenario)
59
+
60
+ # BUILD_REMOTE + marker_environment overlay is unsupported; drop overrides.
61
+ if marker_environment and build_policy_overrides:
62
+ build_policy_overrides = {}
63
+
64
+ resolution_strategy = sc.ResolutionStrategy(scenario.get("resolution", "highest"))
65
+ vcs_config = sc.VcsConfig(
66
+ policy=sc.VcsPolicy(scenario.get("vcs_policy", "block")),
67
+ allowed_schemes=frozenset(scenario.get("vcs_allowed_schemes", [])),
68
+ allowed_repos=tuple(scenario.get("vcs_allowed_repos", [])),
69
+ require_pin=scenario.get("vcs_require_pin", True),
70
+ )
71
+
72
+ if scenario.get("project_name"):
73
+ requirement_strings += sc.expand_project_extras(
74
+ scenario["project_name"],
75
+ scenario.get("project_extras", []),
76
+ scenario.get("optional_dependencies", {}),
77
+ )
78
+
79
+ marker_env = sc.scenario_marker_env(python_version, marker_environment)
80
+ requirements = sc.parse_requirements(
81
+ requirement_strings, vcs_config=vcs_config, marker_environment=marker_env
82
+ )
83
+ constraints = (
84
+ sc.parse_requirements(
85
+ constraint_strings, vcs_config=vcs_config, marker_environment=marker_env
86
+ )
87
+ if constraint_strings
88
+ else None
89
+ )
90
+
91
+ datetime_str = scenario.get("datetime")
92
+ return {
93
+ "requirements": requirements,
94
+ "python_version": python_version,
95
+ "uploaded_prior_to": sc.parse_datetime(datetime_str) if datetime_str else None,
96
+ "constraints": constraints,
97
+ "marker_environment": marker_environment or None,
98
+ "indexes": sc.parse_indexes(name, scenario),
99
+ "index_overrides": sc.parse_index_overrides(name, scenario) or None,
100
+ "build_policy_overrides": build_policy_overrides or None,
101
+ "resolution_strategy": resolution_strategy,
102
+ }
103
+
104
+
105
+ def report(name: str, data: dict) -> None:
106
+ stats = data["stats"]
107
+ state = "OK" if data["result"]["success"] else f"FAIL {data['result']['error']}"
108
+ print(
109
+ f"{name}: {state} in {stats['wall_time_seconds']}s, "
110
+ f"{stats['decisions']} decisions, {stats['conflicts']} conflicts"
111
+ )
112
+
113
+
114
+ def main() -> None:
115
+ parser = argparse.ArgumentParser(description=__doc__)
116
+ parser.add_argument("scenario", help="bare name or toml_stem:name")
117
+ parser.add_argument(
118
+ "--cprofile",
119
+ action="store_true",
120
+ help="profile in-process and print the top functions by self time",
121
+ )
122
+ parser.add_argument("--limit", type=int, default=30)
123
+ args = parser.parse_args()
124
+
125
+ name, scenario = find_scenario(args.scenario)
126
+ inputs = build_inputs(name, scenario)
127
+
128
+ if not args.cprofile:
129
+ report(name, sc.resolve_scenario(**inputs))
130
+ return
131
+
132
+ profiler = cProfile.Profile()
133
+ data = profiler.runcall(sc.resolve_scenario, **inputs)
134
+ report(name, data)
135
+ pstats.Stats(profiler).sort_stats("tottime").print_stats(args.limit)
136
+
137
+
138
+ if __name__ == "__main__":
139
+ main()
@@ -0,0 +1 @@
1
+ *