Add color to python pin CLI (#5215)

## Summary

![Screenshot 2024-07-19 at 9 03
10 AM](https://github.com/user-attachments/assets/5668bd23-3f09-4964-bc09-9f3788f5a841)
This commit is contained in:
Charlie Marsh 2024-07-19 13:19:28 -04:00 committed by GitHub
parent 12dd450a8e
commit 7762d78281
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 61 additions and 43 deletions

View file

@ -14,10 +14,11 @@ pub use crate::prefix::Prefix;
pub use crate::python_version::PythonVersion; pub use crate::python_version::PythonVersion;
pub use crate::target::Target; pub use crate::target::Target;
pub use crate::version_files::{ pub use crate::version_files::{
request_from_version_file, requests_from_version_file, PYTHON_VERSIONS_FILENAME, request_from_version_file, requests_from_version_file, write_version_file,
PYTHON_VERSION_FILENAME, PYTHON_VERSIONS_FILENAME, PYTHON_VERSION_FILENAME,
}; };
pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment}; pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment};
mod discovery; mod discovery;
pub mod downloads; pub mod downloads;
mod environment; mod environment;
@ -83,19 +84,19 @@ pub enum Error {
// TODO(zanieb): We should write a mock interpreter script that works on Windows // TODO(zanieb): We should write a mock interpreter script that works on Windows
#[cfg(all(test, unix))] #[cfg(all(test, unix))]
mod tests { mod tests {
use anyhow::Result;
use indoc::{formatdoc, indoc};
use std::{ use std::{
env, env,
ffi::{OsStr, OsString}, ffi::{OsStr, OsString},
path::{Path, PathBuf}, path::{Path, PathBuf},
str::FromStr, str::FromStr,
}; };
use anyhow::Result;
use assert_fs::{fixture::ChildPath, prelude::*, TempDir};
use indoc::{formatdoc, indoc};
use temp_env::with_vars; use temp_env::with_vars;
use test_log::test; use test_log::test;
use assert_fs::{fixture::ChildPath, prelude::*, TempDir};
use uv_cache::Cache; use uv_cache::Cache;
use crate::{ use crate::{

View file

@ -46,6 +46,12 @@ pub async fn request_from_version_file() -> Result<Option<PythonRequest>, std::i
} }
} }
/// Write a version to a .`python-version` file.
pub async fn write_version_file(version: &str) -> Result<(), std::io::Error> {
debug!("Writing Python version `{version}` to `{PYTHON_VERSION_FILENAME}`");
fs::tokio::write(PYTHON_VERSION_FILENAME, format!("{version}\n")).await
}
async fn read_versions_file() -> Result<Option<Vec<String>>, std::io::Error> { async fn read_versions_file() -> Result<Option<Vec<String>>, std::io::Error> {
match fs::tokio::read_to_string(PYTHON_VERSIONS_FILENAME).await { match fs::tokio::read_to_string(PYTHON_VERSIONS_FILENAME).await {
Ok(content) => { Ok(content) => {

View file

@ -1,15 +1,15 @@
use std::fmt::Write; use std::fmt::Write;
use std::path::PathBuf;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use owo_colors::OwoColorize;
use tracing::debug;
use uv_cache::Cache; use uv_cache::Cache;
use uv_configuration::PreviewMode; use uv_configuration::PreviewMode;
use uv_fs::Simplified; use uv_fs::Simplified;
use uv_python::{ use uv_python::{
requests_from_version_file, EnvironmentPreference, PythonInstallation, PythonPreference, request_from_version_file, requests_from_version_file, write_version_file,
PythonRequest, PYTHON_VERSION_FILENAME, EnvironmentPreference, PythonInstallation, PythonPreference, PythonRequest,
PYTHON_VERSION_FILENAME,
}; };
use uv_warnings::warn_user_once; use uv_warnings::warn_user_once;
@ -50,7 +50,7 @@ pub(crate) async fn pin(
Ok(python) => Some(python), Ok(python) => Some(python),
// If no matching Python version is found, don't fail unless `resolved` was requested // If no matching Python version is found, don't fail unless `resolved` was requested
Err(uv_python::Error::MissingPython(err)) if !resolved => { Err(uv_python::Error::MissingPython(err)) if !resolved => {
warn_user_once!("{}", err); warn_user_once!("{err}");
None None
} }
Err(err) => return Err(err.into()), Err(err) => return Err(err.into()),
@ -68,16 +68,27 @@ pub(crate) async fn pin(
request.to_canonical_string() request.to_canonical_string()
}; };
debug!("Using pin `{}`", output); let existing = request_from_version_file().await.ok().flatten();
let version_file = PathBuf::from(PYTHON_VERSION_FILENAME); write_version_file(&output).await?;
let exists = version_file.exists();
debug!("Writing pin to {}", version_file.user_display()); if let Some(existing) = existing
fs_err::write(&version_file, format!("{output}\n"))?; .map(|existing| existing.to_canonical_string())
if exists { .filter(|existing| existing != &output)
writeln!(printer.stdout(), "Replaced existing pin with `{output}`")?; {
writeln!(
printer.stdout(),
"Updated `{}` from `{}` -> `{}`",
PYTHON_VERSION_FILENAME.cyan(),
existing.green(),
output.green()
)?;
} else { } else {
writeln!(printer.stdout(), "Pinned to `{output}`")?; writeln!(
printer.stdout(),
"Pinned `{}` to `{}`",
PYTHON_VERSION_FILENAME.cyan(),
output.green()
)?;
} }
Ok(ExitStatus::Success) Ok(ExitStatus::Success)

View file

@ -28,7 +28,7 @@ fn python_pin() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Pinned to `any` Pinned `.python-version` to `any`
----- stderr ----- ----- stderr -----
"###); "###);
@ -57,7 +57,7 @@ fn python_pin() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `3.12` Updated `.python-version` from `any` -> `3.12`
----- stderr ----- ----- stderr -----
"###); "###);
@ -73,7 +73,7 @@ fn python_pin() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `3.11` Updated `.python-version` from `3.12` -> `3.11`
----- stderr ----- ----- stderr -----
"###); "###);
@ -89,7 +89,7 @@ fn python_pin() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `cpython` Updated `.python-version` from `3.11` -> `cpython`
----- stderr ----- ----- stderr -----
"###); "###);
@ -105,7 +105,7 @@ fn python_pin() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `cpython@3.12` Updated `.python-version` from `cpython` -> `cpython@3.12`
----- stderr ----- ----- stderr -----
"###); "###);
@ -121,7 +121,7 @@ fn python_pin() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `cpython@3.12` Pinned `.python-version` to `cpython@3.12`
----- stderr ----- ----- stderr -----
"###); "###);
@ -137,7 +137,7 @@ fn python_pin() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `cpython-3.12-any-any-any` Updated `.python-version` from `cpython@3.12` -> `cpython-3.12-any-any-any`
----- stderr ----- ----- stderr -----
"###); "###);
@ -153,7 +153,7 @@ fn python_pin() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `[PYTHON-3.11]` Updated `.python-version` from `cpython-3.12-any-any-any` -> `[PYTHON-3.11]`
----- stderr ----- ----- stderr -----
"###); "###);
@ -176,7 +176,7 @@ fn python_pin() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `pypy` Updated `.python-version` from `[PYTHON-3.11]` -> `pypy`
----- stderr ----- ----- stderr -----
warning: No interpreter found for PyPy in system path warning: No interpreter found for PyPy in system path
@ -194,14 +194,14 @@ fn python_pin() {
#[cfg(unix)] #[cfg(unix)]
{ {
uv_snapshot!(context.filters(), context.python_pin().arg("3.7"), @r###" uv_snapshot!(context.filters(), context.python_pin().arg("3.7"), @r###"
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `3.7` Updated `.python-version` from `pypy` -> `3.7`
----- stderr ----- ----- stderr -----
warning: No interpreter found for Python 3.7 in system path warning: No interpreter found for Python 3.7 in system path
"###); "###);
let python_version = let python_version =
fs_err::read_to_string(context.temp_dir.join(PYTHON_VERSION_FILENAME)).unwrap(); fs_err::read_to_string(context.temp_dir.join(PYTHON_VERSION_FILENAME)).unwrap();
@ -222,7 +222,7 @@ fn python_pin_no_python() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Pinned to `3.12` Pinned `.python-version` to `3.12`
----- stderr ----- ----- stderr -----
warning: No interpreter found for Python 3.12 in system path warning: No interpreter found for Python 3.12 in system path
@ -264,7 +264,7 @@ fn python_pin_resolve() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Pinned to `[PYTHON-3.11]` Pinned `.python-version` to `[PYTHON-3.11]`
----- stderr ----- ----- stderr -----
"###); "###);
@ -284,7 +284,7 @@ fn python_pin_resolve() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `[PYTHON-3.12]` Updated `.python-version` from `[PYTHON-3.11]` -> `[PYTHON-3.12]`
----- stderr ----- ----- stderr -----
"###); "###);
@ -304,7 +304,7 @@ fn python_pin_resolve() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `[PYTHON-3.11]` Updated `.python-version` from `[PYTHON-3.12]` -> `[PYTHON-3.11]`
----- stderr ----- ----- stderr -----
"###); "###);
@ -324,7 +324,7 @@ fn python_pin_resolve() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `[PYTHON-3.11]` Pinned `.python-version` to `[PYTHON-3.11]`
----- stderr ----- ----- stderr -----
"###); "###);
@ -344,7 +344,7 @@ fn python_pin_resolve() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `[PYTHON-3.12]` Updated `.python-version` from `[PYTHON-3.11]` -> `[PYTHON-3.12]`
----- stderr ----- ----- stderr -----
"###); "###);
@ -364,7 +364,7 @@ fn python_pin_resolve() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `[PYTHON-3.12]` Pinned `.python-version` to `[PYTHON-3.12]`
----- stderr ----- ----- stderr -----
"###); "###);
@ -389,7 +389,7 @@ fn python_pin_resolve() {
success: true success: true
exit_code: 0 exit_code: 0
----- stdout ----- ----- stdout -----
Replaced existing pin with `[PYTHON-3.12]` Pinned `.python-version` to `[PYTHON-3.12]`
----- stderr ----- ----- stderr -----
"###); "###);