mirror of
https://github.com/astral-sh/uv.git
synced 2026-05-06 08:56:53 -04:00
Stabilize Python upgrades (#17766)
Includes a few things... - Drops preview warnings for use of `uv python upgrade` and `uv python install --upgrade` - Adds `--resolve-links` to `uv python find`, which I needed in test cases to retain existing snapshots - Fixes issues in our "Using environment ..." messages on Windows which were incorrect - Refactors `from_executable` for the `PythonMinorVersionLink` type (https://github.com/astral-sh/uv/pull/17842/commits/28b2ed2525327d94fdf5372a29bbbc476d74680f) to use the type system to prevent incorrect construction (for above) - Removes special casing where we only upgrade links if they already exist, which existed so preview wasn't needed on every invocation - Fixes a bug with `PythonMinorVersionLink::exists` which returned `true` even if the link pointed to the wrong Python installation leading to discovery failures
This commit is contained in:
Generated
+1
-3
@@ -5920,7 +5920,6 @@ dependencies = [
|
||||
"uv-normalize",
|
||||
"uv-pep440",
|
||||
"uv-pep508",
|
||||
"uv-preview",
|
||||
"uv-pypi-types",
|
||||
"uv-python",
|
||||
"uv-static",
|
||||
@@ -6782,6 +6781,7 @@ dependencies = [
|
||||
"indoc",
|
||||
"insta",
|
||||
"itertools 0.14.0",
|
||||
"junction",
|
||||
"owo-colors",
|
||||
"ref-cast",
|
||||
"regex",
|
||||
@@ -7094,7 +7094,6 @@ dependencies = [
|
||||
"uv-normalize",
|
||||
"uv-pep440",
|
||||
"uv-pep508",
|
||||
"uv-preview",
|
||||
"uv-pypi-types",
|
||||
"uv-python",
|
||||
"uv-settings",
|
||||
@@ -7189,7 +7188,6 @@ dependencies = [
|
||||
"uv-console",
|
||||
"uv-fs",
|
||||
"uv-platform-tags",
|
||||
"uv-preview",
|
||||
"uv-pypi-types",
|
||||
"uv-python",
|
||||
"uv-shell",
|
||||
|
||||
@@ -25,7 +25,6 @@ uv-fs = { workspace = true }
|
||||
uv-normalize = { workspace = true }
|
||||
uv-pep440 = { workspace = true }
|
||||
uv-pep508 = { workspace = true }
|
||||
uv-preview = { workspace = true }
|
||||
uv-pypi-types = { workspace = true }
|
||||
uv-python = { workspace = true }
|
||||
uv-static = { workspace = true }
|
||||
|
||||
@@ -40,7 +40,6 @@ use uv_fs::{LockedFile, LockedFileMode};
|
||||
use uv_fs::{PythonExt, Simplified};
|
||||
use uv_normalize::PackageName;
|
||||
use uv_pep440::Version;
|
||||
use uv_preview::Preview;
|
||||
use uv_pypi_types::VerbatimParsedUrl;
|
||||
use uv_python::{Interpreter, PythonEnvironment};
|
||||
use uv_static::EnvVars;
|
||||
@@ -293,7 +292,6 @@ impl SourceBuild {
|
||||
level: BuildOutput,
|
||||
concurrent_builds: usize,
|
||||
credentials_cache: &CredentialsCache,
|
||||
preview: Preview,
|
||||
) -> Result<Self, Error> {
|
||||
let temp_dir = build_context.cache().venv_dir()?;
|
||||
|
||||
@@ -365,7 +363,6 @@ impl SourceBuild {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
preview,
|
||||
)?
|
||||
};
|
||||
|
||||
|
||||
@@ -6426,6 +6426,12 @@ pub struct PythonFindArgs {
|
||||
#[arg(long)]
|
||||
pub show_version: bool,
|
||||
|
||||
/// Resolve symlinks in the output path.
|
||||
///
|
||||
/// When enabled, the output path will be canonicalized, resolving any symlinks.
|
||||
#[arg(long)]
|
||||
pub resolve_links: bool,
|
||||
|
||||
/// URL pointing to JSON of custom Python installations.
|
||||
#[arg(long, value_hint = ValueHint::Other)]
|
||||
pub python_downloads_json_url: Option<String>,
|
||||
|
||||
@@ -26,8 +26,8 @@ uv-installer = { workspace = true }
|
||||
uv-macros = { workspace = true }
|
||||
uv-options-metadata = { workspace = true }
|
||||
uv-pep508 = { workspace = true }
|
||||
uv-preview = { workspace = true }
|
||||
uv-pypi-types = { workspace = true }
|
||||
uv-preview = { workspace = true }
|
||||
uv-python = { workspace = true }
|
||||
uv-settings = { workspace = true, features = ["schemars"] }
|
||||
uv-static = { workspace = true }
|
||||
|
||||
@@ -491,7 +491,6 @@ impl BuildContext for BuildDispatch<'_> {
|
||||
build_output,
|
||||
self.concurrency.builds,
|
||||
self.client.credentials_cache(),
|
||||
self.preview,
|
||||
)
|
||||
.boxed_local()
|
||||
.await?;
|
||||
|
||||
@@ -68,6 +68,7 @@ url = { workspace = true }
|
||||
which = { workspace = true }
|
||||
|
||||
[target.'cfg(target_os = "windows")'.dependencies]
|
||||
junction = { workspace = true }
|
||||
windows-registry = { workspace = true }
|
||||
windows = { workspace = true }
|
||||
|
||||
|
||||
@@ -356,7 +356,6 @@ fn python_executables_from_installed<'a>(
|
||||
implementation: Option<&'a ImplementationName>,
|
||||
platform: PlatformRequest,
|
||||
preference: PythonPreference,
|
||||
preview: Preview,
|
||||
) -> Box<dyn Iterator<Item = Result<(PythonSource, PathBuf), Error>> + 'a> {
|
||||
let from_managed_installations = iter::once_with(move || {
|
||||
ManagedPythonInstallations::from_settings(None)
|
||||
@@ -411,7 +410,6 @@ fn python_executables_from_installed<'a>(
|
||||
.then(|| {
|
||||
PythonMinorVersionLink::from_installation(
|
||||
&installation,
|
||||
preview,
|
||||
)
|
||||
.filter(PythonMinorVersionLink::exists)
|
||||
.map(
|
||||
@@ -547,7 +545,7 @@ fn python_executables<'a>(
|
||||
|
||||
let from_virtual_environments = python_executables_from_virtual_environments(preview);
|
||||
let from_installed =
|
||||
python_executables_from_installed(version, implementation, platform, preference, preview);
|
||||
python_executables_from_installed(version, implementation, platform, preference);
|
||||
|
||||
// Limit the search to the relevant environment preference; this avoids unnecessary work like
|
||||
// traversal of the file system. Subsequent filtering should be done by the caller with
|
||||
@@ -1542,7 +1540,6 @@ pub(crate) async fn find_best_python_installation(
|
||||
reporter,
|
||||
python_install_mirror,
|
||||
pypy_install_mirror,
|
||||
preview,
|
||||
)
|
||||
.await
|
||||
.map(Some),
|
||||
|
||||
@@ -260,7 +260,6 @@ impl PythonInstallation {
|
||||
reporter,
|
||||
python_install_mirror,
|
||||
pypy_install_mirror,
|
||||
preview,
|
||||
)
|
||||
.await?;
|
||||
|
||||
@@ -278,7 +277,6 @@ impl PythonInstallation {
|
||||
reporter: Option<&dyn Reporter>,
|
||||
python_install_mirror: Option<&str>,
|
||||
pypy_install_mirror: Option<&str>,
|
||||
preview: Preview,
|
||||
) -> Result<Self, Error> {
|
||||
let installations = ManagedPythonInstallations::from_settings(None)?.init()?;
|
||||
let installations_dir = installations.root();
|
||||
@@ -321,7 +319,7 @@ impl PythonInstallation {
|
||||
.patch()
|
||||
.is_some_and(|p| p >= highest_patch)
|
||||
{
|
||||
installed.ensure_minor_version_link(preview)?;
|
||||
installed.ensure_minor_version_link()?;
|
||||
}
|
||||
|
||||
if let Err(e) = installed.ensure_dylib_patched() {
|
||||
|
||||
@@ -12,7 +12,6 @@ use fs_err as fs;
|
||||
use itertools::Itertools;
|
||||
use thiserror::Error;
|
||||
use tracing::{debug, warn};
|
||||
use uv_preview::{Preview, PreviewFeature};
|
||||
#[cfg(windows)]
|
||||
use windows::Win32::Storage::FileSystem::FILE_ATTRIBUTE_REPARSE_POINT;
|
||||
|
||||
@@ -30,6 +29,7 @@ use crate::implementation::{
|
||||
Error as ImplementationError, ImplementationName, LenientImplementationName,
|
||||
};
|
||||
use crate::installation::{self, PythonInstallationKey};
|
||||
use crate::interpreter::Interpreter;
|
||||
use crate::python_version::PythonVersion;
|
||||
use crate::{
|
||||
PythonInstallationMinorVersionKey, PythonRequest, PythonVariant, macos_dylib, sysconfig,
|
||||
@@ -357,7 +357,9 @@ impl ManagedPythonInstallation {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_path(path: PathBuf) -> Result<Self, Error> {
|
||||
pub(crate) fn from_path(path: impl AsRef<Path>) -> Result<Self, Error> {
|
||||
let path = path.as_ref();
|
||||
|
||||
let key = PythonInstallationKey::from_str(
|
||||
path.file_name()
|
||||
.ok_or(Error::NameError("name is empty".to_string()))?
|
||||
@@ -365,7 +367,8 @@ impl ManagedPythonInstallation {
|
||||
.ok_or(Error::NameError("not a valid string".to_string()))?,
|
||||
)?;
|
||||
|
||||
let path = std::path::absolute(&path).map_err(|err| Error::AbsolutePath(path, err))?;
|
||||
let path = std::path::absolute(path)
|
||||
.map_err(|err| Error::AbsolutePath(path.to_path_buf(), err))?;
|
||||
|
||||
// Try to read the BUILD file if it exists
|
||||
let build = match fs::read_to_string(path.join("BUILD")) {
|
||||
@@ -383,6 +386,34 @@ impl ManagedPythonInstallation {
|
||||
})
|
||||
}
|
||||
|
||||
/// Try to create a [`ManagedPythonInstallation`] from an [`Interpreter`].
|
||||
///
|
||||
/// Returns `None` if the interpreter is not a managed installation.
|
||||
pub fn try_from_interpreter(interpreter: &Interpreter) -> Option<Self> {
|
||||
let managed_root = ManagedPythonInstallations::from_settings(None).ok()?;
|
||||
|
||||
// Canonicalize both paths to handle Windows path format differences
|
||||
// (e.g., \\?\ prefix, different casing, junction vs actual path).
|
||||
// Fall back to the original path if canonicalization fails (e.g., target doesn't exist).
|
||||
let sys_base_prefix = dunce::canonicalize(interpreter.sys_base_prefix())
|
||||
.unwrap_or_else(|_| interpreter.sys_base_prefix().to_path_buf());
|
||||
let root = dunce::canonicalize(managed_root.root())
|
||||
.unwrap_or_else(|_| managed_root.root().to_path_buf());
|
||||
|
||||
// Verify the interpreter's base prefix is within the managed root
|
||||
let suffix = sys_base_prefix.strip_prefix(&root).ok()?;
|
||||
|
||||
let first_component = suffix.components().next()?;
|
||||
let name = first_component.as_os_str().to_str()?;
|
||||
|
||||
// Verify it's a valid installation key
|
||||
PythonInstallationKey::from_str(name).ok()?;
|
||||
|
||||
// Construct the installation from the path within the managed root
|
||||
let path = managed_root.root().join(name);
|
||||
Self::from_path(path).ok()
|
||||
}
|
||||
|
||||
/// The path to this managed installation's Python executable.
|
||||
///
|
||||
/// If the installation has multiple executables i.e., `python`, `python3`, etc., this will
|
||||
@@ -560,23 +591,8 @@ impl ManagedPythonInstallation {
|
||||
|
||||
/// Ensure the environment contains the symlink directory (or junction on Windows)
|
||||
/// pointing to the patch directory for this minor version.
|
||||
pub fn ensure_minor_version_link(&self, preview: Preview) -> Result<(), Error> {
|
||||
if let Some(minor_version_link) = PythonMinorVersionLink::from_installation(self, preview) {
|
||||
minor_version_link.create_directory()?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// If the environment contains a symlink directory (or junction on Windows),
|
||||
/// update it to the latest patch directory for this minor version.
|
||||
///
|
||||
/// Unlike [`ensure_minor_version_link`], will not create a new symlink directory
|
||||
/// if one doesn't already exist,
|
||||
pub fn update_minor_version_link(&self, preview: Preview) -> Result<(), Error> {
|
||||
if let Some(minor_version_link) = PythonMinorVersionLink::from_installation(self, preview) {
|
||||
if !minor_version_link.exists() {
|
||||
return Ok(());
|
||||
}
|
||||
pub fn ensure_minor_version_link(&self) -> Result<(), Error> {
|
||||
if let Some(minor_version_link) = PythonMinorVersionLink::from_installation(self) {
|
||||
minor_version_link.create_directory()?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -774,11 +790,7 @@ impl PythonMinorVersionLink {
|
||||
/// For a Python 3.10.8 installation in `C:\path\to\uv\python\cpython-3.10.8-windows-x86_64-none\python.exe`,
|
||||
/// the junction would be `C:\path\to\uv\python\cpython-3.10-windows-x86_64-none` and the executable path including the
|
||||
/// junction would be `C:\path\to\uv\python\cpython-3.10-windows-x86_64-none\python.exe`.
|
||||
pub fn from_executable(
|
||||
executable: &Path,
|
||||
key: &PythonInstallationKey,
|
||||
preview: Preview,
|
||||
) -> Option<Self> {
|
||||
fn from_executable(executable: &Path, key: &PythonInstallationKey) -> Option<Self> {
|
||||
let implementation = key.implementation();
|
||||
if !matches!(
|
||||
implementation.as_ref(),
|
||||
@@ -828,24 +840,11 @@ impl PythonMinorVersionLink {
|
||||
symlink_executable,
|
||||
target_directory,
|
||||
};
|
||||
// If preview mode is disabled, still return a `MinorVersionSymlink` for
|
||||
// existing symlinks, allowing continued operations without the `--preview`
|
||||
// flag after initial symlink directory installation.
|
||||
if !preview.is_enabled(PreviewFeature::PythonUpgrade) && !minor_version_link.exists() {
|
||||
return None;
|
||||
}
|
||||
Some(minor_version_link)
|
||||
}
|
||||
|
||||
pub fn from_installation(
|
||||
installation: &ManagedPythonInstallation,
|
||||
preview: Preview,
|
||||
) -> Option<Self> {
|
||||
Self::from_executable(
|
||||
installation.executable(false).as_path(),
|
||||
installation.key(),
|
||||
preview,
|
||||
)
|
||||
pub fn from_installation(installation: &ManagedPythonInstallation) -> Option<Self> {
|
||||
Self::from_executable(installation.executable(false).as_path(), installation.key())
|
||||
}
|
||||
|
||||
pub fn create_directory(&self) -> Result<(), Error> {
|
||||
@@ -877,13 +876,21 @@ impl PythonMinorVersionLink {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if the minor version link exists and points to the expected target directory.
|
||||
///
|
||||
/// This verifies both that the symlink/junction exists AND that it points to the
|
||||
/// `target_directory` specified in this struct. This is important because the link
|
||||
/// may exist but point to a different installation (e.g., after an upgrade), in which
|
||||
/// case we should not use the link for the current installation.
|
||||
pub fn exists(&self) -> bool {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
self.symlink_directory
|
||||
.symlink_metadata()
|
||||
.map(|metadata| metadata.file_type().is_symlink())
|
||||
.unwrap_or(false)
|
||||
.is_ok_and(|metadata| metadata.file_type().is_symlink())
|
||||
&& self
|
||||
.read_target()
|
||||
.is_some_and(|target| target == self.target_directory)
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
@@ -894,6 +901,25 @@ impl PythonMinorVersionLink {
|
||||
// is a symlink or junction.
|
||||
(metadata.file_attributes() & FILE_ATTRIBUTE_REPARSE_POINT.0) != 0
|
||||
})
|
||||
&& self
|
||||
.read_target()
|
||||
.is_some_and(|target| target == self.target_directory)
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the target of the minor version link.
|
||||
///
|
||||
/// On Unix, this reads the symlink target. On Windows, this reads the junction target
|
||||
/// using the `junction` crate which properly handles the `\??\` prefix that Windows
|
||||
/// uses internally for junction targets.
|
||||
pub fn read_target(&self) -> Option<PathBuf> {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
self.symlink_directory.read_link().ok()
|
||||
}
|
||||
#[cfg(windows)]
|
||||
{
|
||||
junction::get_target(&self.symlink_directory).ok()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ uv-installer = { workspace = true }
|
||||
uv-normalize = { workspace = true }
|
||||
uv-pep440 = { workspace = true }
|
||||
uv-pep508 = { workspace = true }
|
||||
uv-preview = { workspace = true }
|
||||
uv-pypi-types = { workspace = true }
|
||||
uv-python = { workspace = true }
|
||||
uv-settings = { workspace = true }
|
||||
|
||||
@@ -14,7 +14,6 @@ use uv_install_wheel::read_record_file;
|
||||
use uv_installer::SitePackages;
|
||||
use uv_normalize::{InvalidNameError, PackageName};
|
||||
use uv_pep440::Version;
|
||||
use uv_preview::Preview;
|
||||
use uv_python::{Interpreter, PythonEnvironment};
|
||||
use uv_state::{StateBucket, StateStore};
|
||||
use uv_static::EnvVars;
|
||||
@@ -311,7 +310,6 @@ impl InstalledTools {
|
||||
&self,
|
||||
name: &PackageName,
|
||||
interpreter: Interpreter,
|
||||
preview: Preview,
|
||||
) -> Result<PythonEnvironment, Error> {
|
||||
let environment_path = self.tool_dir(name);
|
||||
|
||||
@@ -342,7 +340,6 @@ impl InstalledTools {
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
preview,
|
||||
)?;
|
||||
|
||||
Ok(venv)
|
||||
|
||||
@@ -21,7 +21,6 @@ workspace = true
|
||||
uv-console = { workspace = true }
|
||||
uv-fs = { workspace = true }
|
||||
uv-platform-tags = { workspace = true }
|
||||
uv-preview = { workspace = true }
|
||||
uv-pypi-types = { workspace = true }
|
||||
uv-python = { workspace = true }
|
||||
uv-shell = { workspace = true }
|
||||
|
||||
@@ -3,7 +3,6 @@ use std::path::{Path, PathBuf};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use uv_preview::Preview;
|
||||
use uv_python::{Interpreter, PythonEnvironment};
|
||||
|
||||
pub use virtualenv::{OnExisting, RemovalReason, remove_virtualenv};
|
||||
@@ -63,7 +62,6 @@ pub fn create_venv(
|
||||
relocatable: bool,
|
||||
seed: bool,
|
||||
upgradeable: bool,
|
||||
preview: Preview,
|
||||
) -> Result<PythonEnvironment, Error> {
|
||||
// Create the virtualenv at the given location.
|
||||
let virtualenv = virtualenv::create(
|
||||
@@ -75,7 +73,6 @@ pub fn create_venv(
|
||||
relocatable,
|
||||
seed,
|
||||
upgradeable,
|
||||
preview,
|
||||
)?;
|
||||
|
||||
// Create the corresponding `PythonEnvironment`.
|
||||
|
||||
@@ -14,9 +14,10 @@ use tracing::{debug, trace};
|
||||
use crate::{Error, Prompt};
|
||||
use uv_fs::{CWD, Simplified, cachedir};
|
||||
use uv_platform_tags::Os;
|
||||
use uv_preview::Preview;
|
||||
use uv_pypi_types::Scheme;
|
||||
use uv_python::managed::{PythonMinorVersionLink, create_link_to_executable};
|
||||
use uv_python::managed::{
|
||||
ManagedPythonInstallation, PythonMinorVersionLink, create_link_to_executable,
|
||||
};
|
||||
use uv_python::{Interpreter, VirtualEnvironment};
|
||||
use uv_shell::escape_posix_for_single_quotes;
|
||||
use uv_version::version;
|
||||
@@ -57,7 +58,6 @@ pub(crate) fn create(
|
||||
relocatable: bool,
|
||||
seed: bool,
|
||||
upgradeable: bool,
|
||||
preview: Preview,
|
||||
) -> Result<VirtualEnvironment, Error> {
|
||||
// Determine the base Python executable; that is, the Python executable that should be
|
||||
// considered the "base" for the virtual environment.
|
||||
@@ -205,12 +205,11 @@ pub(crate) fn create(
|
||||
fs_err::write(location.join(".gitignore"), "*")?;
|
||||
|
||||
let mut using_minor_version_link = false;
|
||||
let executable_target = if upgradeable && interpreter.is_standalone() {
|
||||
if let Some(minor_version_link) = PythonMinorVersionLink::from_executable(
|
||||
base_python.as_path(),
|
||||
&interpreter.key(),
|
||||
preview,
|
||||
) {
|
||||
let executable_target = if upgradeable {
|
||||
if let Some(minor_version_link) =
|
||||
ManagedPythonInstallation::try_from_interpreter(interpreter)
|
||||
.and_then(|installation| PythonMinorVersionLink::from_installation(&installation))
|
||||
{
|
||||
if !minor_version_link.exists() {
|
||||
base_python.clone()
|
||||
} else {
|
||||
@@ -236,7 +235,7 @@ pub(crate) fn create(
|
||||
};
|
||||
|
||||
// Per PEP 405, the Python `home` is the parent directory of the interpreter.
|
||||
// In preview mode, for standalone interpreters, this `home` value will include a
|
||||
// For standalone interpreters, this `home` value will include a
|
||||
// symlink directory on Unix or junction on Windows to enable transparent Python patch
|
||||
// upgrades.
|
||||
let python_home = executable_target
|
||||
|
||||
@@ -33,6 +33,7 @@ use uv_pep508::{MarkerEnvironment, RequirementOrigin, VerbatimUrl};
|
||||
use uv_platform_tags::Tags;
|
||||
use uv_preview::Preview;
|
||||
use uv_pypi_types::{Conflicts, ResolverMarkerEnvironment};
|
||||
use uv_python::managed::{ManagedPythonInstallation, PythonMinorVersionLink};
|
||||
use uv_python::{PythonEnvironment, PythonInstallation};
|
||||
use uv_requirements::{
|
||||
GroupsSpecification, LookaheadResolver, NamedRequirementsResolver, RequirementsSource,
|
||||
@@ -900,13 +901,26 @@ pub(crate) fn report_target_environment(
|
||||
cache: &Cache,
|
||||
printer: Printer,
|
||||
) -> Result<(), Error> {
|
||||
// Resolve minor-version link directories (e.g., `cpython-3.12` → `cpython-3.12.12`).
|
||||
// On Windows, junction points aren't resolved by the interpreter's `sys.prefix`, so we
|
||||
// use the target directory from the minor-version link to display the actual installation.
|
||||
// This only applies to managed installations, not virtual environments.
|
||||
let root = if env.interpreter().is_virtualenv() {
|
||||
env.root().to_path_buf()
|
||||
} else {
|
||||
ManagedPythonInstallation::try_from_interpreter(env.interpreter())
|
||||
.and_then(|installation| PythonMinorVersionLink::from_installation(&installation))
|
||||
.map(|link| link.target_directory)
|
||||
.unwrap_or_else(|| env.root().to_path_buf())
|
||||
};
|
||||
|
||||
let message = format!(
|
||||
"Using Python {} environment at: {}",
|
||||
env.interpreter().python_version(),
|
||||
env.root().user_display()
|
||||
root.user_display()
|
||||
);
|
||||
|
||||
let Ok(target) = std::path::absolute(env.root()) else {
|
||||
let Ok(target) = std::path::absolute(&root) else {
|
||||
debug!("{}", message);
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
@@ -191,7 +191,6 @@ impl CachedEnvironment {
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
preview,
|
||||
)?;
|
||||
|
||||
sync_environment(
|
||||
|
||||
@@ -1454,7 +1454,6 @@ impl ProjectEnvironment {
|
||||
false,
|
||||
false,
|
||||
upgradeable,
|
||||
preview,
|
||||
)?;
|
||||
return Ok(if replace {
|
||||
Self::WouldReplace(root, environment, temp_dir)
|
||||
@@ -1496,7 +1495,6 @@ impl ProjectEnvironment {
|
||||
false,
|
||||
false,
|
||||
upgradeable,
|
||||
preview,
|
||||
)?;
|
||||
|
||||
if replace {
|
||||
@@ -1650,7 +1648,6 @@ impl ScriptEnvironment {
|
||||
false,
|
||||
false,
|
||||
upgradeable,
|
||||
preview,
|
||||
)?;
|
||||
return Ok(if root.exists() {
|
||||
Self::WouldReplace(root, environment, temp_dir)
|
||||
@@ -1692,7 +1689,6 @@ impl ScriptEnvironment {
|
||||
false,
|
||||
false,
|
||||
upgradeable,
|
||||
preview,
|
||||
)?;
|
||||
|
||||
Ok(if replaced {
|
||||
|
||||
@@ -498,7 +498,6 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
preview,
|
||||
)?;
|
||||
|
||||
Some(environment.into_interpreter())
|
||||
@@ -717,7 +716,6 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
preview,
|
||||
)?
|
||||
} else {
|
||||
// If we're not isolating the environment, reuse the base environment for the
|
||||
@@ -954,7 +952,6 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
preview,
|
||||
)?;
|
||||
venv.into_interpreter()
|
||||
} else {
|
||||
@@ -1082,7 +1079,6 @@ hint: If you are running a script with `{}` in the shebang, you may need to incl
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
preview,
|
||||
)
|
||||
})
|
||||
.transpose()?
|
||||
|
||||
@@ -28,6 +28,7 @@ pub(crate) async fn find(
|
||||
project_dir: &Path,
|
||||
request: Option<String>,
|
||||
show_version: bool,
|
||||
resolve_links: bool,
|
||||
no_project: bool,
|
||||
no_config: bool,
|
||||
system: bool,
|
||||
@@ -112,11 +113,12 @@ pub(crate) async fn find(
|
||||
python.interpreter().python_version()
|
||||
)?;
|
||||
} else {
|
||||
writeln!(
|
||||
printer.stdout(),
|
||||
"{}",
|
||||
std::path::absolute(python.interpreter().sys_executable())?.simplified_display()
|
||||
)?;
|
||||
let path = if resolve_links {
|
||||
dunce::canonicalize(python.interpreter().sys_executable())?
|
||||
} else {
|
||||
std::path::absolute(python.interpreter().sys_executable())?
|
||||
};
|
||||
writeln!(printer.stdout(), "{}", path.simplified_display())?;
|
||||
}
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
@@ -125,6 +127,7 @@ pub(crate) async fn find(
|
||||
pub(crate) async fn find_script(
|
||||
script: Pep723ItemRef<'_>,
|
||||
show_version: bool,
|
||||
resolve_links: bool,
|
||||
client_builder: &BaseClientBuilder<'_>,
|
||||
python_preference: PythonPreference,
|
||||
python_downloads: PythonDownloads,
|
||||
@@ -160,11 +163,12 @@ pub(crate) async fn find_script(
|
||||
if show_version {
|
||||
writeln!(printer.stdout(), "{}", interpreter.python_version())?;
|
||||
} else {
|
||||
writeln!(
|
||||
printer.stdout(),
|
||||
"{}",
|
||||
std::path::absolute(interpreter.sys_executable())?.simplified_display()
|
||||
)?;
|
||||
let path = if resolve_links {
|
||||
dunce::canonicalize(interpreter.sys_executable())?
|
||||
} else {
|
||||
std::path::absolute(interpreter.sys_executable())?
|
||||
};
|
||||
writeln!(printer.stdout(), "{}", path.simplified_display())?;
|
||||
}
|
||||
|
||||
Ok(ExitStatus::Success)
|
||||
|
||||
@@ -318,15 +318,6 @@ async fn perform_install(
|
||||
);
|
||||
}
|
||||
|
||||
if let PythonUpgrade::Enabled(source @ PythonUpgradeSource::Upgrade) = upgrade {
|
||||
if !preview.is_enabled(PreviewFeature::PythonUpgrade) {
|
||||
warn_user!(
|
||||
"`{source}` is experimental and may change without warning. Pass `--preview-features {}` to disable this warning",
|
||||
PreviewFeature::PythonUpgrade
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if default && targets.len() > 1 {
|
||||
anyhow::bail!("The `--default` flag cannot be used with multiple targets");
|
||||
}
|
||||
@@ -733,16 +724,7 @@ async fn perform_install(
|
||||
);
|
||||
|
||||
for installation in minor_versions.values() {
|
||||
if matches!(
|
||||
upgrade,
|
||||
PythonUpgrade::Enabled(PythonUpgradeSource::Upgrade)
|
||||
) {
|
||||
// During an upgrade, update existing symlinks but avoid
|
||||
// creating new ones.
|
||||
installation.update_minor_version_link(preview)?;
|
||||
} else {
|
||||
installation.ensure_minor_version_link(preview)?;
|
||||
}
|
||||
installation.ensure_minor_version_link()?;
|
||||
}
|
||||
|
||||
if changelog.installed.is_empty() && errors.is_empty() {
|
||||
@@ -1015,7 +997,7 @@ fn create_bin_links(
|
||||
}
|
||||
let executable = if upgradeable {
|
||||
if let Some(minor_version_link) =
|
||||
PythonMinorVersionLink::from_installation(installation, preview)
|
||||
PythonMinorVersionLink::from_installation(installation)
|
||||
{
|
||||
minor_version_link.symlink_executable.clone()
|
||||
} else {
|
||||
|
||||
@@ -12,7 +12,6 @@ use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use tracing::{debug, warn};
|
||||
|
||||
use uv_fs::Simplified;
|
||||
use uv_preview::Preview;
|
||||
use uv_python::downloads::PythonDownloadRequest;
|
||||
use uv_python::managed::{
|
||||
ManagedPythonInstallations, PythonMinorVersionLink, python_executable_dir,
|
||||
@@ -30,14 +29,13 @@ pub(crate) async fn uninstall(
|
||||
targets: Vec<String>,
|
||||
all: bool,
|
||||
printer: Printer,
|
||||
preview: Preview,
|
||||
) -> Result<ExitStatus> {
|
||||
let installations = ManagedPythonInstallations::from_settings(install_dir)?.init()?;
|
||||
|
||||
let _lock = installations.lock().await?;
|
||||
|
||||
// Perform the uninstallation.
|
||||
do_uninstall(&installations, targets, all, printer, preview).await?;
|
||||
do_uninstall(&installations, targets, all, printer).await?;
|
||||
|
||||
// Clean up any empty directories.
|
||||
if uv_fs::directories(installations.root())?.all(|path| uv_fs::is_temporary(&path)) {
|
||||
@@ -66,7 +64,6 @@ async fn do_uninstall(
|
||||
targets: Vec<String>,
|
||||
all: bool,
|
||||
printer: Printer,
|
||||
preview: Preview,
|
||||
) -> Result<ExitStatus> {
|
||||
let start = std::time::Instant::now();
|
||||
|
||||
@@ -241,7 +238,7 @@ async fn do_uninstall(
|
||||
.iter()
|
||||
.filter(|(minor_version, _)| uninstalled_minor_versions.contains(minor_version))
|
||||
{
|
||||
installation.update_minor_version_link(preview)?;
|
||||
installation.ensure_minor_version_link()?;
|
||||
}
|
||||
// For each uninstalled installation, check if there are no remaining installations
|
||||
// for its minor version. If there are none remaining, remove the symlink directory
|
||||
@@ -249,7 +246,7 @@ async fn do_uninstall(
|
||||
for installation in &matching_installations {
|
||||
if !remaining_minor_versions.contains_key(installation.minor_version_key()) {
|
||||
if let Some(minor_version_link) =
|
||||
PythonMinorVersionLink::from_installation(installation, preview)
|
||||
PythonMinorVersionLink::from_installation(installation)
|
||||
{
|
||||
if minor_version_link.exists() {
|
||||
let result = if cfg!(windows) {
|
||||
|
||||
@@ -696,7 +696,7 @@ pub(crate) async fn install(
|
||||
},
|
||||
};
|
||||
|
||||
let environment = installed_tools.create_environment(package_name, interpreter, preview)?;
|
||||
let environment = installed_tools.create_environment(package_name, interpreter)?;
|
||||
|
||||
// At this point, we removed any existing environment, so we should remove any of its
|
||||
// executables.
|
||||
|
||||
@@ -360,7 +360,7 @@ async fn upgrade_tool(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let environment = installed_tools.create_environment(name, interpreter.clone(), preview)?;
|
||||
let environment = installed_tools.create_environment(name, interpreter.clone())?;
|
||||
|
||||
let environment = sync_environment(
|
||||
environment,
|
||||
|
||||
@@ -21,7 +21,7 @@ use uv_distribution_types::{
|
||||
use uv_fs::Simplified;
|
||||
use uv_install_wheel::LinkMode;
|
||||
use uv_normalize::DefaultGroups;
|
||||
use uv_preview::{Preview, PreviewFeature};
|
||||
use uv_preview::Preview;
|
||||
use uv_python::{
|
||||
EnvironmentPreference, PythonDownloads, PythonInstallation, PythonPreference, PythonRequest,
|
||||
};
|
||||
@@ -190,10 +190,9 @@ pub(crate) async fn venv(
|
||||
path.user_display().cyan()
|
||||
)?;
|
||||
|
||||
let upgradeable = preview.is_enabled(PreviewFeature::PythonUpgrade)
|
||||
&& python_request
|
||||
.as_ref()
|
||||
.is_none_or(|request| !request.includes_patch());
|
||||
let upgradeable = python_request
|
||||
.as_ref()
|
||||
.is_none_or(|request| !request.includes_patch());
|
||||
|
||||
// Create the virtual environment.
|
||||
let venv = uv_virtualenv::create_venv(
|
||||
@@ -205,7 +204,6 @@ pub(crate) async fn venv(
|
||||
relocatable,
|
||||
seed,
|
||||
upgradeable,
|
||||
preview,
|
||||
)
|
||||
.map_err(VenvError::Creation)?;
|
||||
|
||||
|
||||
@@ -1733,14 +1733,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
||||
let args = settings::PythonUninstallSettings::resolve(args, filesystem);
|
||||
show_settings!(args);
|
||||
|
||||
commands::python_uninstall(
|
||||
args.install_dir,
|
||||
args.targets,
|
||||
args.all,
|
||||
printer,
|
||||
globals.preview,
|
||||
)
|
||||
.await
|
||||
commands::python_uninstall(args.install_dir, args.targets, args.all, printer).await
|
||||
}
|
||||
Commands::Python(PythonNamespace {
|
||||
command: PythonCommand::Find(args),
|
||||
@@ -1755,6 +1748,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
||||
commands::python_find_script(
|
||||
(&script).into(),
|
||||
args.show_version,
|
||||
args.resolve_links,
|
||||
// TODO(zsol): is this the right thing to do here?
|
||||
&client_builder.subcommand(vec!["python".to_owned(), "find".to_owned()]),
|
||||
globals.python_preference,
|
||||
@@ -1770,6 +1764,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
|
||||
&project_dir,
|
||||
args.request,
|
||||
args.show_version,
|
||||
args.resolve_links,
|
||||
args.no_project,
|
||||
cli.top_level.no_config,
|
||||
args.system,
|
||||
|
||||
@@ -1457,6 +1457,7 @@ impl PythonUninstallSettings {
|
||||
pub(crate) struct PythonFindSettings {
|
||||
pub(crate) request: Option<String>,
|
||||
pub(crate) show_version: bool,
|
||||
pub(crate) resolve_links: bool,
|
||||
pub(crate) no_project: bool,
|
||||
pub(crate) system: bool,
|
||||
pub(crate) python_downloads_json_url: Option<String>,
|
||||
@@ -1472,6 +1473,7 @@ impl PythonFindSettings {
|
||||
let PythonFindArgs {
|
||||
request,
|
||||
show_version,
|
||||
resolve_links,
|
||||
no_project,
|
||||
system,
|
||||
no_system,
|
||||
@@ -1499,6 +1501,7 @@ impl PythonFindSettings {
|
||||
Self {
|
||||
request,
|
||||
show_version,
|
||||
resolve_links,
|
||||
no_project,
|
||||
system: flag(system, no_system, "system").unwrap_or_default(),
|
||||
python_downloads_json_url,
|
||||
|
||||
@@ -9,6 +9,7 @@ use std::path::{Path, PathBuf};
|
||||
use std::process::{Command, ExitStatus, Output, Stdio};
|
||||
use std::str::FromStr;
|
||||
use std::{env, io};
|
||||
use uv_preview::Preview;
|
||||
use uv_python::downloads::ManagedPythonDownloadList;
|
||||
|
||||
use assert_cmd::assert::{Assert, OutputAssertExt};
|
||||
@@ -26,7 +27,6 @@ use tokio::io::AsyncWriteExt;
|
||||
|
||||
use uv_cache::{Cache, CacheBucket};
|
||||
use uv_fs::Simplified;
|
||||
use uv_preview::Preview;
|
||||
use uv_python::managed::ManagedPythonInstallations;
|
||||
use uv_python::{
|
||||
EnvironmentPreference, PythonInstallation, PythonPreference, PythonRequest, PythonVersion,
|
||||
|
||||
@@ -13671,7 +13671,10 @@ fn pip_install_no_sources_editable_to_registry_switch() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "test-python-managed")]
|
||||
// TODO(zb): On Windows, this test shows the minor version symlink path instead of the
|
||||
// actual installation path. The `report_target_environment` fix only handles the "Using Python"
|
||||
// message but not the "externally managed" error path which uses `env.root()` directly.
|
||||
#[cfg(all(feature = "test-python-managed", not(windows)))]
|
||||
#[test]
|
||||
fn install_with_system_interpreter() {
|
||||
let context = TestContext::new_with_versions(&[])
|
||||
|
||||
@@ -1381,7 +1381,7 @@ fn python_find_prerelease_version_specifiers() {
|
||||
context.python_install().arg("3.14.0rc3").assert().success();
|
||||
|
||||
// `>=3.14` should allow pre-release versions
|
||||
uv_snapshot!(context.filters(), context.python_find().arg(">=3.14"), @"
|
||||
uv_snapshot!(context.filters(), context.python_find().arg(">=3.14").arg("--resolve-links"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -1392,7 +1392,7 @@ fn python_find_prerelease_version_specifiers() {
|
||||
");
|
||||
|
||||
// `>3.14rc2` should not match rc2
|
||||
uv_snapshot!(context.filters(), context.python_find().arg(">3.14.0rc2"), @"
|
||||
uv_snapshot!(context.filters(), context.python_find().arg(">3.14.0rc2").arg("--resolve-links"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -1412,7 +1412,7 @@ fn python_find_prerelease_version_specifiers() {
|
||||
");
|
||||
|
||||
// `>=3.14.0rc3` should match rc3
|
||||
uv_snapshot!(context.filters(), context.python_find().arg(">=3.14.0rc3"), @"
|
||||
uv_snapshot!(context.filters(), context.python_find().arg(">=3.14.0rc3").arg("--resolve-links"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -1422,7 +1422,7 @@ fn python_find_prerelease_version_specifiers() {
|
||||
");
|
||||
|
||||
// `<3.14.0rc3` should match rc2
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("<3.14.0rc3"), @"
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("<3.14.0rc3").arg("--resolve-links"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -1432,7 +1432,7 @@ fn python_find_prerelease_version_specifiers() {
|
||||
");
|
||||
|
||||
// `<=3.14.0rc3` should match rc3
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("<=3.14.0rc3"), @"
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("<=3.14.0rc3").arg("--resolve-links"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -1445,7 +1445,7 @@ fn python_find_prerelease_version_specifiers() {
|
||||
context.python_install().arg("3.14.0").assert().success();
|
||||
|
||||
// `>=3.14` should prefer stable
|
||||
uv_snapshot!(context.filters(), context.python_find().arg(">=3.14"), @"
|
||||
uv_snapshot!(context.filters(), context.python_find().arg(">=3.14").arg("--resolve-links"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -1455,7 +1455,7 @@ fn python_find_prerelease_version_specifiers() {
|
||||
");
|
||||
|
||||
// `>3.14rc2` should prefer stable
|
||||
uv_snapshot!(context.filters(), context.python_find().arg(">3.14.0rc2"), @"
|
||||
uv_snapshot!(context.filters(), context.python_find().arg(">3.14.0rc2").arg("--resolve-links"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -1485,7 +1485,7 @@ fn python_find_prerelease_with_patch_request() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.14.0rc3-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
|
||||
----- stderr -----
|
||||
warning: You're using a pre-release version of Python (3.14.0rc3) but a stable version is available. Use `uv python upgrade 3.14` to upgrade.
|
||||
|
||||
@@ -57,7 +57,7 @@ fn python_install() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14"
|
||||
read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/bin/python3.14"
|
||||
);
|
||||
});
|
||||
} else if cfg!(windows) {
|
||||
@@ -65,7 +65,7 @@ fn python_install() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python"
|
||||
read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/python"
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -476,7 +476,7 @@ fn python_install_minor() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.11.[LATEST]-[PLATFORM]/bin/python3.11"
|
||||
read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.11-[PLATFORM]/bin/python3.11"
|
||||
);
|
||||
});
|
||||
} else if cfg!(windows) {
|
||||
@@ -484,7 +484,7 @@ fn python_install_minor() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.11.[LATEST]-[PLATFORM]/python"
|
||||
read_link(&bin_python), @"[TEMP_DIR]/managed/cpython-3.11-[PLATFORM]/python"
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1332,7 +1332,7 @@ fn python_install_debug() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13.[LATEST]-[PLATFORM]/bin/python3.13
|
||||
[TEMP_DIR]/managed/cpython-3.13-[PLATFORM]/bin/python3.13
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -1495,7 +1495,7 @@ fn python_install_debug_freethreaded() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13.[LATEST]-[PLATFORM]/bin/python3.13
|
||||
[TEMP_DIR]/managed/cpython-3.13-[PLATFORM]/bin/python3.13
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -1504,7 +1504,7 @@ fn python_install_debug_freethreaded() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13.[LATEST]+freethreaded-[PLATFORM]/bin/python3.13t
|
||||
[TEMP_DIR]/managed/cpython-3.13+freethreaded-[PLATFORM]/bin/python3.13t
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -1667,7 +1667,7 @@ fn python_install_default() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14"
|
||||
read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/bin/python3.14"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1675,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.[LATEST]-[PLATFORM]/bin/python3.14"
|
||||
read_link(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/bin/python3.14"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1683,7 +1683,7 @@ fn python_install_default() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14"
|
||||
read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/bin/python3.14"
|
||||
);
|
||||
});
|
||||
} else if cfg!(windows) {
|
||||
@@ -1691,7 +1691,7 @@ fn python_install_default() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python"
|
||||
read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/python"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1699,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.[LATEST]-[PLATFORM]/python"
|
||||
read_link(&bin_python_minor_14), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/python"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1707,7 +1707,7 @@ fn python_install_default() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/python"
|
||||
read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/python"
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -1767,7 +1767,7 @@ fn python_install_default() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/bin/python3.12"
|
||||
read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/bin/python3.12"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1775,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.[LATEST]-[PLATFORM]/bin/python3.12"
|
||||
read_link(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/bin/python3.12"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1783,7 +1783,7 @@ fn python_install_default() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/bin/python3.12"
|
||||
read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/bin/python3.12"
|
||||
);
|
||||
});
|
||||
} else {
|
||||
@@ -1791,7 +1791,7 @@ fn python_install_default() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/python"
|
||||
read_link(&bin_python_major), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/python"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1799,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.[LATEST]-[PLATFORM]/python"
|
||||
read_link(&bin_python_minor_12), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/python"
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1807,7 +1807,7 @@ fn python_install_default() {
|
||||
filters => context.filters(),
|
||||
}, {
|
||||
insta::assert_snapshot!(
|
||||
read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12.[LATEST]-[PLATFORM]/python"
|
||||
read_link(&bin_python_default), @"[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/python"
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -2500,7 +2500,7 @@ fn python_find_prerelease() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.15.[LATEST]-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
[TEMP_DIR]/managed/cpython-3.15-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -2510,7 +2510,7 @@ fn python_find_prerelease() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.15.[LATEST]-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
[TEMP_DIR]/managed/cpython-3.15-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -2519,7 +2519,7 @@ fn python_find_prerelease() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.15.[LATEST]-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
[TEMP_DIR]/managed/cpython-3.15-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -2539,7 +2539,7 @@ fn python_find_prerelease() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13.[LATEST]-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
[TEMP_DIR]/managed/cpython-3.13-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -2800,7 +2800,7 @@ fn python_install_emulated_macos() {
|
||||
");
|
||||
|
||||
// It should be discoverable with `uv python find`
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13"), @r"
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13").arg("--resolve-links"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -2815,7 +2815,7 @@ fn python_install_emulated_macos() {
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
cpython-3.13.[LATEST]-macos-aarch64-none <download available>
|
||||
cpython-3.13.[LATEST]-macos-x86_64-none managed/cpython-3.13.[LATEST]-macos-x86_64-none/bin/python3.13
|
||||
cpython-3.13.[LATEST]-macos-x86_64-none managed/cpython-3.13-macos-x86_64-none/bin/python3.13
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -2831,7 +2831,7 @@ fn python_install_emulated_macos() {
|
||||
");
|
||||
|
||||
// Once we've installed the native version, it should be preferred over x86_64
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13"), @r"
|
||||
uv_snapshot!(context.filters(), context.python_find().arg("3.13").arg("--resolve-links"), @r"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -2876,7 +2876,7 @@ fn python_install_emulated_windows_x86_on_x64() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13.[LATEST]-windows-x86-none/python
|
||||
[TEMP_DIR]/managed/cpython-3.13-windows-x86-none/python
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -2887,7 +2887,7 @@ fn python_install_emulated_windows_x86_on_x64() {
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
cpython-3.13.[LATEST]-windows-x86_64-none <download available>
|
||||
cpython-3.13.[LATEST]-windows-x86-none managed/cpython-3.13.[LATEST]-windows-x86-none/python
|
||||
cpython-3.13.[LATEST]-windows-x86-none managed/cpython-3.13-windows-x86-none/python
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -2907,7 +2907,7 @@ fn python_install_emulated_windows_x86_on_x64() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.13.[LATEST]-windows-x86_64-none/python
|
||||
[TEMP_DIR]/managed/cpython-3.13-windows-x86_64-none/python
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -3463,14 +3463,15 @@ fn uninstall_last_patch() {
|
||||
);
|
||||
|
||||
#[cfg(windows)]
|
||||
uv_snapshot!(filters, context.run().arg("python").arg("--version"), @r#"
|
||||
uv_snapshot!(filters, context.run().arg("python").arg("--version"), @r"
|
||||
success: false
|
||||
exit_code: 103
|
||||
exit_code: 2
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
No Python at '"[TEMP_DIR]/managed/cpython-3.10-[PLATFORM]/python'
|
||||
"#
|
||||
error: Failed to inspect Python interpreter from active virtual environment at `.venv/[BIN]/python`
|
||||
Caused by: Python interpreter not found at `[VENV]/[BIN]/python`
|
||||
"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3630,7 +3631,7 @@ fn python_install_pyodide() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.14.[LATEST]-[PLATFORM]/bin/python3.14
|
||||
[TEMP_DIR]/managed/cpython-3.14-[PLATFORM]/bin/python3.14
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
@@ -3687,7 +3688,7 @@ fn python_install_build_version() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
[TEMP_DIR]/managed/cpython-3.12.5-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
[TEMP_DIR]/managed/cpython-3.12-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
|
||||
----- stderr -----
|
||||
");
|
||||
|
||||
@@ -456,7 +456,7 @@ fn python_list_downloads_installed() {
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
cpython-3.10.19-[PLATFORM] managed/cpython-3.10.19-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
cpython-3.10.19-[PLATFORM] managed/cpython-3.10-[PLATFORM]/[INSTALL-BIN]/[PYTHON]
|
||||
pypy-3.10.16-[PLATFORM] <download available>
|
||||
graalpy-3.10.0-[PLATFORM] <download available>
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ fn python_upgrade() {
|
||||
.with_filtered_latest_python_versions();
|
||||
|
||||
// Install an earlier patch version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.10.17"), @"
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.10.17"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -27,7 +27,7 @@ fn python_upgrade() {
|
||||
");
|
||||
|
||||
// Don't accept patch version as argument to upgrade command
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.10.17"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10.17"), @"
|
||||
success: false
|
||||
exit_code: 1
|
||||
----- stdout -----
|
||||
@@ -37,7 +37,7 @@ fn python_upgrade() {
|
||||
");
|
||||
|
||||
// Upgrade patch version
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.10"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -48,7 +48,7 @@ fn python_upgrade() {
|
||||
");
|
||||
|
||||
// Should be a no-op when already upgraded
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.10"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -58,7 +58,7 @@ fn python_upgrade() {
|
||||
");
|
||||
|
||||
// Should reinstall on `--reinstall`
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.10").arg("--reinstall"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10").arg("--reinstall"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -86,7 +86,6 @@ fn python_upgrade() {
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
warning: `uv python upgrade` is experimental and may change without warning. Pass `--preview-features python-upgrade` to disable this warning
|
||||
Installed Python 3.14.[LATEST] in [TIME]
|
||||
+ cpython-3.14.[LATEST]-[PLATFORM] (python3.14)
|
||||
");
|
||||
@@ -101,7 +100,7 @@ fn python_upgrade_without_version() {
|
||||
.with_managed_python_dirs();
|
||||
|
||||
// Should be a no-op when no versions have been installed
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade(), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -111,7 +110,7 @@ fn python_upgrade_without_version() {
|
||||
");
|
||||
|
||||
// Install earlier patch versions for different minor versions
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.11.8").arg("3.12.8").arg("3.13.1"), @"
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.11.8").arg("3.12.8").arg("3.13.1"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -127,7 +126,7 @@ fn python_upgrade_without_version() {
|
||||
filters.push((r"3.13.\d+", "3.13.[X]"));
|
||||
|
||||
// Upgrade one patch version
|
||||
uv_snapshot!(filters, context.python_upgrade().arg("--preview").arg("3.13"), @"
|
||||
uv_snapshot!(filters, context.python_upgrade().arg("3.13"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -139,7 +138,7 @@ fn python_upgrade_without_version() {
|
||||
|
||||
// Providing no minor version to `uv python upgrade` should upgrade the rest
|
||||
// of the patch versions
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade(), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -151,7 +150,7 @@ fn python_upgrade_without_version() {
|
||||
");
|
||||
|
||||
// Should be a no-op when every version is already upgraded
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade(), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -170,7 +169,7 @@ fn python_upgrade_transparent_from_venv() {
|
||||
.with_managed_python_dirs();
|
||||
|
||||
// Install an earlier patch version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.10.17"), @"
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.10.17"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -228,7 +227,7 @@ fn python_upgrade_transparent_from_venv() {
|
||||
);
|
||||
|
||||
// Upgrade patch version
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.10"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -262,8 +261,8 @@ fn python_upgrade_transparent_from_venv() {
|
||||
);
|
||||
}
|
||||
|
||||
// Installing Python in preview mode should not prevent virtual environments
|
||||
// from transparently upgrading.
|
||||
// Installing Python should not prevent virtual environments from transparently
|
||||
// upgrading.
|
||||
#[test]
|
||||
fn python_upgrade_transparent_from_venv_preview() {
|
||||
let context: TestContext = TestContext::new_with_versions(&["3.13"])
|
||||
@@ -272,8 +271,8 @@ fn python_upgrade_transparent_from_venv_preview() {
|
||||
.with_filtered_exe_suffix()
|
||||
.with_managed_python_dirs();
|
||||
|
||||
// Install an earlier patch version using `--preview`
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.10.17").arg("--preview"), @"
|
||||
// Install an earlier patch version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.10.17"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -306,7 +305,7 @@ fn python_upgrade_transparent_from_venv_preview() {
|
||||
);
|
||||
|
||||
// Upgrade patch version
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.10"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -337,7 +336,7 @@ fn python_upgrade_ignored_with_python_pin() {
|
||||
.with_managed_python_dirs();
|
||||
|
||||
// Install an earlier patch version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.10.17"), @"
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.10.17"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -370,7 +369,7 @@ fn python_upgrade_ignored_with_python_pin() {
|
||||
");
|
||||
|
||||
// Upgrade patch version
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.10"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -403,7 +402,7 @@ fn python_no_transparent_upgrade_with_venv_patch_specification() {
|
||||
.with_managed_python_dirs();
|
||||
|
||||
// Install an earlier patch version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.10.17"), @"
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.10.17"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -436,7 +435,7 @@ fn python_no_transparent_upgrade_with_venv_patch_specification() {
|
||||
);
|
||||
|
||||
// Upgrade patch version
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.10"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -470,7 +469,7 @@ fn python_transparent_upgrade_venv_venv() {
|
||||
.with_managed_python_dirs();
|
||||
|
||||
// Install an earlier patch version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.10.17"), @"
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.10.17"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -528,7 +527,7 @@ fn python_transparent_upgrade_venv_venv() {
|
||||
);
|
||||
|
||||
// Upgrade patch version
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.10"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -566,7 +565,7 @@ fn python_upgrade_transparent_from_venv_module() {
|
||||
let bin_dir = context.temp_dir.child("bin");
|
||||
|
||||
// Install earlier patch version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.12.9"), @"
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.12.9"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -597,7 +596,7 @@ fn python_upgrade_transparent_from_venv_module() {
|
||||
);
|
||||
|
||||
// Upgrade patch version
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.12"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.12"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -634,7 +633,7 @@ fn python_upgrade_transparent_from_venv_module_in_venv() {
|
||||
let bin_dir = context.temp_dir.child("bin");
|
||||
|
||||
// Install earlier patch version
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.10.17"), @"
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.10.17"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -683,7 +682,7 @@ fn python_upgrade_transparent_from_venv_module_in_venv() {
|
||||
);
|
||||
|
||||
// Upgrade patch version
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.10"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.10"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -725,7 +724,7 @@ fn python_upgrade_force_install() -> Result<()> {
|
||||
.touch()?;
|
||||
|
||||
// Try to upgrade with a non-managed interpreter installed in `bin`.
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.12"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.12"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -737,7 +736,7 @@ fn python_upgrade_force_install() -> Result<()> {
|
||||
");
|
||||
|
||||
// Force the `bin` install.
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.12").arg("--force").arg("--preview").arg("3.12"), @"
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.12").arg("--force").arg("3.12"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -769,7 +768,6 @@ fn python_upgrade_implementation() {
|
||||
----- stdout -----
|
||||
|
||||
----- stderr -----
|
||||
warning: `uv python upgrade` is experimental and may change without warning. Pass `--preview-features python-upgrade` to disable this warning
|
||||
All versions already on latest supported patch release
|
||||
");
|
||||
}
|
||||
@@ -783,7 +781,7 @@ fn python_upgrade_build_version() {
|
||||
.with_managed_python_dirs();
|
||||
|
||||
// Install Python 3.12
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("--preview").arg("3.12"), @"
|
||||
uv_snapshot!(context.filters(), context.python_install().arg("3.12"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -794,7 +792,7 @@ fn python_upgrade_build_version() {
|
||||
");
|
||||
|
||||
// Should be a no-op when already installed at latest version
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.12"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.12"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -812,7 +810,7 @@ fn python_upgrade_build_version() {
|
||||
fs_err::write(&build_file, "19000101").unwrap();
|
||||
|
||||
// Now upgrade should detect the outdated build version and reinstall
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.12"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.12"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
@@ -823,7 +821,7 @@ fn python_upgrade_build_version() {
|
||||
");
|
||||
|
||||
// Should be a no-op again after upgrade
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("--preview").arg("3.12"), @"
|
||||
uv_snapshot!(context.filters(), context.python_upgrade().arg("3.12"), @"
|
||||
success: true
|
||||
exit_code: 0
|
||||
----- stdout -----
|
||||
|
||||
@@ -51,11 +51,6 @@ can use `uv pip install` with a `pylock.toml` file without additional configurat
|
||||
specifying the `pylock.toml` file indicates you want to use the feature. However, a warning will be
|
||||
displayed that the feature is in preview. The preview feature can be enabled to silence the warning.
|
||||
|
||||
Other preview features change behavior without changes to your use of uv. For example, when the
|
||||
`python-upgrade` feature is enabled, the default behavior of `uv python install` changes to allow uv
|
||||
to upgrade Python versions transparently. This feature requires enabling the preview flag for proper
|
||||
usage.
|
||||
|
||||
## Available preview features
|
||||
|
||||
The following preview features are available:
|
||||
@@ -67,8 +62,6 @@ The following preview features are available:
|
||||
- `pylock`: Allows installing from `pylock.toml` files.
|
||||
- `python-install-default`: Allows
|
||||
[installing `python` and `python3` executables](./python-versions.md#installing-python-executables).
|
||||
- `python-upgrade`: Allows
|
||||
[transparent Python version upgrades](./python-versions.md#upgrading-python-versions).
|
||||
- `format`: Allows using `uv format`.
|
||||
- `native-auth`: Enables storage of credentials in a
|
||||
[system-native location](../concepts/authentication/http.md#the-uv-credentials-store).
|
||||
|
||||
@@ -158,12 +158,9 @@ $ uv python install 3.12.8 # Updates `python3.12` to point to 3.12.8
|
||||
|
||||
!!! important
|
||||
|
||||
Support for upgrading Python versions is in _preview_. This means the behavior is experimental
|
||||
and subject to change.
|
||||
|
||||
Upgrades are only supported for uv-managed Python versions.
|
||||
|
||||
Upgrades are not currently supported for PyPy and GraalPy.
|
||||
Upgrades are not currently supported for PyPy, GraalPy, and Pyodide.
|
||||
|
||||
uv allows transparently upgrading Python versions to the latest patch release, e.g., 3.13.4 to
|
||||
3.13.5. uv does not allow transparently upgrading across minor Python versions, e.g., 3.12 to 3.13,
|
||||
@@ -187,14 +184,8 @@ $ uv python upgrade
|
||||
After an upgrade, uv will prefer the new version, but will retain the existing version as it may
|
||||
still be used by virtual environments.
|
||||
|
||||
If the Python version was installed with the `python-upgrade` [preview feature](./preview.md)
|
||||
enabled, e.g., `uv python install 3.12 --preview-features python-upgrade`, virtual environments
|
||||
using the Python version will be automatically upgraded to the new patch version.
|
||||
|
||||
!!! note
|
||||
|
||||
If the virtual environment was created _before_ opting in to the preview mode, it will not be
|
||||
included in the automatic upgrades.
|
||||
Virtual environments using the Python version will be automatically upgraded to the new patch
|
||||
version.
|
||||
|
||||
If a virtual environment was created with an explicitly requested patch version, e.g.,
|
||||
`uv venv -p 3.10.8`, it will not be transparently upgraded to a new version.
|
||||
|
||||
Reference in New Issue
Block a user