Add uv python pin --rm to remove .python-version pins (#13860)

I realized it is non-trivial to delete the global Python version pin,
and that we were missing this simple functionality
This commit is contained in:
Zanie Blue 2025-06-05 12:05:42 -05:00 committed by GitHub
parent 789a246cf3
commit 062b6ab743
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 85 additions and 0 deletions

View file

@ -5032,6 +5032,10 @@ pub struct PythonPinArgs {
/// directory, this version will be used instead.
#[arg(long)]
pub global: bool,
/// Remove the Python version pin.
#[arg(long, conflicts_with = "request", conflicts_with = "resolved")]
pub rm: bool,
}
#[derive(Args)]

View file

@ -20,6 +20,7 @@ use crate::commands::{ExitStatus, project::find_requires_python};
use crate::printer::Printer;
/// Pin to a specific Python version.
#[allow(clippy::fn_params_excessive_bools)]
pub(crate) async fn pin(
project_dir: &Path,
request: Option<String>,
@ -27,6 +28,7 @@ pub(crate) async fn pin(
python_preference: PythonPreference,
no_project: bool,
global: bool,
rm: bool,
cache: &Cache,
printer: Printer,
) -> Result<ExitStatus> {
@ -56,6 +58,19 @@ pub(crate) async fn pin(
PythonVersionFile::discover(project_dir, &VersionFileDiscoveryOptions::default()).await
};
if rm {
let Some(file) = version_file? else {
bail!("No Python version file found");
};
fs_err::tokio::remove_file(file.path()).await?;
writeln!(
printer.stdout(),
"Removed Python version file at `{}`",
file.path().user_display()
)?;
return Ok(ExitStatus::Success);
}
let Some(request) = request else {
// Display the current pinned Python version
if let Some(file) = version_file? {

View file

@ -1466,6 +1466,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
globals.python_preference,
args.no_project,
args.global,
args.rm,
&cache,
printer,
)

View file

@ -1056,6 +1056,7 @@ pub(crate) struct PythonPinSettings {
pub(crate) resolved: bool,
pub(crate) no_project: bool,
pub(crate) global: bool,
pub(crate) rm: bool,
}
impl PythonPinSettings {
@ -1068,6 +1069,7 @@ impl PythonPinSettings {
resolved,
no_project,
global,
rm,
} = args;
Self {
@ -1075,6 +1077,7 @@ impl PythonPinSettings {
resolved: flag(resolved, no_resolved).unwrap_or(false),
no_project,
global,
rm,
}
}
}

View file

@ -2,6 +2,7 @@ use std::path::PathBuf;
use crate::common::{TestContext, uv_snapshot};
use anyhow::Result;
use assert_cmd::assert::OutputAssertExt;
use assert_fs::fixture::{FileWriteStr, PathChild, PathCreateDir};
use insta::assert_snapshot;
use uv_python::{
@ -814,3 +815,63 @@ fn python_pin_with_comments() -> Result<()> {
Ok(())
}
#[test]
fn python_pin_rm() {
let context: TestContext = TestContext::new_with_versions(&["3.12"]);
uv_snapshot!(context.filters(), context.python_pin().arg("--rm"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: No Python version file found
");
// Remove the local pin
context.python_pin().arg("3.12").assert().success();
uv_snapshot!(context.filters(), context.python_pin().arg("--rm"), @r"
success: true
exit_code: 0
----- stdout -----
Removed Python version file at `.python-version`
----- stderr -----
");
uv_snapshot!(context.filters(), context.python_pin().arg("--rm").arg("--global"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: No Python version file found
");
// Global does not detect the local pin
context.python_pin().arg("3.12").assert().success();
uv_snapshot!(context.filters(), context.python_pin().arg("--rm").arg("--global"), @r"
success: false
exit_code: 2
----- stdout -----
----- stderr -----
error: No Python version file found
");
context
.python_pin()
.arg("3.12")
.arg("--global")
.assert()
.success();
uv_snapshot!(context.filters(), context.python_pin().arg("--rm").arg("--global"), @r"
success: true
exit_code: 0
----- stdout -----
Removed Python version file at `[UV_USER_CONFIG_DIR]/.python-version`
----- stderr -----
");
}

View file

@ -2904,6 +2904,7 @@ uv python pin [OPTIONS] [REQUEST]
</dd><dt id="uv-python-pin--resolved"><a href="#uv-python-pin--resolved"><code>--resolved</code></a></dt><dd><p>Write the resolved Python interpreter path instead of the request.</p>
<p>Ensures that the exact same interpreter is used.</p>
<p>This option is usually not safe to use when committing the <code>.python-version</code> file to version control.</p>
</dd><dt id="uv-python-pin--rm"><a href="#uv-python-pin--rm"><code>--rm</code></a></dt><dd><p>Remove the Python version pin</p>
</dd><dt id="uv-python-pin--verbose"><a href="#uv-python-pin--verbose"><code>--verbose</code></a>, <code>-v</code></dt><dd><p>Use verbose output.</p>
<p>You can configure fine-grained logging using the <code>RUST_LOG</code> environment variable. (<a href="https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives">https://docs.rs/tracing-subscriber/latest/tracing_subscriber/filter/struct.EnvFilter.html#directives</a>)</p>
</dd></dl>