Add EXTERNALLY-MANAGED markers to managed toolchains (#4312)

Closes #4240 

e.g.

```
❯ cargo run -q -- pip install anyio --python "/Users/zb/Library/Application Support/uv/toolchains/cpython-3.12.0-macos-aarch64-none/install/bin/python3"
error: The interpreter at /Users/zb/Library/Application Support/uv/toolchains/cpython-3.12.0-macos-aarch64-none/install is externally managed, and indicates the following:

  This toolchain is managed by uv and should not be modified.

Consider creating a virtual environment with `uv venv`.
```
This commit is contained in:
Zanie Blue 2024-06-17 11:25:34 -04:00 committed by GitHub
parent e264637b63
commit 67f1285ce3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 30 additions and 3 deletions

View file

@ -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!(

View file

@ -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.

View file

@ -178,6 +178,7 @@ impl Toolchain {
};
let installed = InstalledToolchain::new(path)?;
installed.ensure_externally_managed()?;
Ok(Self {
source: ToolchainSource::Managed,

View file

@ -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)