diff --git a/crates/uv-dev/src/fetch_python.rs b/crates/uv-dev/src/fetch_python.rs index 3303a323b..859ec8008 100644 --- a/crates/uv-dev/src/fetch_python.rs +++ b/crates/uv-dev/src/fetch_python.rs @@ -8,7 +8,7 @@ use uv_toolchain::ToolchainRequest; use uv_fs::Simplified; use uv_toolchain::downloads::{DownloadResult, Error, PythonDownload, PythonDownloadRequest}; -use uv_toolchain::managed::InstalledToolchains; +use uv_toolchain::managed::{InstalledToolchain, InstalledToolchains}; #[derive(Parser, Debug)] pub(crate) struct FetchPythonArgs { @@ -73,6 +73,11 @@ pub(crate) async fn fetch_python(args: FetchPythonArgs) -> Result<()> { results.push((version, path)); } + for (_, path) in results { + let installed = InstalledToolchain::new(path)?; + installed.ensure_externally_managed()?; + } + if downloaded > 0 { let s = if downloaded == 1 { "" } else { "s" }; info!( diff --git a/crates/uv-toolchain/src/managed.rs b/crates/uv-toolchain/src/managed.rs index 73e5e7c9f..f21d80dc7 100644 --- a/crates/uv-toolchain/src/managed.rs +++ b/crates/uv-toolchain/src/managed.rs @@ -196,6 +196,10 @@ impl InstalledToolchains { } } +static EXTERNALLY_MANAGED: &str = "[externally-managed] +Error=This toolchain is managed by uv and should not be modified. +"; + /// A uv-managed Python toolchain installed on the current system.. #[derive(Debug, Clone)] pub struct InstalledToolchain { @@ -283,6 +287,20 @@ impl InstalledToolchain { ToolchainRequest::Version(version) => version.matches_version(&self.python_version), } } + + /// Ensure the toolchain is marked as externally managed with the + /// standard `EXTERNALLY-MANAGED` file. + pub fn ensure_externally_managed(&self) -> Result<(), Error> { + let lib = if cfg!(windows) { "Lib" } else { "lib" }; + let file = self + .path + .join("install") + .join(lib) + .join(format!("python{}", self.python_version.python_version())) + .join("EXTERNALLY-MANAGED"); + fs_err::write(file, EXTERNALLY_MANAGED)?; + Ok(()) + } } /// Generate a platform portion of a key from the environment. diff --git a/crates/uv-toolchain/src/toolchain.rs b/crates/uv-toolchain/src/toolchain.rs index 0c9d42c9e..2655836e2 100644 --- a/crates/uv-toolchain/src/toolchain.rs +++ b/crates/uv-toolchain/src/toolchain.rs @@ -178,6 +178,7 @@ impl Toolchain { }; let installed = InstalledToolchain::new(path)?; + installed.ensure_externally_managed()?; Ok(Self { source: ToolchainSource::Managed, diff --git a/crates/uv/src/commands/toolchain/install.rs b/crates/uv/src/commands/toolchain/install.rs index 0b9e5d1f7..1bcebf81a 100644 --- a/crates/uv/src/commands/toolchain/install.rs +++ b/crates/uv/src/commands/toolchain/install.rs @@ -5,7 +5,7 @@ use uv_client::Connectivity; use uv_configuration::PreviewMode; use uv_fs::Simplified; use uv_toolchain::downloads::{DownloadResult, PythonDownload, PythonDownloadRequest}; -use uv_toolchain::managed::InstalledToolchains; +use uv_toolchain::managed::{InstalledToolchain, InstalledToolchains}; use uv_toolchain::ToolchainRequest; use uv_warnings::warn_user; @@ -101,10 +101,13 @@ pub(crate) async fn install( DownloadResult::Fetched(path) => path, }; + let installed = InstalledToolchain::new(path)?; + installed.ensure_externally_managed()?; + writeln!( printer.stderr(), "Installed Python {version} to {}", - path.user_display() + installed.path().user_display() )?; Ok(ExitStatus::Success)