diff --git a/.github/workflows/sync-python-releases.yml b/.github/workflows/sync-python-releases.yml index 8baab9c2b..9535ce64e 100644 --- a/.github/workflows/sync-python-releases.yml +++ b/.github/workflows/sync-python-releases.yml @@ -37,6 +37,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: "Update Python version constants" + run: uv run scripts/sync-python-version-constants.py + - name: "Create Pull Request" uses: peter-evans/create-pull-request@98357b18bf14b5342f975ff684046ec3b2a07725 # v8.0.0 with: @@ -45,6 +48,7 @@ jobs: crates/uv-python/download-metadata.json crates/uv-dev/src/generate_sysconfig_mappings.rs crates/uv-python/src/sysconfig/generated_mappings.rs + crates/uv/tests/it/common/mod.rs branch: "sync-python-releases" title: "Sync latest Python releases" body: "Automated update for Python releases." diff --git a/crates/uv/tests/it/common/mod.rs b/crates/uv/tests/it/common/mod.rs index 242ead7b4..e23452eb8 100644 --- a/crates/uv/tests/it/common/mod.rs +++ b/crates/uv/tests/it/common/mod.rs @@ -39,6 +39,14 @@ static EXCLUDE_NEWER: &str = "2024-03-25T00:00:00Z"; pub const PACKSE_VERSION: &str = "0.3.53"; pub const DEFAULT_PYTHON_VERSION: &str = "3.12"; +// The expected latest patch version for each Python minor version. +pub const LATEST_PYTHON_3_15: &str = "3.15.0a3"; +pub const LATEST_PYTHON_3_14: &str = "3.14.2"; +pub const LATEST_PYTHON_3_13: &str = "3.13.11"; +pub const LATEST_PYTHON_3_12: &str = "3.12.12"; +pub const LATEST_PYTHON_3_11: &str = "3.11.14"; +pub const LATEST_PYTHON_3_10: &str = "3.10.19"; + /// Using a find links url allows using `--index-url` instead of `--extra-index-url` in tests /// to prevent dependency confusion attacks against our test suite. pub fn build_vendor_links_url() -> String { @@ -468,6 +476,26 @@ impl TestContext { self } + /// Adds a filter that replaces the latest Python patch versions with `[LATEST]` placeholder. + pub fn with_filtered_latest_python_versions(mut self) -> Self { + // Filter the latest patch versions with [LATEST] placeholder + // The order matters - we want to match the full version first + for (minor, patch) in [ + ("3.15", LATEST_PYTHON_3_15.strip_prefix("3.15.").unwrap()), + ("3.14", LATEST_PYTHON_3_14.strip_prefix("3.14.").unwrap()), + ("3.13", LATEST_PYTHON_3_13.strip_prefix("3.13.").unwrap()), + ("3.12", LATEST_PYTHON_3_12.strip_prefix("3.12.").unwrap()), + ("3.11", LATEST_PYTHON_3_11.strip_prefix("3.11.").unwrap()), + ("3.10", LATEST_PYTHON_3_10.strip_prefix("3.10.").unwrap()), + ] { + // Match the full version in various contexts (cpython-X.Y.Z, Python X.Y.Z, etc.) + let pattern = format!(r"(\b){minor}\.{patch}(\b)"); + let replacement = format!("${{1}}{minor}.[LATEST]${{2}}"); + self.filters.push((pattern, replacement)); + } + self + } + /// Add a filter that ignores temporary directory in path. pub fn with_filtered_windows_temp_dir(mut self) -> Self { let pattern = regex::escape( diff --git a/crates/uv/tests/it/python_install.rs b/crates/uv/tests/it/python_install.rs index 80efaa995..599d23e5f 100644 --- a/crates/uv/tests/it/python_install.rs +++ b/crates/uv/tests/it/python_install.rs @@ -23,6 +23,7 @@ fn python_install() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_empty_python_install_mirror() .with_python_download_cache(); @@ -34,8 +35,8 @@ fn python_install() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python3.14) "); let bin_python = context @@ -55,7 +56,7 @@ fn python_install() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/bin/python3.14" + read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14" ); }); } else if cfg!(windows) { @@ -63,7 +64,7 @@ fn python_install() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/python" + read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python" ); }); } @@ -106,8 +107,8 @@ fn python_install() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - ~ cpython-3.14.2-[PLATFORM] (python3.14) + Installed Python 3.14.[LATEST] in [TIME] + ~ cpython-3.14.[LATEST]-[PLATFORM] (python3.14) "); // The executable should still be present in the bin directory @@ -135,8 +136,8 @@ fn python_install() { ----- stderr ----- Searching for Python versions matching: Python 3.14 - Uninstalled Python 3.14.2 in [TIME] - - cpython-3.14.2-[PLATFORM] (python3.14) + Uninstalled Python 3.14.[LATEST] in [TIME] + - cpython-3.14.[LATEST]-[PLATFORM] (python3.14) "); // The executable should be removed @@ -148,6 +149,7 @@ fn python_reinstall() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_python_download_cache(); @@ -159,8 +161,8 @@ fn python_reinstall() { ----- stderr ----- Installed 2 versions in [TIME] - + cpython-3.12.12-[PLATFORM] (python3.12) - + cpython-3.13.11-[PLATFORM] (python3.13) + + cpython-3.12.[LATEST]-[PLATFORM] (python3.12) + + cpython-3.13.[LATEST]-[PLATFORM] (python3.13) "); // Reinstall a single version @@ -170,8 +172,8 @@ fn python_reinstall() { ----- stdout ----- ----- stderr ----- - Installed Python 3.13.11 in [TIME] - ~ cpython-3.13.11-[PLATFORM] (python3.13) + Installed Python 3.13.[LATEST] in [TIME] + ~ cpython-3.13.[LATEST]-[PLATFORM] (python3.13) "); // Reinstall multiple versions @@ -182,8 +184,8 @@ fn python_reinstall() { ----- stderr ----- Installed 2 versions in [TIME] - ~ cpython-3.12.12-[PLATFORM] (python3.12) - ~ cpython-3.13.11-[PLATFORM] (python3.13) + ~ cpython-3.12.[LATEST]-[PLATFORM] (python3.12) + ~ cpython-3.13.[LATEST]-[PLATFORM] (python3.13) "); // Reinstalling a version that is not installed should also work @@ -193,8 +195,8 @@ fn python_reinstall() { ----- stdout ----- ----- stderr ----- - Installed Python 3.11.14 in [TIME] - + cpython-3.11.14-[PLATFORM] (python3.11) + Installed Python 3.11.[LATEST] in [TIME] + + cpython-3.11.[LATEST]-[PLATFORM] (python3.11) "); } @@ -383,6 +385,7 @@ fn python_install_force() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs(); // Install the latest version @@ -392,8 +395,8 @@ fn python_install_force() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python3.14) "); let bin_python = context @@ -407,8 +410,8 @@ fn python_install_force() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python3.14) "); // The executable should still be present in the bin directory @@ -424,7 +427,7 @@ fn python_install_force() { ----- stdout ----- ----- stderr ----- - warning: Failed to install executable for cpython-3.14.2-[PLATFORM] + warning: Failed to install executable for cpython-3.14.[LATEST]-[PLATFORM] Caused by: Executable already exists at `[BIN]/python3.14` but is not managed by uv; use `--force` to replace it "); @@ -434,8 +437,8 @@ fn python_install_force() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python3.14) "); bin_python.assert(predicate::path::exists()); @@ -446,6 +449,7 @@ fn python_install_minor() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs(); // Install a minor version @@ -455,8 +459,8 @@ fn python_install_minor() { ----- stdout ----- ----- stderr ----- - Installed Python 3.11.14 in [TIME] - + cpython-3.11.14-[PLATFORM] (python3.11) + Installed Python 3.11.[LATEST] in [TIME] + + cpython-3.11.[LATEST]-[PLATFORM] (python3.11) "); let bin_python = context @@ -472,7 +476,7 @@ fn python_install_minor() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.11.14-[PLATFORM]/bin/python3.11" + read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.11.[LATEST]-[PLATFORM]/bin/python3.11" ); }); } else if cfg!(windows) { @@ -480,7 +484,7 @@ fn python_install_minor() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.11.14-[PLATFORM]/python" + read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.11.[LATEST]-[PLATFORM]/python" ); }); } @@ -492,8 +496,8 @@ fn python_install_minor() { ----- stderr ----- Searching for Python versions matching: Python 3.11 - Uninstalled Python 3.11.14 in [TIME] - - cpython-3.11.14-[PLATFORM] (python3.11) + Uninstalled Python 3.11.[LATEST] in [TIME] + - cpython-3.11.[LATEST]-[PLATFORM] (python3.11) "); // The executable should be removed @@ -585,6 +589,7 @@ fn python_install_preview() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_python_download_cache(); @@ -595,8 +600,8 @@ fn python_install_preview() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); let bin_python = context @@ -657,8 +662,8 @@ fn python_install_preview() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - ~ cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Installed Python 3.14.[LATEST] in [TIME] + ~ cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); // The executable should still be present in the bin directory @@ -671,8 +676,8 @@ fn python_install_preview() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); // The executable should still be present in the bin directory @@ -688,7 +693,7 @@ fn python_install_preview() { ----- stdout ----- ----- stderr ----- - warning: Failed to install executable for cpython-3.14.2-[PLATFORM] + warning: Failed to install executable for cpython-3.14.[LATEST]-[PLATFORM] Caused by: Executable already exists at `[BIN]/python3.14` but is not managed by uv; use `--force` to replace it "); @@ -699,7 +704,7 @@ fn python_install_preview() { ----- stdout ----- ----- stderr ----- - error: Failed to install executable for cpython-3.14.2-[PLATFORM] + error: Failed to install executable for cpython-3.14.[LATEST]-[PLATFORM] Caused by: Executable already exists at `[BIN]/python3.14` but is not managed by uv; use `--force` to replace it "); uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.14").env(EnvVars::UV_PYTHON_INSTALL_BIN, "1"), @r" @@ -708,7 +713,7 @@ fn python_install_preview() { ----- stdout ----- ----- stderr ----- - error: Failed to install executable for cpython-3.14.2-[PLATFORM] + error: Failed to install executable for cpython-3.14.[LATEST]-[PLATFORM] Caused by: Executable already exists at `[BIN]/python3.14` but is not managed by uv; use `--force` to replace it "); @@ -736,8 +741,8 @@ fn python_install_preview() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python3.14) "); bin_python.assert(predicate::path::exists()); @@ -768,8 +773,8 @@ fn python_install_preview() { ----- stderr ----- Searching for Python versions matching: Python 3.14 - Uninstalled Python 3.14.2 in [TIME] - - cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Uninstalled Python 3.14.[LATEST] in [TIME] + - cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); // The executable should be removed @@ -782,8 +787,8 @@ fn python_install_preview() { ----- stdout ----- ----- stderr ----- - Installed Python 3.11.14 in [TIME] - + cpython-3.11.14-[PLATFORM] (python3.11) + Installed Python 3.11.[LATEST] in [TIME] + + cpython-3.11.[LATEST]-[PLATFORM] (python3.11) "); let bin_python = context @@ -816,8 +821,8 @@ fn python_install_preview() { ----- stderr ----- Searching for Python versions matching: Python 3.11 - Uninstalled Python 3.11.14 in [TIME] - - cpython-3.11.14-[PLATFORM] (python3.11) + Uninstalled Python 3.11.[LATEST] in [TIME] + - cpython-3.11.[LATEST]-[PLATFORM] (python3.11) "); // Install multiple patch versions @@ -861,6 +866,7 @@ fn python_install_preview_no_bin() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_python_download_cache(); @@ -871,8 +877,8 @@ fn python_install_preview_no_bin() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] "); let bin_python = context @@ -1066,6 +1072,7 @@ fn python_install_preview_upgrade() { fn python_install_freethreaded() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_python_download_cache() .with_filtered_python_install_bin() @@ -1079,8 +1086,8 @@ fn python_install_freethreaded() { ----- stdout ----- ----- stderr ----- - Installed Python 3.13.11 in [TIME] - + cpython-3.13.11+freethreaded-[PLATFORM] (python3.13t) + Installed Python 3.13.[LATEST] in [TIME] + + cpython-3.13.[LATEST]+freethreaded-[PLATFORM] (python3.13t) "); let bin_python = context @@ -1132,7 +1139,7 @@ fn python_install_freethreaded() { ----- stdout ----- ----- stderr ----- - Using CPython 3.13.11+freethreaded + Using CPython 3.13.[LATEST]+freethreaded Creating virtual environment at: .venv Activate with: source .venv/[BIN]/activate "); @@ -1191,8 +1198,8 @@ fn python_install_freethreaded() { ----- stdout ----- ----- stderr ----- - Installed Python 3.13.11 in [TIME] - + cpython-3.13.11-[PLATFORM] (python3.13) + Installed Python 3.13.[LATEST] in [TIME] + + cpython-3.13.[LATEST]-[PLATFORM] (python3.13) "); // Should not work with older Python versions @@ -1213,8 +1220,8 @@ fn python_install_freethreaded() { ----- stderr ----- Searching for Python installations Uninstalled 2 versions in [TIME] - - cpython-3.13.11+freethreaded-[PLATFORM] (python3.13t) - - cpython-3.13.11-[PLATFORM] (python3.13) + - cpython-3.13.[LATEST]+freethreaded-[PLATFORM] (python3.13t) + - cpython-3.13.[LATEST]-[PLATFORM] (python3.13) "); } @@ -1253,6 +1260,7 @@ fn python_install_debug() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs(); // Install the latest version @@ -1262,8 +1270,8 @@ fn python_install_debug() { ----- stdout ----- ----- stderr ----- - Installed Python 3.13.11 in [TIME] - + cpython-3.13.11+debug-[PLATFORM] (python3.13d) + Installed Python 3.13.[LATEST] in [TIME] + + cpython-3.13.[LATEST]+debug-[PLATFORM] (python3.13d) "); let bin_python = context @@ -1315,8 +1323,8 @@ fn python_install_debug() { ----- stdout ----- ----- stderr ----- - Installed Python 3.13.11 in [TIME] - + cpython-3.13.11-[PLATFORM] (python3.13) + Installed Python 3.13.[LATEST] in [TIME] + + cpython-3.13.[LATEST]-[PLATFORM] (python3.13) "); // Now we should prefer the non-debug version without opt-in @@ -1324,7 +1332,7 @@ fn python_install_debug() { success: true exit_code: 0 ----- stdout ----- - [TEMP_DIR]/managed/cpython-3.13.11-[PLATFORM]/bin/python3.13 + [TEMP_DIR]/managed/cpython-3.13.[LATEST]-[PLATFORM]/bin/python3.13 ----- stderr ----- "); @@ -1356,8 +1364,8 @@ fn python_install_debug() { ----- stdout ----- ----- stderr ----- - Installed Python 3.12.12 in [TIME] - + cpython-3.12.12+debug-[PLATFORM] (python3.12d) + Installed Python 3.12.[LATEST] in [TIME] + + cpython-3.12.[LATEST]+debug-[PLATFORM] (python3.12d) "); uv_snapshot!(context.filters(), context.python_uninstall().arg("--all"), @r" @@ -1368,9 +1376,9 @@ fn python_install_debug() { ----- stderr ----- Searching for Python installations Uninstalled 3 versions in [TIME] - - cpython-3.12.12+debug-[PLATFORM] (python3.12d) - - cpython-3.13.11+debug-[PLATFORM] (python3.13d) - - cpython-3.13.11-[PLATFORM] (python3.13) + - cpython-3.12.[LATEST]+debug-[PLATFORM] (python3.12d) + - cpython-3.13.[LATEST]+debug-[PLATFORM] (python3.13d) + - cpython-3.13.[LATEST]-[PLATFORM] (python3.13) "); } @@ -1381,6 +1389,7 @@ fn python_install_debug_freethreaded() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_python_download_cache(); @@ -1391,8 +1400,8 @@ fn python_install_debug_freethreaded() { ----- stdout ----- ----- stderr ----- - Installed Python 3.13.11 in [TIME] - + cpython-3.13.11+freethreaded+debug-[PLATFORM] (python3.13td) + Installed Python 3.13.[LATEST] in [TIME] + + cpython-3.13.[LATEST]+freethreaded+debug-[PLATFORM] (python3.13td) "); let bin_python = context @@ -1455,8 +1464,8 @@ fn python_install_debug_freethreaded() { ----- stdout ----- ----- stderr ----- - Installed Python 3.13.11 in [TIME] - + cpython-3.13.11-[PLATFORM] (python3.13) + Installed Python 3.13.[LATEST] in [TIME] + + cpython-3.13.[LATEST]-[PLATFORM] (python3.13) "); // Should be distinct from 3.13t @@ -1466,8 +1475,8 @@ fn python_install_debug_freethreaded() { ----- stdout ----- ----- stderr ----- - Installed Python 3.13.11 in [TIME] - + cpython-3.13.11+freethreaded-[PLATFORM] (python3.13t) + Installed Python 3.13.[LATEST] in [TIME] + + cpython-3.13.[LATEST]+freethreaded-[PLATFORM] (python3.13t) "); // Should be distinct from 3.13d @@ -1477,8 +1486,8 @@ fn python_install_debug_freethreaded() { ----- stdout ----- ----- stderr ----- - Installed Python 3.13.11 in [TIME] - + cpython-3.13.11+debug-[PLATFORM] (python3.13d) + Installed Python 3.13.[LATEST] in [TIME] + + cpython-3.13.[LATEST]+debug-[PLATFORM] (python3.13d) "); // Now we should prefer the non-debug version without opt-in @@ -1486,7 +1495,7 @@ fn python_install_debug_freethreaded() { success: true exit_code: 0 ----- stdout ----- - [TEMP_DIR]/managed/cpython-3.13.11-[PLATFORM]/bin/python3.13 + [TEMP_DIR]/managed/cpython-3.13.[LATEST]-[PLATFORM]/bin/python3.13 ----- stderr ----- "); @@ -1495,7 +1504,7 @@ fn python_install_debug_freethreaded() { success: true exit_code: 0 ----- stdout ----- - [TEMP_DIR]/managed/cpython-3.13.11+freethreaded-[PLATFORM]/bin/python3.13t + [TEMP_DIR]/managed/cpython-3.13.[LATEST]+freethreaded-[PLATFORM]/bin/python3.13t ----- stderr ----- "); @@ -1518,10 +1527,10 @@ fn python_install_debug_freethreaded() { ----- stderr ----- Searching for Python installations Uninstalled 4 versions in [TIME] - - cpython-3.13.11+freethreaded+debug-[PLATFORM] (python3.13td) - - cpython-3.13.11+freethreaded-[PLATFORM] (python3.13t) - - cpython-3.13.11+debug-[PLATFORM] (python3.13d) - - cpython-3.13.11-[PLATFORM] (python3.13) + - cpython-3.13.[LATEST]+freethreaded+debug-[PLATFORM] (python3.13td) + - cpython-3.13.[LATEST]+freethreaded-[PLATFORM] (python3.13t) + - cpython-3.13.[LATEST]+debug-[PLATFORM] (python3.13d) + - cpython-3.13.[LATEST]-[PLATFORM] (python3.13) "); } @@ -1569,6 +1578,7 @@ fn python_install_default() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_python_download_cache(); @@ -1591,8 +1601,8 @@ fn python_install_default() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python3.14) "); // Only the minor versioned executable should be installed @@ -1608,8 +1618,8 @@ fn python_install_default() { ----- stderr ----- warning: The `--default` option is experimental and may change without warning. Pass `--preview-features python-install-default` to disable this warning - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python, python3) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python, python3) "); // Now all the executables should be installed @@ -1625,8 +1635,8 @@ fn python_install_default() { ----- stderr ----- Searching for Python installations - Uninstalled Python 3.14.2 in [TIME] - - cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Uninstalled Python 3.14.[LATEST] in [TIME] + - cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); // The executables should be removed @@ -1642,8 +1652,8 @@ fn python_install_default() { ----- stderr ----- warning: The `--default` option is experimental and may change without warning. Pass `--preview-features python-install-default` to disable this warning - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); // Since it's a default install, we should include all of the executables @@ -1657,7 +1667,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/bin/python3.14" + read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14" ); }); @@ -1665,7 +1675,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/bin/python3.14" + read_link(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14" ); }); @@ -1673,7 +1683,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/bin/python3.14" + read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14" ); }); } else if cfg!(windows) { @@ -1681,7 +1691,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/python" + read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python" ); }); @@ -1689,7 +1699,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/python" + read_link(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python" ); }); @@ -1697,7 +1707,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/python" + read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python" ); }); } @@ -1710,8 +1720,8 @@ fn python_install_default() { ----- stderr ----- Searching for Python versions matching: Python 3.14 - Uninstalled Python 3.14.2 in [TIME] - - cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Uninstalled Python 3.14.[LATEST] in [TIME] + - cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); // We should remove all the executables @@ -1738,8 +1748,8 @@ fn python_install_default() { ----- stderr ----- warning: The `--default` option is experimental and may change without warning. Pass `--preview-features python-install-default` to disable this warning - Installed Python 3.12.12 in [TIME] - + cpython-3.12.12-[PLATFORM] (python, python3, python3.12) + Installed Python 3.12.[LATEST] in [TIME] + + cpython-3.12.[LATEST]-[PLATFORM] (python, python3, python3.12) "); let bin_python_minor_12 = context @@ -1757,7 +1767,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/bin/python3.12" + read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/bin/python3.12" ); }); @@ -1765,7 +1775,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/bin/python3.12" + read_link(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/bin/python3.12" ); }); @@ -1773,7 +1783,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/bin/python3.12" + read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/bin/python3.12" ); }); } else { @@ -1781,7 +1791,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/python" + read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/python" ); }); @@ -1789,7 +1799,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/python" + read_link(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/python" ); }); @@ -1797,7 +1807,7 @@ fn python_install_default() { filters => context.filters(), }, { insta::assert_snapshot!( - read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/python" + read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/python" ); }); } @@ -1808,6 +1818,7 @@ fn python_install_default_preview() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_python_download_cache(); @@ -1830,8 +1841,8 @@ fn python_install_default_preview() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python3.14) "); // Only the minor versioned executable should be installed @@ -1846,8 +1857,8 @@ fn python_install_default_preview() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python, python3) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python, python3) "); // Now all the executables should be installed @@ -1863,8 +1874,8 @@ fn python_install_default_preview() { ----- stderr ----- Searching for Python installations - Uninstalled Python 3.14.2 in [TIME] - - cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Uninstalled Python 3.14.[LATEST] in [TIME] + - cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); // The executables should be removed @@ -1879,8 +1890,8 @@ fn python_install_default_preview() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); // Since it's a default install, we should include all of the executables @@ -1897,7 +1908,7 @@ fn python_install_default_preview() { read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/bin/python3.14" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/bin/python3.14" + canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14" ); }); @@ -1908,7 +1919,7 @@ fn python_install_default_preview() { read_link(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/bin/python3.14" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/bin/python3.14" + canonicalize_link_path(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14" ); }); @@ -1919,7 +1930,7 @@ fn python_install_default_preview() { read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/bin/python3.14" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/bin/python3.14" + canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14" ); }); } else if cfg!(windows) { @@ -1930,7 +1941,7 @@ fn python_install_default_preview() { read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/python" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/python" + canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python" ); }); @@ -1941,7 +1952,7 @@ fn python_install_default_preview() { read_link(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/python" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/python" + canonicalize_link_path(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python" ); }); @@ -1952,7 +1963,7 @@ fn python_install_default_preview() { read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/python" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/python" + canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python" ); }); } @@ -1965,8 +1976,8 @@ fn python_install_default_preview() { ----- stderr ----- Searching for Python versions matching: Python 3.14 - Uninstalled Python 3.14.2 in [TIME] - - cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Uninstalled Python 3.14.[LATEST] in [TIME] + - cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); // We should remove all the executables @@ -1991,8 +2002,8 @@ fn python_install_default_preview() { ----- stdout ----- ----- stderr ----- - Installed Python 3.12.12 in [TIME] - + cpython-3.12.12-[PLATFORM] (python, python3, python3.12) + Installed Python 3.12.[LATEST] in [TIME] + + cpython-3.12.[LATEST]-[PLATFORM] (python, python3, python3.12) "); let bin_python_minor_12 = context @@ -2013,7 +2024,7 @@ fn python_install_default_preview() { read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/bin/python3.12" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/bin/python3.12" + canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/bin/python3.12" ); }); @@ -2024,7 +2035,7 @@ fn python_install_default_preview() { read_link(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/bin/python3.12" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/bin/python3.12" + canonicalize_link_path(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/bin/python3.12" ); }); @@ -2035,7 +2046,7 @@ fn python_install_default_preview() { read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/bin/python3.12" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/bin/python3.12" + canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/bin/python3.12" ); }); } else { @@ -2046,7 +2057,7 @@ fn python_install_default_preview() { read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/python" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/python" + canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/python" ); }); @@ -2057,7 +2068,7 @@ fn python_install_default_preview() { read_link(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/python" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/python" + canonicalize_link_path(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/python" ); }); @@ -2068,7 +2079,7 @@ fn python_install_default_preview() { read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/python" ); insta::assert_snapshot!( - canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/python" + canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/python" ); }); } @@ -2080,8 +2091,8 @@ fn python_install_default_preview() { ----- stdout ----- ----- stderr ----- - Installed Python 3.14.2 in [TIME] - + cpython-3.14.2-[PLATFORM] (python, python3, python3.14) + Installed Python 3.14.[LATEST] in [TIME] + + cpython-3.14.[LATEST]-[PLATFORM] (python, python3, python3.14) "); // All the executables should exist @@ -2096,7 +2107,7 @@ fn python_install_default_preview() { filters => context.filters(), }, { insta::assert_snapshot!( - canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/bin/python3.14" + canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14" ); }); @@ -2104,7 +2115,7 @@ fn python_install_default_preview() { filters => context.filters(), }, { insta::assert_snapshot!( - canonicalize_link_path(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/bin/python3.14" + canonicalize_link_path(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14" ); }); @@ -2112,7 +2123,7 @@ fn python_install_default_preview() { filters => context.filters(), }, { insta::assert_snapshot!( - canonicalize_link_path(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/bin/python3.12" + canonicalize_link_path(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/bin/python3.12" ); }); @@ -2120,7 +2131,7 @@ fn python_install_default_preview() { filters => context.filters(), }, { insta::assert_snapshot!( - canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/bin/python3.14" + canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14" ); }); } else if cfg!(windows) { @@ -2128,7 +2139,7 @@ fn python_install_default_preview() { filters => context.filters(), }, { insta::assert_snapshot!( - canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/python" + canonicalize_link_path(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python" ); }); @@ -2136,7 +2147,7 @@ fn python_install_default_preview() { filters => context.filters(), }, { insta::assert_snapshot!( - canonicalize_link_path(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/python" + canonicalize_link_path(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python" ); }); @@ -2144,7 +2155,7 @@ fn python_install_default_preview() { filters => context.filters(), }, { insta::assert_snapshot!( - canonicalize_link_path(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.12-[PLATFORM]/python" + canonicalize_link_path(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/python" ); }); @@ -2152,7 +2163,7 @@ fn python_install_default_preview() { filters => context.filters(), }, { insta::assert_snapshot!( - canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.2-[PLATFORM]/python" + canonicalize_link_path(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python" ); }); } @@ -2302,6 +2313,7 @@ fn python_install_default_from_env() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() .with_filtered_exe_suffix() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_python_download_cache(); @@ -2312,8 +2324,8 @@ fn python_install_default_from_env() { ----- stdout ----- ----- stderr ----- - Installed Python 3.12.12 in [TIME] - + cpython-3.12.12-[PLATFORM] (python3.12) + Installed Python 3.12.[LATEST] in [TIME] + + cpython-3.12.[LATEST]-[PLATFORM] (python3.12) "); // But prefer explicit requests @@ -2323,8 +2335,8 @@ fn python_install_default_from_env() { ----- stdout ----- ----- stderr ----- - Installed Python 3.11.14 in [TIME] - + cpython-3.11.14-[PLATFORM] (python3.11) + Installed Python 3.11.[LATEST] in [TIME] + + cpython-3.11.[LATEST]-[PLATFORM] (python3.11) "); // We should ignore `UV_PYTHON` here and complain there is not a target @@ -2351,8 +2363,8 @@ fn python_install_default_from_env() { ----- stderr ----- Searching for Python installations Uninstalled 2 versions in [TIME] - - cpython-3.11.14-[PLATFORM] (python3.11) - - cpython-3.12.12-[PLATFORM] (python3.12) + - cpython-3.11.[LATEST]-[PLATFORM] (python3.11) + - cpython-3.12.[LATEST]-[PLATFORM] (python3.12) "); // Uninstall with no targets should error @@ -2436,6 +2448,7 @@ fn python_install_patch_dylib() { fn python_install_prerelease() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_python_download_cache() .with_filtered_python_install_bin() @@ -2449,8 +2462,8 @@ fn python_install_prerelease() { ----- stdout ----- ----- stderr ----- - Installed Python 3.15.0a3 in [TIME] - + cpython-3.15.0a3-[PLATFORM] (python3.15) + Installed Python 3.15.[LATEST] in [TIME] + + cpython-3.15.[LATEST]-[PLATFORM] (python3.15) "); // Install a specific pre-release @@ -2469,6 +2482,7 @@ fn python_install_prerelease() { fn python_find_prerelease() { let context: TestContext = TestContext::new_with_versions(&[]) .with_filtered_python_keys() + .with_filtered_latest_python_versions() .with_managed_python_dirs() .with_python_download_cache() .with_filtered_python_install_bin() @@ -2485,7 +2499,7 @@ fn python_find_prerelease() { success: true exit_code: 0 ----- stdout ----- - [TEMP_DIR]/managed/cpython-3.15.0a3-[PLATFORM]/[INSTALL-BIN]/[PYTHON] + [TEMP_DIR]/managed/cpython-3.15.[LATEST]-[PLATFORM]/[INSTALL-BIN]/[PYTHON] ----- stderr ----- "); @@ -2495,7 +2509,7 @@ fn python_find_prerelease() { success: true exit_code: 0 ----- stdout ----- - [TEMP_DIR]/managed/cpython-3.15.0a3-[PLATFORM]/[INSTALL-BIN]/[PYTHON] + [TEMP_DIR]/managed/cpython-3.15.[LATEST]-[PLATFORM]/[INSTALL-BIN]/[PYTHON] ----- stderr ----- "); @@ -2504,7 +2518,7 @@ fn python_find_prerelease() { success: true exit_code: 0 ----- stdout ----- - [TEMP_DIR]/managed/cpython-3.15.0a3-[PLATFORM]/[INSTALL-BIN]/[PYTHON] + [TEMP_DIR]/managed/cpython-3.15.[LATEST]-[PLATFORM]/[INSTALL-BIN]/[PYTHON] ----- stderr ----- "); @@ -2516,15 +2530,15 @@ fn python_find_prerelease() { ----- stdout ----- ----- stderr ----- - Installed Python 3.13.11 in [TIME] - + cpython-3.13.11-[PLATFORM] (python3.13) + Installed Python 3.13.[LATEST] in [TIME] + + cpython-3.13.[LATEST]-[PLATFORM] (python3.13) "); uv_snapshot!(context.filters(), context.python_find().arg("3"), @r" success: true exit_code: 0 ----- stdout ----- - [TEMP_DIR]/managed/cpython-3.13.11-[PLATFORM]/[INSTALL-BIN]/[PYTHON] + [TEMP_DIR]/managed/cpython-3.13.[LATEST]-[PLATFORM]/[INSTALL-BIN]/[PYTHON] ----- stderr ----- "); diff --git a/scripts/sync-python-version-constants.py b/scripts/sync-python-version-constants.py new file mode 100644 index 000000000..b84e46045 --- /dev/null +++ b/scripts/sync-python-version-constants.py @@ -0,0 +1,113 @@ +"""Update the Python version constants in the test common module. + +This script reads the download-metadata.json file and extracts the latest +patch version for each minor version (3.15, 3.14, 3.13, 3.12, 3.11, 3.10). +It then updates the LATEST_PYTHON_X_Y constants in crates/uv/tests/it/common/mod.rs. + +For minor versions with stable releases, it uses the latest stable version. +For minor versions with only prereleases, it uses the latest prerelease. + +This is called by the sync-python-releases workflow to keep the test constants +in sync with the latest available Python versions. +""" + +# /// script +# requires-python = ">=3.12" +# dependencies = ["packaging"] +# /// + +from __future__ import annotations + +import json +import re +from pathlib import Path + +from packaging.version import Version + +SELF_DIR = Path(__file__).parent +ROOT = SELF_DIR.parent + + +def main() -> None: + # Read the download metadata + metadata_path = ROOT / "crates" / "uv-python" / "download-metadata.json" + with open(metadata_path) as f: + metadata = json.load(f) + + # Collect all versions per minor, separating stable and prerelease + stable_versions: dict[str, str] = {} + prerelease_versions: dict[str, str] = {} + + for info in metadata.values(): + if info["name"] != "cpython": + continue + if info.get("variant"): + continue + + minor = f"3.{info['minor']}" + prerelease = info.get("prerelease", "") + + version = f"{info['major']}.{info['minor']}.{info['patch']}" + if prerelease: + version += prerelease + + if prerelease: + if minor not in prerelease_versions or Version(version) > Version( + prerelease_versions[minor] + ): + prerelease_versions[minor] = version + else: + if minor not in stable_versions or Version(version) > Version( + stable_versions[minor] + ): + stable_versions[minor] = version + + # Use stable if available, otherwise prerelease + latest_versions: dict[str, str] = {} + for minor in stable_versions: + latest_versions[minor] = stable_versions[minor] + for minor in prerelease_versions: + if minor not in latest_versions: + latest_versions[minor] = prerelease_versions[minor] + + # Update the constants in common/mod.rs + mod_path = ROOT / "crates" / "uv" / "tests" / "it" / "common" / "mod.rs" + content = mod_path.read_text() + + # Extract old values first + old_versions: dict[str, str] = {} + for minor in ["3.15", "3.14", "3.13", "3.12", "3.11", "3.10"]: + const_name = f"LATEST_PYTHON_{minor.replace('.', '_')}" + match = re.search(rf'pub const {const_name}: &str = "([^"]+)";', content) + if match: + old_versions[minor] = match.group(1) + + for minor in ["3.15", "3.14", "3.13", "3.12", "3.11", "3.10"]: + if minor not in latest_versions: + continue + const_name = f"LATEST_PYTHON_{minor.replace('.', '_')}" + old_pattern = rf'pub const {const_name}: &str = "[^"]+";' + new_value = f'pub const {const_name}: &str = "{latest_versions[minor]}";' + content = re.sub(old_pattern, new_value, content) + + mod_path.write_text(content) + + updates = [] + for minor in ["3.15", "3.14", "3.13", "3.12", "3.11", "3.10"]: + if minor not in latest_versions: + continue + new_version = latest_versions[minor] + old_version = old_versions.get(minor) + if old_version != new_version: + updates.append(f" {old_version} -> {new_version}") + + if updates: + print("Updated Python version constants:") + for update in updates: + print(update) + else: + print("Python version constants are up to date") + + +if __name__ == "__main__": + main()