nab-python 0.0.5__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.
- {nab_python-0.0.5 → nab_python-0.0.6}/PKG-INFO +3 -3
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/_profile_runner.py +1 -1
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/canary.py +89 -47
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/compare.py +0 -1
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/pdm-lowest-direct.toml +0 -1
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/pdm-lowest.toml +0 -1
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/pdm.toml +0 -1
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/uv-lowest-direct.toml +3 -4
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/uv-lowest.toml +3 -4
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/uv.toml +3 -4
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios.py +36 -38
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/strategy_sweep.py +18 -10
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/universal_scenarios.py +76 -2
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/universal_summary.py +23 -3
- {nab_python-0.0.5 → nab_python-0.0.6}/pyproject.toml +3 -3
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_build/env.py +17 -9
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_build/runner.py +2 -2
- nab_python-0.0.6/src/nab_python/_conflict_kind.py +62 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_lockfile/builder.py +81 -38
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_lockfile/disjointness.py +5 -5
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_lockfile/pylock.py +17 -4
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_lockfile/requirements.py +5 -1
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_provider/build_remote.py +39 -4
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_provider/extras.py +6 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_provider/listing.py +170 -48
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_provider/metadata_resolver.py +121 -19
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_provider/sources.py +3 -5
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_testing/coordinator_fake.py +6 -1
- nab_python-0.0.6/src/nab_python/_testing/overrides.py +18 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vcs_admission.py +30 -11
- nab_python-0.0.6/src/nab_python/_vendor/packaging/PROVENANCE.md +92 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_elffile.py +5 -5
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_manylinux.py +30 -15
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_parser.py +21 -4
- nab_python-0.0.6/src/nab_python/_vendor/packaging/_ranges.py +836 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_tokenizer.py +5 -1
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/dependency_groups.py +29 -1
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/direct_url.py +19 -4
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/licenses/__init__.py +18 -6
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/markers.py +29 -6
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/metadata.py +3 -2
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/pylock.py +6 -1
- nab_python-0.0.6/src/nab_python/_vendor/packaging/ranges.py +1306 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/requirements.py +60 -20
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/specifiers.py +290 -374
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/tags.py +106 -43
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/utils.py +33 -5
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/build_backend.py +11 -3
- nab_python-0.0.6/src/nab_python/config.py +2200 -0
- nab_python-0.0.6/src/nab_python/config_sources.py +1799 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/download.py +41 -12
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/fetch.py +152 -72
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/lockfile.py +59 -5
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/metadata.py +5 -4
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/provider.py +459 -82
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/requirements_file.py +143 -18
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/resolve.py +166 -54
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/universal/matrix.py +0 -2
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/universal/provider.py +7 -8
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/universal/reresolve.py +27 -21
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/universal/resolve.py +62 -50
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/universal/validate.py +95 -22
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/universal/wheel_selection.py +67 -19
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/workspace.py +8 -1
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/strategies.py +47 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/test_fetch_coordinator.py +12 -2
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/test_multi_index_pep503.py +12 -4
- nab_python-0.0.6/tests/property_python/test_override_overlap.py +79 -0
- nab_python-0.0.6/tests/property_python/test_prerelease_admission.py +163 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/test_vcs_admission.py +43 -9
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_async_transports.py +26 -1
- nab_python-0.0.6/tests/test_benchmark_parse_requirements.py +41 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_build_backend.py +135 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_build_runner.py +32 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_cached_client.py +267 -4
- nab_python-0.0.6/tests/test_config.py +3067 -0
- nab_python-0.0.6/tests/test_config_sources.py +2330 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_download.py +117 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_fetch.py +321 -71
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_local_index.py +56 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_lockfile.py +457 -7
- nab_python-0.0.6/tests/test_marker_evaluation.py +221 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_metadata.py +36 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_multi_index.py +10 -2
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_provider.py +2010 -145
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_requirements_file.py +231 -5
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_resolve.py +545 -36
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_resolver_packaging.py +402 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_simple_client_filenames.py +16 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_simple_client_hashes.py +12 -0
- nab_python-0.0.6/tests/test_simple_client_upload_time.py +33 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_vcs.py +34 -1
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_vcs_admission.py +79 -14
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_workspace.py +18 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/test_reresolve.py +49 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/test_resolve.py +138 -13
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/test_universal_provider.py +73 -7
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/test_validate.py +233 -3
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/test_wheel_selection.py +115 -2
- nab_python-0.0.5/src/nab_python/_conflict_kind.py +0 -20
- nab_python-0.0.5/src/nab_python/_vendor/packaging/PROVENANCE.md +0 -73
- nab_python-0.0.5/src/nab_python/_vendor/packaging/_range_utils.py +0 -998
- nab_python-0.0.5/src/nab_python/_vendor/packaging/_version_utils.py +0 -90
- nab_python-0.0.5/src/nab_python/_vendor/packaging/ranges.py +0 -2112
- nab_python-0.0.5/src/nab_python/config.py +0 -1443
- nab_python-0.0.5/tests/test_config.py +0 -1874
- {nab_python-0.0.5 → nab_python-0.0.6}/.gitignore +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/LICENSE +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/README.md +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/README.md +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/cache/.gitignore +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/canary_results/.gitignore +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/results/.gitignore +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/ai-stack-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/ai-stack-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/ai-stack.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/airflow-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/airflow-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/airflow.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/big-packages-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/big-packages-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/big-packages.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/cross-tracker-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/cross-tracker-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/cross-tracker.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/ecosystem-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/ecosystem-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/ecosystem.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/forums-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/forums-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/forums.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/pip-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/pip-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/pip.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/poetry-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/poetry-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/poetry.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/pytorch-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/pytorch-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/pytorch.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/quick-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/quick-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/quick.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/rip-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/rip-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/rip.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/universal.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/unsupported-lowest-direct.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/unsupported-lowest.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/scenarios/unsupported.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/strategy_sweep_results/summary.json +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/benchmarks/strategy_sweep_summary.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_build/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_build/errors.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_lockfile/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_packaging_provider.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_provider/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_provider/lookahead.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_provider/priority.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_testing/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_toml.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/LICENSE +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/LICENSE.APACHE +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/LICENSE.BSD +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_musllinux.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/_structures.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/errors.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/licenses/_spdx.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/py.typed +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/_vendor/packaging/version.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/py.typed +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/src/nab_python/universal/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/test_build_policy_never.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/test_download_hashes.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/test_extras_pep685.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/test_lockfile_pep751.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/test_marker_overlay.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/property_python/test_resolver_pep440.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/ruff.toml +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/test_cache.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/property_universal/__init__.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/property_universal/strategies.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/property_universal/test_alignment.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/property_universal/test_matrix.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/property_universal/test_provider_pep425.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/property_universal/test_validate.py +0 -0
- {nab_python-0.0.5 → nab_python-0.0.6}/tests/universal/test_matrix.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nab-python
|
|
3
|
-
Version: 0.0.
|
|
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.
|
|
24
|
-
Requires-Dist: nab-resolver==0.0.
|
|
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
|
-
"
|
|
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,
|
|
5
|
-
small enough to finish in a few minutes so it can
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
278
|
-
|
|
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
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
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 = {
|
|
@@ -382,7 +418,7 @@ def median_run(scenario: dict, runs: int) -> tuple[list[dict], dict]:
|
|
|
382
418
|
constraints,
|
|
383
419
|
marker_environment=marker_environment or None,
|
|
384
420
|
indexes=indexes,
|
|
385
|
-
|
|
421
|
+
index_routes=index_routes or None,
|
|
386
422
|
build_policy_overrides=build_policy_overrides or None,
|
|
387
423
|
trust_unverified_sdist_deps=trust_unverified_sdist_deps,
|
|
388
424
|
)
|
|
@@ -397,6 +433,9 @@ def median_run(scenario: dict, runs: int) -> tuple[list[dict], dict]:
|
|
|
397
433
|
summary = {
|
|
398
434
|
"success_runs": f"{successes}/{len(runs_data)}",
|
|
399
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")),
|
|
400
439
|
"median_conflicts": int(med("conflicts")),
|
|
401
440
|
"median_backjumps": int(med("backjumps")),
|
|
402
441
|
"median_wall": round(med("wall_time_seconds"), 2),
|
|
@@ -435,29 +474,32 @@ def main() -> None:
|
|
|
435
474
|
f"{'scenario':<45} "
|
|
436
475
|
f"{'success':>9} "
|
|
437
476
|
f"{'med_dec':>8} "
|
|
477
|
+
f"{'med_dist':>10} "
|
|
438
478
|
f"{'med_wall':>10} "
|
|
439
479
|
f"{'min_dec':>8} "
|
|
440
480
|
f"{'max_dec':>8}"
|
|
441
481
|
)
|
|
442
|
-
print("-" *
|
|
482
|
+
print("-" * 110)
|
|
443
483
|
|
|
444
484
|
summary_all: dict[str, dict] = {}
|
|
445
485
|
for name in scenarios_to_run:
|
|
446
486
|
scenario = find_scenario(name)
|
|
487
|
+
label = name.split(":", 1)[-1]
|
|
447
488
|
if scenario is None:
|
|
448
|
-
print(f"{
|
|
489
|
+
print(f"{label:<45} NOT FOUND")
|
|
449
490
|
continue
|
|
450
491
|
runs_data, summary = median_run(scenario, args.runs)
|
|
451
|
-
summary_all[
|
|
492
|
+
summary_all[label] = {"runs": runs_data, "summary": summary}
|
|
452
493
|
|
|
453
494
|
if "skipped" in summary:
|
|
454
|
-
print(f"{
|
|
495
|
+
print(f"{label:<45} SKIPPED: {summary['skipped']}")
|
|
455
496
|
continue
|
|
456
497
|
|
|
457
498
|
print(
|
|
458
|
-
f"{
|
|
499
|
+
f"{label:<45} "
|
|
459
500
|
f"{summary['success_runs']:>9} "
|
|
460
501
|
f"{summary['median_decisions']:>8} "
|
|
502
|
+
f"{summary['median_distributions_seen']:>10} "
|
|
461
503
|
f"{summary['median_wall']:>10} "
|
|
462
504
|
f"{summary['min_decisions']:>8} "
|
|
463
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
|
}
|
|
@@ -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.
|
|
1534
|
-
#
|
|
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
|
|
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.
|
|
1534
|
-
#
|
|
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
|
|
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.
|
|
1426
|
-
#
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
350
|
-
expected_input["
|
|
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
|
|
371
|
+
def parse_index_routes(
|
|
368
372
|
scenario_name: str,
|
|
369
373
|
scenario: dict,
|
|
370
|
-
) -> list[
|
|
371
|
-
"""Read the ``
|
|
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``)
|
|
375
|
-
|
|
376
|
-
so :func:`nab_python.fetch.
|
|
377
|
-
|
|
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("
|
|
383
|
+
raw = scenario.get("index_routes", [])
|
|
380
384
|
if not isinstance(raw, list):
|
|
381
385
|
msg = (
|
|
382
|
-
f"{scenario_name}:
|
|
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[
|
|
390
|
+
out: list[IndexRoute] = []
|
|
387
391
|
for entry in raw:
|
|
388
392
|
if not isinstance(entry, dict):
|
|
389
393
|
msg = (
|
|
390
|
-
f"{scenario_name}:
|
|
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}:
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
#
|
|
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"
|
|
@@ -585,7 +583,7 @@ def process_scenario(
|
|
|
585
583
|
vcs_policy_str,
|
|
586
584
|
marker_environment,
|
|
587
585
|
indexes,
|
|
588
|
-
|
|
586
|
+
index_routes,
|
|
589
587
|
build_packages=sorted(build_policy_overrides),
|
|
590
588
|
resolution_strategy=resolution_strategy,
|
|
591
589
|
trust_unverified_sdist_deps=trust_unverified_sdist_deps,
|
|
@@ -620,7 +618,7 @@ def process_scenario(
|
|
|
620
618
|
constraints,
|
|
621
619
|
marker_environment=marker_environment or None,
|
|
622
620
|
indexes=indexes,
|
|
623
|
-
|
|
621
|
+
index_routes=index_routes or None,
|
|
624
622
|
build_policy_overrides=build_policy_overrides or None,
|
|
625
623
|
resolution_strategy=resolution_strategy,
|
|
626
624
|
trust_unverified_sdist_deps=trust_unverified_sdist_deps,
|
|
@@ -31,6 +31,9 @@ if TYPE_CHECKING:
|
|
|
31
31
|
|
|
32
32
|
from nab_index.httpx_async_transport import HttpxAsyncTransport
|
|
33
33
|
from nab_python._vendor.packaging.ranges import VersionRange
|
|
34
|
+
from nab_python._vendor.packaging.requirements import Requirement
|
|
35
|
+
from nab_python._vendor.packaging.utils import canonicalize_name
|
|
36
|
+
from nab_python.config import PackageOverride
|
|
34
37
|
from nab_python.fetch import FetchCoordinator
|
|
35
38
|
from nab_python.provider import (
|
|
36
39
|
BuildPolicy,
|
|
@@ -78,19 +81,27 @@ def _resolve_one( # noqa: PLR0913 - one kwarg per knob; bundling hides the surf
|
|
|
78
81
|
python_version: str,
|
|
79
82
|
marker_environment: dict[str, str] | None,
|
|
80
83
|
indexes: list,
|
|
81
|
-
|
|
84
|
+
index_routes: list | None,
|
|
82
85
|
uploaded_prior_to: datetime | None,
|
|
83
86
|
strategy: ResolutionStrategy,
|
|
84
87
|
direct_packages: frozenset[str],
|
|
85
88
|
extras_mode: ExtrasMode,
|
|
86
89
|
build_policy_overrides: Mapping[str, BuildPolicy] | None,
|
|
87
90
|
) -> dict:
|
|
91
|
+
package_overrides = tuple(
|
|
92
|
+
PackageOverride(
|
|
93
|
+
requirement=Requirement(name),
|
|
94
|
+
name=canonicalize_name(name),
|
|
95
|
+
version_range=VersionRange.full(),
|
|
96
|
+
build_policy=policy,
|
|
97
|
+
)
|
|
98
|
+
for name, policy in (build_policy_overrides or {}).items()
|
|
99
|
+
)
|
|
88
100
|
with FetchCoordinator(
|
|
89
101
|
HttpxAsyncTransport(),
|
|
90
102
|
indexes=indexes,
|
|
91
103
|
cache_dir=CACHE_DIR,
|
|
92
|
-
|
|
93
|
-
marker_environment=marker_environment,
|
|
104
|
+
index_routes=index_routes,
|
|
94
105
|
) as coordinator:
|
|
95
106
|
provider = Provider(
|
|
96
107
|
coordinator,
|
|
@@ -99,7 +110,7 @@ def _resolve_one( # noqa: PLR0913 - one kwarg per knob; bundling hides the surf
|
|
|
99
110
|
uploaded_prior_to=uploaded_prior_to,
|
|
100
111
|
dist_policy=DistPolicy.WHEEL_OR_SDIST,
|
|
101
112
|
build_policy=BuildPolicy.NEVER,
|
|
102
|
-
|
|
113
|
+
package_overrides=package_overrides,
|
|
103
114
|
marker_environment=marker_environment,
|
|
104
115
|
resolution_strategy=strategy,
|
|
105
116
|
direct_packages=direct_packages,
|
|
@@ -164,7 +175,7 @@ def _scenario_inputs(scenario: dict) -> dict | None:
|
|
|
164
175
|
constraint_strings: list[str] = scenario.get("constraints", [])
|
|
165
176
|
marker_env = _scenarios.parse_marker_environment("sweep", scenario)
|
|
166
177
|
indexes = _scenarios.parse_indexes("sweep", scenario)
|
|
167
|
-
|
|
178
|
+
index_routes = _scenarios.parse_index_routes("sweep", scenario)
|
|
168
179
|
datetime_str = scenario.get("datetime")
|
|
169
180
|
project_name = scenario.get("project_name")
|
|
170
181
|
project_extras = scenario.get("project_extras", [])
|
|
@@ -205,10 +216,7 @@ def _scenario_inputs(scenario: dict) -> dict | None:
|
|
|
205
216
|
)
|
|
206
217
|
build_policy_overrides = _scenarios.parse_build_packages("sweep", scenario)
|
|
207
218
|
if marker_env and build_policy_overrides:
|
|
208
|
-
#
|
|
209
|
-
# rejected at provider construction. Drop the overrides so the
|
|
210
|
-
# sweep keeps running; a scenario that now fails was relying on
|
|
211
|
-
# the prior silent passthrough.
|
|
219
|
+
# BUILD_REMOTE + marker_environment is rejected at provider construction.
|
|
212
220
|
build_policy_overrides = []
|
|
213
221
|
return {
|
|
214
222
|
"requirements": requirements,
|
|
@@ -216,7 +224,7 @@ def _scenario_inputs(scenario: dict) -> dict | None:
|
|
|
216
224
|
"python_version": python_version,
|
|
217
225
|
"marker_environment": marker_env or None,
|
|
218
226
|
"indexes": indexes,
|
|
219
|
-
"
|
|
227
|
+
"index_routes": index_routes or None,
|
|
220
228
|
"uploaded_prior_to": uploaded_prior_to,
|
|
221
229
|
"direct_packages": _direct_set(requirements),
|
|
222
230
|
"build_policy_overrides": build_policy_overrides or None,
|