mirror of
https://github.com/astral-sh/uv.git
synced 2025-07-07 21:35:00 +00:00
Require --global
for removal of the global Python pin (#14169)
While reviewing https://github.com/astral-sh/uv/pull/14107, @oconnor663 pointed out a bug where we allow `uv python pin --rm` to delete the global pin without the `--global` flag. I think that shouldn't be allowed? I'm not 100% certain though.
This commit is contained in:
parent
e40357e529
commit
7ea7071939
5 changed files with 62 additions and 11 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4665,7 +4665,6 @@ dependencies = [
|
||||||
"uv-client",
|
"uv-client",
|
||||||
"uv-configuration",
|
"uv-configuration",
|
||||||
"uv-console",
|
"uv-console",
|
||||||
"uv-dirs",
|
|
||||||
"uv-dispatch",
|
"uv-dispatch",
|
||||||
"uv-distribution",
|
"uv-distribution",
|
||||||
"uv-distribution-filename",
|
"uv-distribution-filename",
|
||||||
|
|
|
@ -217,6 +217,19 @@ impl PythonVersionFile {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new representation of a global Python version file.
|
||||||
|
///
|
||||||
|
/// Returns [`None`] if the user configuration directory cannot be determined.
|
||||||
|
pub fn global() -> Option<Self> {
|
||||||
|
let path = user_uv_config_dir()?.join(PYTHON_VERSION_FILENAME);
|
||||||
|
Some(Self::new(path))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns `true` if the version file is a global version file.
|
||||||
|
pub fn is_global(&self) -> bool {
|
||||||
|
PythonVersionFile::global().is_some_and(|global| self.path() == global.path())
|
||||||
|
}
|
||||||
|
|
||||||
/// Return the first request declared in the file, if any.
|
/// Return the first request declared in the file, if any.
|
||||||
pub fn version(&self) -> Option<&PythonRequest> {
|
pub fn version(&self) -> Option<&PythonRequest> {
|
||||||
self.versions.first()
|
self.versions.first()
|
||||||
|
@ -260,6 +273,9 @@ impl PythonVersionFile {
|
||||||
/// Update the version file on the file system.
|
/// Update the version file on the file system.
|
||||||
pub async fn write(&self) -> Result<(), std::io::Error> {
|
pub async fn write(&self) -> Result<(), std::io::Error> {
|
||||||
debug!("Writing Python versions to `{}`", self.path.display());
|
debug!("Writing Python versions to `{}`", self.path.display());
|
||||||
|
if let Some(parent) = self.path.parent() {
|
||||||
|
fs_err::tokio::create_dir_all(parent).await?;
|
||||||
|
}
|
||||||
fs::tokio::write(
|
fs::tokio::write(
|
||||||
&self.path,
|
&self.path,
|
||||||
self.versions
|
self.versions
|
||||||
|
|
|
@ -24,7 +24,6 @@ uv-cli = { workspace = true }
|
||||||
uv-client = { workspace = true }
|
uv-client = { workspace = true }
|
||||||
uv-configuration = { workspace = true }
|
uv-configuration = { workspace = true }
|
||||||
uv-console = { workspace = true }
|
uv-console = { workspace = true }
|
||||||
uv-dirs = { workspace = true }
|
|
||||||
uv-dispatch = { workspace = true }
|
uv-dispatch = { workspace = true }
|
||||||
uv-distribution = { workspace = true }
|
uv-distribution = { workspace = true }
|
||||||
uv-distribution-filename = { workspace = true }
|
uv-distribution-filename = { workspace = true }
|
||||||
|
|
|
@ -9,7 +9,6 @@ use tracing::debug;
|
||||||
use uv_cache::Cache;
|
use uv_cache::Cache;
|
||||||
use uv_client::BaseClientBuilder;
|
use uv_client::BaseClientBuilder;
|
||||||
use uv_configuration::{DependencyGroupsWithDefaults, PreviewMode};
|
use uv_configuration::{DependencyGroupsWithDefaults, PreviewMode};
|
||||||
use uv_dirs::user_uv_config_dir;
|
|
||||||
use uv_fs::Simplified;
|
use uv_fs::Simplified;
|
||||||
use uv_python::{
|
use uv_python::{
|
||||||
EnvironmentPreference, PYTHON_VERSION_FILENAME, PythonDownloads, PythonInstallation,
|
EnvironmentPreference, PYTHON_VERSION_FILENAME, PythonDownloads, PythonInstallation,
|
||||||
|
@ -72,10 +71,20 @@ pub(crate) async fn pin(
|
||||||
}
|
}
|
||||||
bail!("No Python version file found");
|
bail!("No Python version file found");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if !global && file.is_global() {
|
||||||
|
bail!("No Python version file found; use `--rm --global` to remove the global pin");
|
||||||
|
}
|
||||||
|
|
||||||
fs_err::tokio::remove_file(file.path()).await?;
|
fs_err::tokio::remove_file(file.path()).await?;
|
||||||
writeln!(
|
writeln!(
|
||||||
printer.stdout(),
|
printer.stdout(),
|
||||||
"Removed Python version file at `{}`",
|
"Removed {} at `{}`",
|
||||||
|
if global {
|
||||||
|
"global Python pin"
|
||||||
|
} else {
|
||||||
|
"Python version file"
|
||||||
|
},
|
||||||
file.path().user_display()
|
file.path().user_display()
|
||||||
)?;
|
)?;
|
||||||
return Ok(ExitStatus::Success);
|
return Ok(ExitStatus::Success);
|
||||||
|
@ -192,12 +201,11 @@ pub(crate) async fn pin(
|
||||||
let existing = version_file.ok().flatten();
|
let existing = version_file.ok().flatten();
|
||||||
// TODO(zanieb): Allow updating the discovered version file with an `--update` flag.
|
// TODO(zanieb): Allow updating the discovered version file with an `--update` flag.
|
||||||
let new = if global {
|
let new = if global {
|
||||||
let Some(config_dir) = user_uv_config_dir() else {
|
let Some(new) = PythonVersionFile::global() else {
|
||||||
return Err(anyhow::anyhow!("No user-level config directory found."));
|
// TODO(zanieb): We should find a nice way to surface that as an error
|
||||||
|
bail!("Failed to determine directory for global Python pin");
|
||||||
};
|
};
|
||||||
fs_err::tokio::create_dir_all(&config_dir).await?;
|
new.with_versions(vec![request])
|
||||||
PythonVersionFile::new(config_dir.join(PYTHON_VERSION_FILENAME))
|
|
||||||
.with_versions(vec![request])
|
|
||||||
} else {
|
} else {
|
||||||
PythonVersionFile::new(project_dir.join(PYTHON_VERSION_FILENAME))
|
PythonVersionFile::new(project_dir.join(PYTHON_VERSION_FILENAME))
|
||||||
.with_versions(vec![request])
|
.with_versions(vec![request])
|
||||||
|
|
|
@ -855,7 +855,7 @@ fn python_pin_rm() {
|
||||||
error: No Python version file found
|
error: No Python version file found
|
||||||
");
|
");
|
||||||
|
|
||||||
// Remove the local pin
|
// Create and remove a local pin
|
||||||
context.python_pin().arg("3.12").assert().success();
|
context.python_pin().arg("3.12").assert().success();
|
||||||
uv_snapshot!(context.filters(), context.python_pin().arg("--rm"), @r"
|
uv_snapshot!(context.filters(), context.python_pin().arg("--rm"), @r"
|
||||||
success: true
|
success: true
|
||||||
|
@ -892,12 +892,41 @@ fn python_pin_rm() {
|
||||||
.arg("--global")
|
.arg("--global")
|
||||||
.assert()
|
.assert()
|
||||||
.success();
|
.success();
|
||||||
|
|
||||||
uv_snapshot!(context.filters(), context.python_pin().arg("--rm").arg("--global"), @r"
|
uv_snapshot!(context.filters(), context.python_pin().arg("--rm").arg("--global"), @r"
|
||||||
success: true
|
success: true
|
||||||
exit_code: 0
|
exit_code: 0
|
||||||
----- stdout -----
|
----- stdout -----
|
||||||
Removed Python version file at `[UV_USER_CONFIG_DIR]/.python-version`
|
Removed global Python pin at `[UV_USER_CONFIG_DIR]/.python-version`
|
||||||
|
|
||||||
----- stderr -----
|
----- stderr -----
|
||||||
");
|
");
|
||||||
|
|
||||||
|
// Add the global pin again
|
||||||
|
context
|
||||||
|
.python_pin()
|
||||||
|
.arg("3.12")
|
||||||
|
.arg("--global")
|
||||||
|
.assert()
|
||||||
|
.success();
|
||||||
|
|
||||||
|
// Remove the local pin
|
||||||
|
uv_snapshot!(context.filters(), context.python_pin().arg("--rm"), @r"
|
||||||
|
success: true
|
||||||
|
exit_code: 0
|
||||||
|
----- stdout -----
|
||||||
|
Removed Python version file at `.python-version`
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
");
|
||||||
|
|
||||||
|
// The global pin should not be removed without `--global`
|
||||||
|
uv_snapshot!(context.filters(), context.python_pin().arg("--rm"), @r"
|
||||||
|
success: false
|
||||||
|
exit_code: 2
|
||||||
|
----- stdout -----
|
||||||
|
|
||||||
|
----- stderr -----
|
||||||
|
error: No Python version file found; use `--rm --global` to remove the global pin
|
||||||
|
");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue