Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down
15 changes: 15 additions & 0 deletions crates/uv/src/commands/python/pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ 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>,
resolved: bool,
python_preference: PythonPreference,
no_project: bool,
global: bool,
rm: bool,
cache: &Cache,
printer: Printer,
) -> Result<ExitStatus> {
Expand Down Expand Up @@ -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 `{}`",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: since this is trailing we don't "need" the backticks (but it's more uniform with other print outs in this command to have them?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I actually tried "Removed Python version file: {}" but it was awkward since the common case is a version file in the working directory so I reverted it.

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? {
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1466,6 +1466,7 @@ async fn run(mut cli: Cli) -> Result<ExitStatus> {
globals.python_preference,
args.no_project,
args.global,
args.rm,
&cache,
printer,
)
Expand Down
3 changes: 3 additions & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -1068,13 +1069,15 @@ impl PythonPinSettings {
resolved,
no_project,
global,
rm,
} = args;

Self {
request,
resolved: flag(resolved, no_resolved).unwrap_or(false),
no_project,
global,
rm,
}
}
}
Expand Down
61 changes: 61 additions & 0 deletions crates/uv/tests/it/python_pin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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 -----
");
}
1 change: 1 addition & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading