nab-python 0.0.4__tar.gz → 0.0.6__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 (194) hide show
  1. {nab_python-0.0.4 → nab_python-0.0.6}/PKG-INFO +3 -3
  2. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/_profile_runner.py +1 -1
  3. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/canary.py +92 -48
  4. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/compare.py +0 -1
  5. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/pdm-lowest-direct.toml +0 -1
  6. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/pdm-lowest.toml +0 -1
  7. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/pdm.toml +0 -1
  8. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/uv-lowest-direct.toml +3 -4
  9. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/uv-lowest.toml +3 -4
  10. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/uv.toml +3 -4
  11. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios.py +42 -39
  12. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/strategy_sweep.py +18 -10
  13. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/universal_scenarios.py +76 -2
  14. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/universal_summary.py +23 -3
  15. {nab_python-0.0.4 → nab_python-0.0.6}/pyproject.toml +3 -3
  16. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_build/env.py +17 -9
  17. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_build/runner.py +2 -2
  18. nab_python-0.0.6/src/nab_python/_conflict_kind.py +62 -0
  19. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_lockfile/builder.py +81 -38
  20. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_lockfile/disjointness.py +5 -5
  21. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_lockfile/pylock.py +83 -5
  22. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_lockfile/requirements.py +17 -5
  23. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_provider/build_remote.py +39 -4
  24. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_provider/extras.py +38 -23
  25. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_provider/listing.py +189 -60
  26. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_provider/metadata_resolver.py +126 -36
  27. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_provider/priority.py +3 -1
  28. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_provider/sources.py +3 -5
  29. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_testing/coordinator_fake.py +6 -1
  30. nab_python-0.0.6/src/nab_python/_testing/overrides.py +18 -0
  31. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vcs_admission.py +37 -17
  32. nab_python-0.0.6/src/nab_python/_vendor/packaging/PROVENANCE.md +92 -0
  33. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_elffile.py +5 -5
  34. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_manylinux.py +30 -15
  35. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_parser.py +21 -4
  36. nab_python-0.0.6/src/nab_python/_vendor/packaging/_ranges.py +836 -0
  37. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_tokenizer.py +5 -1
  38. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/dependency_groups.py +29 -1
  39. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/direct_url.py +19 -4
  40. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/licenses/__init__.py +18 -6
  41. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/markers.py +29 -6
  42. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/metadata.py +3 -2
  43. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/pylock.py +6 -1
  44. nab_python-0.0.6/src/nab_python/_vendor/packaging/ranges.py +1306 -0
  45. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/requirements.py +60 -20
  46. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/specifiers.py +290 -374
  47. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/tags.py +106 -43
  48. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/utils.py +33 -5
  49. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/build_backend.py +11 -3
  50. nab_python-0.0.6/src/nab_python/config.py +2200 -0
  51. nab_python-0.0.6/src/nab_python/config_sources.py +1799 -0
  52. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/download.py +45 -14
  53. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/fetch.py +185 -78
  54. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/lockfile.py +60 -5
  55. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/metadata.py +29 -6
  56. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/provider.py +469 -93
  57. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/requirements_file.py +143 -18
  58. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/resolve.py +168 -58
  59. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/universal/matrix.py +11 -6
  60. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/universal/provider.py +7 -8
  61. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/universal/reresolve.py +38 -22
  62. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/universal/resolve.py +62 -50
  63. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/universal/validate.py +140 -29
  64. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/universal/wheel_selection.py +89 -21
  65. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/workspace.py +8 -1
  66. {nab_python-0.0.4 → nab_python-0.0.6}/tests/property_python/strategies.py +47 -0
  67. nab_python-0.0.6/tests/property_python/test_build_policy_never.py +151 -0
  68. nab_python-0.0.6/tests/property_python/test_download_hashes.py +123 -0
  69. nab_python-0.0.6/tests/property_python/test_fetch_coordinator.py +272 -0
  70. {nab_python-0.0.4 → nab_python-0.0.6}/tests/property_python/test_multi_index_pep503.py +12 -4
  71. nab_python-0.0.6/tests/property_python/test_override_overlap.py +79 -0
  72. nab_python-0.0.6/tests/property_python/test_prerelease_admission.py +163 -0
  73. nab_python-0.0.6/tests/property_python/test_vcs_admission.py +227 -0
  74. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_async_transports.py +26 -1
  75. nab_python-0.0.6/tests/test_benchmark_parse_requirements.py +41 -0
  76. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_build_backend.py +135 -0
  77. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_build_runner.py +32 -0
  78. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_cached_client.py +275 -4
  79. nab_python-0.0.6/tests/test_config.py +3067 -0
  80. nab_python-0.0.6/tests/test_config_sources.py +2330 -0
  81. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_download.py +147 -0
  82. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_fetch.py +427 -73
  83. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_local_index.py +80 -0
  84. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_lockfile.py +581 -7
  85. nab_python-0.0.6/tests/test_marker_evaluation.py +221 -0
  86. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_metadata.py +58 -0
  87. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_multi_index.py +10 -2
  88. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_provider.py +2288 -202
  89. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_requirements_file.py +231 -5
  90. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_resolve.py +554 -58
  91. nab_python-0.0.6/tests/test_resolver_packaging.py +923 -0
  92. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_simple_client_filenames.py +16 -0
  93. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_simple_client_hashes.py +12 -0
  94. nab_python-0.0.6/tests/test_simple_client_upload_time.py +33 -0
  95. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_vcs.py +34 -1
  96. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_vcs_admission.py +94 -12
  97. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_workspace.py +18 -0
  98. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/test_matrix.py +10 -0
  99. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/test_reresolve.py +100 -3
  100. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/test_resolve.py +138 -13
  101. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/test_universal_provider.py +73 -7
  102. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/test_validate.py +393 -14
  103. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/test_wheel_selection.py +139 -2
  104. nab_python-0.0.4/src/nab_python/_conflict_kind.py +0 -20
  105. nab_python-0.0.4/src/nab_python/_vendor/packaging/PROVENANCE.md +0 -73
  106. nab_python-0.0.4/src/nab_python/_vendor/packaging/_range_utils.py +0 -998
  107. nab_python-0.0.4/src/nab_python/_vendor/packaging/_version_utils.py +0 -90
  108. nab_python-0.0.4/src/nab_python/_vendor/packaging/ranges.py +0 -2112
  109. nab_python-0.0.4/src/nab_python/config.py +0 -1443
  110. nab_python-0.0.4/tests/test_config.py +0 -1860
  111. nab_python-0.0.4/tests/test_resolver_packaging.py +0 -503
  112. {nab_python-0.0.4 → nab_python-0.0.6}/.gitignore +0 -0
  113. {nab_python-0.0.4 → nab_python-0.0.6}/LICENSE +0 -0
  114. {nab_python-0.0.4 → nab_python-0.0.6}/README.md +0 -0
  115. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/README.md +0 -0
  116. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/cache/.gitignore +0 -0
  117. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/canary_results/.gitignore +0 -0
  118. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/results/.gitignore +0 -0
  119. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/ai-stack-lowest-direct.toml +0 -0
  120. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/ai-stack-lowest.toml +0 -0
  121. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/ai-stack.toml +0 -0
  122. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/airflow-lowest-direct.toml +0 -0
  123. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/airflow-lowest.toml +0 -0
  124. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/airflow.toml +0 -0
  125. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/big-packages-lowest-direct.toml +0 -0
  126. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/big-packages-lowest.toml +0 -0
  127. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/big-packages.toml +0 -0
  128. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/cross-tracker-lowest-direct.toml +0 -0
  129. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/cross-tracker-lowest.toml +0 -0
  130. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/cross-tracker.toml +0 -0
  131. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/ecosystem-lowest-direct.toml +0 -0
  132. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/ecosystem-lowest.toml +0 -0
  133. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/ecosystem.toml +0 -0
  134. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/forums-lowest-direct.toml +0 -0
  135. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/forums-lowest.toml +0 -0
  136. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/forums.toml +0 -0
  137. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/pip-lowest-direct.toml +0 -0
  138. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/pip-lowest.toml +0 -0
  139. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/pip.toml +0 -0
  140. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/poetry-lowest-direct.toml +0 -0
  141. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/poetry-lowest.toml +0 -0
  142. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/poetry.toml +0 -0
  143. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/pytorch-lowest-direct.toml +0 -0
  144. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/pytorch-lowest.toml +0 -0
  145. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/pytorch.toml +0 -0
  146. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/quick-lowest-direct.toml +0 -0
  147. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/quick-lowest.toml +0 -0
  148. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/quick.toml +0 -0
  149. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/rip-lowest-direct.toml +0 -0
  150. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/rip-lowest.toml +0 -0
  151. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/rip.toml +0 -0
  152. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/universal.toml +0 -0
  153. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/unsupported-lowest-direct.toml +0 -0
  154. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/unsupported-lowest.toml +0 -0
  155. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/scenarios/unsupported.toml +0 -0
  156. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/strategy_sweep_results/summary.json +0 -0
  157. {nab_python-0.0.4 → nab_python-0.0.6}/benchmarks/strategy_sweep_summary.py +0 -0
  158. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/__init__.py +0 -0
  159. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_build/__init__.py +0 -0
  160. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_build/errors.py +0 -0
  161. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_lockfile/__init__.py +0 -0
  162. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_packaging_provider.py +0 -0
  163. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_provider/__init__.py +0 -0
  164. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_provider/lookahead.py +0 -0
  165. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_testing/__init__.py +0 -0
  166. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_toml.py +0 -0
  167. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/__init__.py +0 -0
  168. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/LICENSE +0 -0
  169. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/LICENSE.APACHE +0 -0
  170. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/LICENSE.BSD +0 -0
  171. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/__init__.py +0 -0
  172. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_musllinux.py +0 -0
  173. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_structures.py +0 -0
  174. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/errors.py +0 -0
  175. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/licenses/_spdx.py +0 -0
  176. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/py.typed +0 -0
  177. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/version.py +0 -0
  178. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/py.typed +0 -0
  179. {nab_python-0.0.4 → nab_python-0.0.6}/src/nab_python/universal/__init__.py +0 -0
  180. {nab_python-0.0.4 → nab_python-0.0.6}/tests/__init__.py +0 -0
  181. {nab_python-0.0.4 → nab_python-0.0.6}/tests/property_python/__init__.py +0 -0
  182. {nab_python-0.0.4 → nab_python-0.0.6}/tests/property_python/test_extras_pep685.py +0 -0
  183. {nab_python-0.0.4 → nab_python-0.0.6}/tests/property_python/test_lockfile_pep751.py +0 -0
  184. {nab_python-0.0.4 → nab_python-0.0.6}/tests/property_python/test_marker_overlay.py +0 -0
  185. {nab_python-0.0.4 → nab_python-0.0.6}/tests/property_python/test_resolver_pep440.py +0 -0
  186. {nab_python-0.0.4 → nab_python-0.0.6}/tests/ruff.toml +0 -0
  187. {nab_python-0.0.4 → nab_python-0.0.6}/tests/test_cache.py +0 -0
  188. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/__init__.py +0 -0
  189. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/property_universal/__init__.py +0 -0
  190. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/property_universal/strategies.py +0 -0
  191. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/property_universal/test_alignment.py +0 -0
  192. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/property_universal/test_matrix.py +0 -0
  193. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/property_universal/test_provider_pep425.py +0 -0
  194. {nab_python-0.0.4 → nab_python-0.0.6}/tests/universal/property_universal/test_validate.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nab-python
3
- Version: 0.0.4
3
+ Version: 0.0.6
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/
@@ -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.4
24
- Requires-Dist: nab-resolver==0.0.4
23
+ Requires-Dist: nab-index==0.0.6
24
+ Requires-Dist: nab-resolver==0.0.6
25
25
  Requires-Dist: pyproject-hooks>=1.2
26
26
  Requires-Dist: tomli-w>=1.2
27
27
  Requires-Dist: tomli>=2.0
@@ -96,7 +96,7 @@ def build_inputs(name: str, scenario: dict) -> dict:
96
96
  "constraints": constraints,
97
97
  "marker_environment": marker_environment or None,
98
98
  "indexes": sc.parse_indexes(name, scenario),
99
- "index_overrides": sc.parse_index_overrides(name, scenario) or None,
99
+ "index_routes": sc.parse_index_routes(name, scenario) or None,
100
100
  "build_policy_overrides": build_policy_overrides or None,
101
101
  "resolution_strategy": resolution_strategy,
102
102
  }
@@ -1,9 +1,9 @@
1
1
  """Quick canary benchmark for fast iteration.
2
2
 
3
3
  Runs a curated subset of scenarios (canaries + hard cases) N times each
4
- and reports median decisions, conflicts, and wall time. The set is
5
- small enough to finish in a few minutes so it can be re-run after each
6
- algorithm change.
4
+ and reports median decisions, distributions seen (search breadth), and
5
+ wall time. The set is small enough to finish in a few minutes so it can
6
+ be re-run after each algorithm change.
7
7
 
8
8
  Usage:
9
9
  python nab-python/benchmarks/canary.py [--commit LABEL] [--runs N]
@@ -38,11 +38,12 @@ from nab_python._vendor.packaging.markers import default_environment
38
38
  from nab_python._vendor.packaging.ranges import VersionRange
39
39
  from nab_python._vendor.packaging.requirements import Requirement
40
40
  from nab_python._vendor.packaging.utils import canonicalize_name
41
+ from nab_python.config import PackageOverride
41
42
  from nab_python.fetch import (
42
43
  DEFAULT_INDEX_NAME,
43
44
  DEFAULT_INDEX_URL,
44
45
  FetchCoordinator,
45
- IndexOverride,
46
+ IndexRoute,
46
47
  )
47
48
  from nab_python.provider import (
48
49
  BuildPolicy,
@@ -62,26 +63,30 @@ DEFAULT_INDEXES: tuple[IndexConfig, ...] = (
62
63
  IndexConfig(DEFAULT_INDEX_NAME, DEFAULT_INDEX_URL),
63
64
  )
64
65
 
66
+ # Each entry pins its TOML variant as ``stem:name``. Most of these names also
67
+ # live in -lowest / -lowest-direct siblings with different counts; the pins
68
+ # record the variant the suite already ran so the numbers are reproducible
69
+ # instead of depending on directory scan order.
65
70
  CANARY_SCENARIOS = [
66
- "boto3-urllib3-transient",
67
- "trustllm",
68
- "copick",
69
- "promptflow-vectordb",
70
- "ultralytics-export",
71
- "datacontract-cli",
72
- "pandas-aws-boto3-dandi-frenzy",
73
- "vllm-transformers-floor",
74
- "google-bigquery-soda",
75
- "langchain-ml-course",
76
- "airflow-3-0-2-awswrangler",
77
- "airflow-3-0-3-pandas-sqlalchemy",
78
- "airflow-portalocker-qdrant",
79
- "airflow-fastapi-121",
80
- "so-dbt-core-snowflake-79744735",
81
- "uv-issue-16601-xinference",
82
- "uv-issue-16601-xinference-fixed",
83
- "rag-chroma-langchain",
84
- "streamlit-langchain",
71
+ "uv-lowest:boto3-urllib3-transient",
72
+ "pip-lowest:trustllm",
73
+ "pip-lowest:copick",
74
+ "pip-lowest:promptflow-vectordb",
75
+ "pip-lowest:ultralytics-export",
76
+ "pip-lowest:datacontract-cli",
77
+ "pip-lowest:pandas-aws-boto3-dandi-frenzy",
78
+ "ai-stack:vllm-transformers-floor",
79
+ "pip-lowest:google-bigquery-soda",
80
+ "pip-lowest:langchain-ml-course",
81
+ "airflow-lowest-direct:airflow-3-0-2-awswrangler",
82
+ "airflow-lowest-direct:airflow-3-0-3-pandas-sqlalchemy",
83
+ "airflow-lowest-direct:airflow-portalocker-qdrant",
84
+ "airflow-lowest-direct:airflow-fastapi-121",
85
+ "forums-lowest-direct:so-dbt-core-snowflake-79744735",
86
+ "uv-lowest:uv-issue-16601-xinference",
87
+ "uv-lowest:uv-issue-16601-xinference-fixed",
88
+ "ai-stack:rag-chroma-langchain",
89
+ "ai-stack:streamlit-langchain",
85
90
  ]
86
91
 
87
92
  WALL_TIMEOUT_S = 60
@@ -150,7 +155,7 @@ def parse_requirements(
150
155
  )
151
156
  raise NotImplementedError(msg)
152
157
  name = canonicalize_name(req.name)
153
- reqs[name] = req.specifier.to_range()
158
+ reqs[name] = reqs.get(name, VersionRange.full()) & req.specifier.to_range()
154
159
  for extra in req.extras:
155
160
  reqs[f"{name}[{extra}]"] = VersionRange.full()
156
161
  return reqs
@@ -208,17 +213,25 @@ def run_one( # noqa: PLR0913 - one wrapper per scenario knob
208
213
  constraints: dict[str, VersionRange] | None,
209
214
  marker_environment: dict[str, str] | None = None,
210
215
  indexes: list[IndexConfig] | None = None,
211
- index_overrides: list[IndexOverride] | None = None,
216
+ index_routes: list[IndexRoute] | None = None,
212
217
  build_policy_overrides: Mapping[str, BuildPolicy] | None = None,
213
218
  *,
214
219
  trust_unverified_sdist_deps: bool = False,
215
220
  ) -> dict:
221
+ package_overrides = tuple(
222
+ PackageOverride(
223
+ requirement=Requirement(name),
224
+ name=canonicalize_name(name),
225
+ version_range=VersionRange.full(),
226
+ build_policy=policy,
227
+ )
228
+ for name, policy in (build_policy_overrides or {}).items()
229
+ )
216
230
  with FetchCoordinator(
217
231
  HttpxAsyncTransport(),
218
232
  indexes=indexes,
219
233
  cache_dir=CACHE_DIR,
220
- index_overrides=index_overrides,
221
- marker_environment=marker_environment,
234
+ index_routes=index_routes,
222
235
  ) as coordinator:
223
236
  provider = Provider(
224
237
  coordinator,
@@ -227,7 +240,7 @@ def run_one( # noqa: PLR0913 - one wrapper per scenario knob
227
240
  uploaded_prior_to=uploaded_prior_to,
228
241
  dist_policy=DistPolicy.WHEEL_OR_SDIST,
229
242
  build_policy=BuildPolicy.NEVER,
230
- build_policy_overrides=build_policy_overrides,
243
+ package_overrides=package_overrides,
231
244
  trust_unverified_sdist_deps=trust_unverified_sdist_deps,
232
245
  marker_environment=marker_environment,
233
246
  )
@@ -268,19 +281,46 @@ def run_one( # noqa: PLR0913 - one wrapper per scenario knob
268
281
  "restarts": rs.restarts,
269
282
  "incompatibilities_learned": rs.incompatibilities_learned,
270
283
  "metadata_fetched": ps.metadata_fetched,
284
+ "distributions_seen": ps.distributions_seen,
271
285
  "look_ahead_rejections": ps.look_ahead_rejections,
272
286
  "packages": packages,
273
287
  "wall_time_seconds": round(elapsed, 3),
274
288
  }
275
289
 
276
290
 
277
- def find_scenario(scenario_name: str) -> dict | None:
278
- for toml_file in SCENARIOS_DIR.glob("*.toml"):
291
+ def find_scenario(spec: str) -> dict | None:
292
+ """Locate a scenario by name, or by ``stem:name`` to pin a specific TOML file.
293
+
294
+ Many names live in several variant TOMLs (a ``highest`` file plus
295
+ ``-lowest`` / ``-lowest-direct`` siblings) with different decision counts.
296
+ A bare name scans the files in sorted order so the match is reproducible
297
+ across machines, and warns when the name is ambiguous so an exact-count run
298
+ can pin the variant with ``stem:name``.
299
+ """
300
+ if ":" in spec:
301
+ toml_stem, name = spec.split(":", 1)
302
+ toml_path = SCENARIOS_DIR / f"{toml_stem}.toml"
303
+ if not toml_path.is_file():
304
+ return None
305
+ with toml_path.open("rb") as f:
306
+ return tomllib.load(f).get(name)
307
+
308
+ matches: list[tuple[str, dict]] = []
309
+ for toml_file in sorted(SCENARIOS_DIR.glob("*.toml")):
279
310
  with toml_file.open("rb") as f:
280
311
  data = tomllib.load(f)
281
- if scenario_name in data:
282
- return data[scenario_name]
283
- return None
312
+ if spec in data:
313
+ matches.append((toml_file.stem, data[spec]))
314
+ if not matches:
315
+ return None
316
+ if len(matches) > 1:
317
+ stems = ", ".join(stem for stem, _ in matches)
318
+ print(
319
+ f" [ambiguous] {spec!r} is in {len(matches)} files ({stems}); "
320
+ f"using {matches[0][0]}. Pin one with '{matches[0][0]}:{spec}'.",
321
+ flush=True,
322
+ )
323
+ return matches[0][1]
284
324
 
285
325
 
286
326
  def median_run(scenario: dict, runs: int) -> tuple[list[dict], dict]:
@@ -328,14 +368,10 @@ def median_run(scenario: dict, runs: int) -> tuple[list[dict], dict]:
328
368
  IndexConfig(name=str(entry["name"]), url=str(entry["url"]))
329
369
  for entry in raw_indexes
330
370
  ]
331
- raw_overrides = scenario.get("index_overrides", [])
332
- index_overrides = [
333
- IndexOverride(
334
- name=str(entry["name"]),
335
- index=str(entry["index"]),
336
- marker=entry.get("marker"),
337
- )
338
- for entry in raw_overrides
371
+ raw_routes = scenario.get("index_routes", [])
372
+ index_routes = [
373
+ IndexRoute(name=str(entry["name"]), index=str(entry["index"]))
374
+ for entry in raw_routes
339
375
  ]
340
376
  raw_build_packages = scenario.get("build_packages", []) or []
341
377
  build_policy_overrides = {
@@ -368,8 +404,10 @@ def median_run(scenario: dict, runs: int) -> tuple[list[dict], dict]:
368
404
  else None
369
405
  )
370
406
  uploaded_prior_to = parse_datetime(datetime_str) if datetime_str else None
407
+ # See scenarios.py: trust pre-2.2 sdist PKG-INFO deps by default so the
408
+ # benchmark measures search, not strict PEP 643 sdist rejection.
371
409
  trust_unverified_sdist_deps = bool(
372
- scenario.get("trust_unverified_sdist_deps", False)
410
+ scenario.get("trust_unverified_sdist_deps", True)
373
411
  )
374
412
 
375
413
  runs_data: list[dict] = [
@@ -380,7 +418,7 @@ def median_run(scenario: dict, runs: int) -> tuple[list[dict], dict]:
380
418
  constraints,
381
419
  marker_environment=marker_environment or None,
382
420
  indexes=indexes,
383
- index_overrides=index_overrides or None,
421
+ index_routes=index_routes or None,
384
422
  build_policy_overrides=build_policy_overrides or None,
385
423
  trust_unverified_sdist_deps=trust_unverified_sdist_deps,
386
424
  )
@@ -395,6 +433,9 @@ def median_run(scenario: dict, runs: int) -> tuple[list[dict], dict]:
395
433
  summary = {
396
434
  "success_runs": f"{successes}/{len(runs_data)}",
397
435
  "median_decisions": int(med("decisions")),
436
+ "median_distributions_seen": int(med("distributions_seen")),
437
+ "median_metadata_fetched": int(med("metadata_fetched")),
438
+ "median_packages": int(med("packages")),
398
439
  "median_conflicts": int(med("conflicts")),
399
440
  "median_backjumps": int(med("backjumps")),
400
441
  "median_wall": round(med("wall_time_seconds"), 2),
@@ -433,29 +474,32 @@ def main() -> None:
433
474
  f"{'scenario':<45} "
434
475
  f"{'success':>9} "
435
476
  f"{'med_dec':>8} "
477
+ f"{'med_dist':>10} "
436
478
  f"{'med_wall':>10} "
437
479
  f"{'min_dec':>8} "
438
480
  f"{'max_dec':>8}"
439
481
  )
440
- print("-" * 100)
482
+ print("-" * 110)
441
483
 
442
484
  summary_all: dict[str, dict] = {}
443
485
  for name in scenarios_to_run:
444
486
  scenario = find_scenario(name)
487
+ label = name.split(":", 1)[-1]
445
488
  if scenario is None:
446
- print(f"{name:<45} NOT FOUND")
489
+ print(f"{label:<45} NOT FOUND")
447
490
  continue
448
491
  runs_data, summary = median_run(scenario, args.runs)
449
- summary_all[name] = {"runs": runs_data, "summary": summary}
492
+ summary_all[label] = {"runs": runs_data, "summary": summary}
450
493
 
451
494
  if "skipped" in summary:
452
- print(f"{name:<45} SKIPPED: {summary['skipped']}")
495
+ print(f"{label:<45} SKIPPED: {summary['skipped']}")
453
496
  continue
454
497
 
455
498
  print(
456
- f"{name:<45} "
499
+ f"{label:<45} "
457
500
  f"{summary['success_runs']:>9} "
458
501
  f"{summary['median_decisions']:>8} "
502
+ f"{summary['median_distributions_seen']:>10} "
459
503
  f"{summary['median_wall']:>10} "
460
504
  f"{summary['min_decisions']:>8} "
461
505
  f"{summary['max_decisions']:>8}"
@@ -38,7 +38,6 @@ STAT_LABELS = {
38
38
  "look_ahead_rejections": "Look-ahead rejections",
39
39
  "packages_resolved": "Packages resolved",
40
40
  "wall_time_seconds": "Wall time (s)",
41
- # legacy keys (older results before stats rework):
42
41
  "listing_requests": "Listing requests (legacy)",
43
42
  "metadata_requests": "Metadata requests (legacy)",
44
43
  }
@@ -329,7 +329,6 @@ resolution = "lowest-direct"
329
329
  python_version = "3.10"
330
330
  platform_system = "Linux"
331
331
  datetime = "2025-10-02 08:42:21"
332
- allow_prereleases = true
333
332
  requirements = [
334
333
  "pydantic>=2.10.6",
335
334
  "pydantic>=2.12.0a1",
@@ -329,7 +329,6 @@ resolution = "lowest"
329
329
  python_version = "3.10"
330
330
  platform_system = "Linux"
331
331
  datetime = "2025-10-02 08:42:21"
332
- allow_prereleases = true
333
332
  requirements = [
334
333
  "pydantic>=2.10.6",
335
334
  "pydantic>=2.12.0a1",
@@ -308,7 +308,6 @@ requirements = ["numpy"]
308
308
  python_version = "3.10"
309
309
  platform_system = "Linux"
310
310
  datetime = "2025-10-02 08:42:21"
311
- allow_prereleases = true
312
311
  requirements = [
313
312
  "pydantic>=2.10.6",
314
313
  "pydantic>=2.12.0a1",
@@ -1530,8 +1530,8 @@ requirements = [
1530
1530
  # Migrated from unsupported.toml (2026-04-27). These were filed against
1531
1531
  # original tool-specific features (uv universal resolution, multi-index,
1532
1532
  # --pre flag, project install) but the requirement list itself is a valid
1533
- # single-platform PyPI input that nab can resolve. Scenarios marked with
1534
- # allow_prereleases = true require pre-release pinning to find a candidate.
1533
+ # single-platform PyPI input that nab can resolve. Some need a
1534
+ # pre-release pin to find a candidate.
1535
1535
  # ============================================================================
1536
1536
 
1537
1537
  [uv-pyspark-resolution]
@@ -1624,12 +1624,11 @@ requirements = ["playwright"]
1624
1624
  [uv-issue-11636-llvmlite-prerelease]
1625
1625
  # https://github.com/astral-sh/uv/issues/11636
1626
1626
  # llvmlite==0.44.0 only exists as a pre-release on PyPI at the chosen
1627
- # date; needs allow_prereleases to find a candidate.
1627
+ # date; needs a pre-release pin to find a candidate.
1628
1628
  resolution = "lowest-direct"
1629
1629
  python_version = "3.13"
1630
1630
  platform_system = "Linux"
1631
1631
  datetime = "2025-02-19 20:01:16"
1632
- allow_prereleases = true
1633
1632
  requirements = ["llvmlite==0.44.0"]
1634
1633
 
1635
1634
  [uv-tensorflow-macos]
@@ -1530,8 +1530,8 @@ requirements = [
1530
1530
  # Migrated from unsupported.toml (2026-04-27). These were filed against
1531
1531
  # original tool-specific features (uv universal resolution, multi-index,
1532
1532
  # --pre flag, project install) but the requirement list itself is a valid
1533
- # single-platform PyPI input that nab can resolve. Scenarios marked with
1534
- # allow_prereleases = true require pre-release pinning to find a candidate.
1533
+ # single-platform PyPI input that nab can resolve. Some need a
1534
+ # pre-release pin to find a candidate.
1535
1535
  # ============================================================================
1536
1536
 
1537
1537
  [uv-pyspark-resolution]
@@ -1624,12 +1624,11 @@ requirements = ["playwright"]
1624
1624
  [uv-issue-11636-llvmlite-prerelease]
1625
1625
  # https://github.com/astral-sh/uv/issues/11636
1626
1626
  # llvmlite==0.44.0 only exists as a pre-release on PyPI at the chosen
1627
- # date; needs allow_prereleases to find a candidate.
1627
+ # date; needs a pre-release pin to find a candidate.
1628
1628
  resolution = "lowest"
1629
1629
  python_version = "3.13"
1630
1630
  platform_system = "Linux"
1631
1631
  datetime = "2025-02-19 20:01:16"
1632
- allow_prereleases = true
1633
1632
  requirements = ["llvmlite==0.44.0"]
1634
1633
 
1635
1634
  [uv-tensorflow-macos]
@@ -1422,8 +1422,8 @@ requirements = [
1422
1422
  # Migrated from unsupported.toml (2026-04-27). These were filed against
1423
1423
  # original tool-specific features (uv universal resolution, multi-index,
1424
1424
  # --pre flag, project install) but the requirement list itself is a valid
1425
- # single-platform PyPI input that nab can resolve. Scenarios marked with
1426
- # allow_prereleases = true require pre-release pinning to find a candidate.
1425
+ # single-platform PyPI input that nab can resolve. Some need a
1426
+ # pre-release pin to find a candidate.
1427
1427
  # ============================================================================
1428
1428
 
1429
1429
  [uv-pyspark-resolution]
@@ -1509,11 +1509,10 @@ requirements = ["playwright"]
1509
1509
  [uv-issue-11636-llvmlite-prerelease]
1510
1510
  # https://github.com/astral-sh/uv/issues/11636
1511
1511
  # llvmlite==0.44.0 only exists as a pre-release on PyPI at the chosen
1512
- # date; needs allow_prereleases to find a candidate.
1512
+ # date; needs a pre-release pin to find a candidate.
1513
1513
  python_version = "3.13"
1514
1514
  platform_system = "Linux"
1515
1515
  datetime = "2025-02-19 20:01:16"
1516
- allow_prereleases = true
1517
1516
  requirements = ["llvmlite==0.44.0"]
1518
1517
 
1519
1518
  [uv-tensorflow-macos]
@@ -35,11 +35,12 @@ from nab_python._vendor.packaging.markers import default_environment
35
35
  from nab_python._vendor.packaging.ranges import VersionRange
36
36
  from nab_python._vendor.packaging.requirements import Requirement
37
37
  from nab_python._vendor.packaging.utils import canonicalize_name
38
+ from nab_python.config import PackageOverride
38
39
  from nab_python.fetch import (
39
40
  DEFAULT_INDEX_NAME,
40
41
  DEFAULT_INDEX_URL,
41
42
  FetchCoordinator,
42
- IndexOverride,
43
+ IndexRoute,
43
44
  )
44
45
  from nab_python.provider import (
45
46
  BuildPolicy,
@@ -137,7 +138,7 @@ def parse_requirements(
137
138
  )
138
139
  raise NotImplementedError(msg)
139
140
  name = canonicalize_name(req.name)
140
- reqs[name] = req.specifier.to_range()
141
+ reqs[name] = reqs.get(name, VersionRange.full()) & req.specifier.to_range()
141
142
  for extra in req.extras:
142
143
  reqs[f"{name}[{extra}]"] = VersionRange.full()
143
144
  return reqs
@@ -212,7 +213,7 @@ def resolve_scenario( # noqa: PLR0913 - one wrapper per scenario knob
212
213
  constraints: dict[str, VersionRange] | None = None,
213
214
  marker_environment: dict[str, str] | None = None,
214
215
  indexes: list[IndexConfig] | None = None,
215
- index_overrides: list[IndexOverride] | None = None,
216
+ index_routes: list[IndexRoute] | None = None,
216
217
  build_policy_overrides: Mapping[str, BuildPolicy] | None = None,
217
218
  resolution_strategy: ResolutionStrategy = ResolutionStrategy.HIGHEST,
218
219
  *,
@@ -222,12 +223,20 @@ def resolve_scenario( # noqa: PLR0913 - one wrapper per scenario knob
222
223
  direct_packages = frozenset(
223
224
  name for name in requirements if split_extra(name)[1] is None
224
225
  )
226
+ package_overrides = tuple(
227
+ PackageOverride(
228
+ requirement=Requirement(name),
229
+ name=canonicalize_name(name),
230
+ version_range=VersionRange.full(),
231
+ build_policy=policy,
232
+ )
233
+ for name, policy in (build_policy_overrides or {}).items()
234
+ )
225
235
  with FetchCoordinator(
226
236
  HttpxAsyncTransport(),
227
237
  indexes=indexes,
228
238
  cache_dir=CACHE_DIR,
229
- index_overrides=index_overrides,
230
- marker_environment=marker_environment,
239
+ index_routes=index_routes,
231
240
  ) as coordinator:
232
241
  provider = Provider(
233
242
  coordinator,
@@ -236,7 +245,7 @@ def resolve_scenario( # noqa: PLR0913 - one wrapper per scenario knob
236
245
  uploaded_prior_to=uploaded_prior_to,
237
246
  dist_policy=DistPolicy.WHEEL_OR_SDIST,
238
247
  build_policy=BuildPolicy.NEVER,
239
- build_policy_overrides=build_policy_overrides,
248
+ package_overrides=package_overrides,
240
249
  trust_unverified_sdist_deps=trust_unverified_sdist_deps,
241
250
  marker_environment=marker_environment,
242
251
  resolution_strategy=resolution_strategy,
@@ -316,7 +325,7 @@ def _expected_input( # noqa: PLR0913 - assembling the JSON dump key
316
325
  vcs_policy_str: str,
317
326
  marker_environment: dict[str, str],
318
327
  indexes: list[IndexConfig],
319
- index_overrides: list[IndexOverride],
328
+ index_routes: list[IndexRoute],
320
329
  build_packages: list[str] | None = None,
321
330
  resolution_strategy: ResolutionStrategy = ResolutionStrategy.HIGHEST,
322
331
  *,
@@ -346,14 +355,9 @@ def _expected_input( # noqa: PLR0913 - assembling the JSON dump key
346
355
  expected_input["indexes"] = [
347
356
  {"name": cfg.name, "url": cfg.url} for cfg in indexes
348
357
  ]
349
- if index_overrides:
350
- expected_input["index_overrides"] = [
351
- {
352
- "name": o.name,
353
- "index": o.index,
354
- **({"marker": o.marker} if o.marker else {}),
355
- }
356
- for o in index_overrides
358
+ if index_routes:
359
+ expected_input["index_routes"] = [
360
+ {"name": o.name, "index": o.index} for o in index_routes
357
361
  ]
358
362
  if build_packages:
359
363
  expected_input["build_packages"] = sorted(build_packages)
@@ -364,30 +368,30 @@ def _expected_input( # noqa: PLR0913 - assembling the JSON dump key
364
368
  return expected_input
365
369
 
366
370
 
367
- def parse_index_overrides(
371
+ def parse_index_routes(
368
372
  scenario_name: str,
369
373
  scenario: dict,
370
- ) -> list[IndexOverride]:
371
- """Read the ``index_overrides`` array of records from a scenario.
374
+ ) -> list[IndexRoute]:
375
+ """Read the ``index_routes`` array of records from a scenario.
372
376
 
373
377
  Each entry is a TOML inline table with keys ``name`` (the package
374
- name) and ``index`` (the *name* of an entry in ``indexes``), plus
375
- an optional ``marker``. Entries are returned in declaration order
376
- so :func:`nab_python.fetch._resolve_overrides` can apply
377
- first-match-wins on duplicates.
378
+ name) and ``index`` (the *name* of an entry in ``indexes``). A route
379
+ carries no version scope and no marker. Entries are returned in
380
+ declaration order so :func:`nab_python.fetch._resolve_routes` can
381
+ apply last-match-wins on duplicates.
378
382
  """
379
- raw = scenario.get("index_overrides", [])
383
+ raw = scenario.get("index_routes", [])
380
384
  if not isinstance(raw, list):
381
385
  msg = (
382
- f"{scenario_name}: index_overrides must be a TOML array of"
386
+ f"{scenario_name}: index_routes must be a TOML array of"
383
387
  f" tables, got {type(raw).__name__}"
384
388
  )
385
389
  raise TypeError(msg)
386
- out: list[IndexOverride] = []
390
+ out: list[IndexRoute] = []
387
391
  for entry in raw:
388
392
  if not isinstance(entry, dict):
389
393
  msg = (
390
- f"{scenario_name}: index_overrides entries must be tables,"
394
+ f"{scenario_name}: index_routes entries must be tables,"
391
395
  f" got {type(entry).__name__}"
392
396
  )
393
397
  raise TypeError(msg)
@@ -396,12 +400,10 @@ def parse_index_overrides(
396
400
  index = entry["index"]
397
401
  except KeyError as missing:
398
402
  msg = (
399
- f"{scenario_name}: index_overrides entry missing required"
400
- f" key {missing!s}"
403
+ f"{scenario_name}: index_routes entry missing required key {missing!s}"
401
404
  )
402
405
  raise ValueError(msg) from None
403
- marker = entry.get("marker")
404
- out.append(IndexOverride(name=str(name), index=str(index), marker=marker))
406
+ out.append(IndexRoute(name=str(name), index=str(index)))
405
407
  return out
406
408
 
407
409
 
@@ -513,14 +515,10 @@ def process_scenario(
513
515
  constraint_strings: list[str] = scenario.get("constraints", [])
514
516
  marker_environment = parse_marker_environment(scenario_name, scenario)
515
517
  indexes = parse_indexes(scenario_name, scenario)
516
- index_overrides = parse_index_overrides(scenario_name, scenario)
518
+ index_routes = parse_index_routes(scenario_name, scenario)
517
519
  build_policy_overrides = parse_build_packages(scenario_name, scenario)
518
520
  if marker_environment and build_policy_overrides:
519
- # BuildPolicy.BUILD_REMOTE + marker_environment is rejected at
520
- # provider construction (host backend cannot reflect the
521
- # impersonated target). Drop the per-package builds so the
522
- # scenario still runs; if resolution now fails the override
523
- # was load-bearing and the scenario needs an audit.
521
+ # BUILD_REMOTE + marker_environment is rejected at provider construction.
524
522
  print(
525
523
  f"\n [audit] {scenario_name}: dropping {len(build_policy_overrides)}"
526
524
  " build_packages override(s) because the scenario uses a"
@@ -543,8 +541,13 @@ def process_scenario(
543
541
  f" got {resolution_raw!r}"
544
542
  )
545
543
  raise ValueError(msg) from exc
544
+ # Trust a pre-2.2 sdist's PKG-INFO deps by default. The benchmark measures
545
+ # resolver search, and under BuildPolicy.NEVER the strict PEP 643 product
546
+ # default rejects every sdist-only pre-2.2 pin (UnsupportedSdistError),
547
+ # dropping in-window versions uv resolves by building. A scenario sets this
548
+ # false to exercise strict behavior.
546
549
  trust_unverified_sdist_deps: bool = scenario.get(
547
- "trust_unverified_sdist_deps", False
550
+ "trust_unverified_sdist_deps", True
548
551
  )
549
552
  optional_dependencies: dict[str, list[str]] = scenario.get(
550
553
  "optional_dependencies", {}
@@ -580,7 +583,7 @@ def process_scenario(
580
583
  vcs_policy_str,
581
584
  marker_environment,
582
585
  indexes,
583
- index_overrides,
586
+ index_routes,
584
587
  build_packages=sorted(build_policy_overrides),
585
588
  resolution_strategy=resolution_strategy,
586
589
  trust_unverified_sdist_deps=trust_unverified_sdist_deps,
@@ -615,7 +618,7 @@ def process_scenario(
615
618
  constraints,
616
619
  marker_environment=marker_environment or None,
617
620
  indexes=indexes,
618
- index_overrides=index_overrides or None,
621
+ index_routes=index_routes or None,
619
622
  build_policy_overrides=build_policy_overrides or None,
620
623
  resolution_strategy=resolution_strategy,
621
624
  trust_unverified_sdist_deps=trust_unverified_sdist_deps,