diff --git a/scripts/populate_tox/README.md b/scripts/populate_tox/README.md index d6c4e52147..e483ed78cb 100644 --- a/scripts/populate_tox/README.md +++ b/scripts/populate_tox/README.md @@ -7,6 +7,20 @@ sure we support everything we claim to. This `populate_tox.py` script is responsible for picking reasonable versions to test automatically and generating parts of `tox.ini` to capture this. +## Running the script + +You require a free-threaded interpreter with pip installed to run the script. With +a recent version of `uv` you can directly run the script with the following +command: + +``` +uv run --python 3.14t \ + --with pip \ + --with-requirements scripts/populate_tox/requirements.txt \ + --with-editable . \ + python scripts/populate_tox/populate_tox.py +``` + ## How it works There is a template in this directory called `tox.jinja` which contains a diff --git a/scripts/populate_tox/populate_tox.py b/scripts/populate_tox/populate_tox.py index 2d81a85ea2..df8495e58f 100644 --- a/scripts/populate_tox/populate_tox.py +++ b/scripts/populate_tox/populate_tox.py @@ -890,7 +890,31 @@ def _normalize_package_dependencies(package_dependencies: list[dict]) -> list[di def _exit_if_not_free_threaded_interpreter(): if "free-threading build" not in sys.version: - raise Exception("Running with a free-threaded interpreter is required.") + exc = Exception("Running with a free-threaded interpreter is required.") + exc.add_note( + "A dry run of pip is used to determine free-threading support of packages." + ) + raise exc + + +def _exit_if_pip_unavailable(): + pip_help_return_code = subprocess.run( + [ + sys.executable, + "-m", + "pip", + "--help", + ], + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ).returncode + + if pip_help_return_code != 0: + exc = Exception("pip must be available.") + exc.add_note( + "A dry run of pip is used to determine free-threading support of packages." + ) + raise exc def main() -> dict[str, list]: @@ -900,6 +924,7 @@ def main() -> dict[str, list]: global MIN_PYTHON_VERSION, MAX_PYTHON_VERSION _exit_if_not_free_threaded_interpreter() + _exit_if_pip_unavailable() meta = _fetch_sdk_metadata() sdk_python_versions = _parse_python_versions_from_classifiers(