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::target::Target;
pub use crate::version_files::{
request_from_version_file, requests_from_version_file, PYTHON_VERSIONS_FILENAME,
PYTHON_VERSION_FILENAME,
request_from_version_file, requests_from_version_file, write_version_file,
PYTHON_VERSIONS_FILENAME, PYTHON_VERSION_FILENAME,
};
pub use crate::virtualenv::{Error as VirtualEnvError, PyVenvConfiguration, VirtualEnvironment};
mod discovery;
pub mod downloads;
mod environment;
@ -83,19 +84,19 @@ pub enum Error {
// TODO(zanieb): We should write a mock interpreter script that works on Windows
#[cfg(all(test, unix))]
mod tests {
use anyhow::Result;
use indoc::{formatdoc, indoc};
use std::{
env,
ffi::{OsStr, OsString},
path::{Path, PathBuf},
str::FromStr,
};
use anyhow::Result;
use assert_fs::{fixture::ChildPath, prelude::*, TempDir};
use indoc::{formatdoc, indoc};
use temp_env::with_vars;
use test_log::test;
use assert_fs::{fixture::ChildPath, prelude::*, TempDir};
use uv_cache::Cache;
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> {
match fs::tokio::read_to_string(PYTHON_VERSIONS_FILENAME).await {
Ok(content) => {

View file

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

View file

@ -28,7 +28,7 @@ fn python_pin() {
success: true
exit_code: 0
----- stdout -----
Pinned to `any`
Pinned `.python-version` to `any`
----- stderr -----
"###);
@ -57,7 +57,7 @@ fn python_pin() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `3.12`
Updated `.python-version` from `any` -> `3.12`
----- stderr -----
"###);
@ -73,7 +73,7 @@ fn python_pin() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `3.11`
Updated `.python-version` from `3.12` -> `3.11`
----- stderr -----
"###);
@ -89,7 +89,7 @@ fn python_pin() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `cpython`
Updated `.python-version` from `3.11` -> `cpython`
----- stderr -----
"###);
@ -105,7 +105,7 @@ fn python_pin() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `cpython@3.12`
Updated `.python-version` from `cpython` -> `cpython@3.12`
----- stderr -----
"###);
@ -121,7 +121,7 @@ fn python_pin() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `cpython@3.12`
Pinned `.python-version` to `cpython@3.12`
----- stderr -----
"###);
@ -137,7 +137,7 @@ fn python_pin() {
success: true
exit_code: 0
----- 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 -----
"###);
@ -153,7 +153,7 @@ fn python_pin() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `[PYTHON-3.11]`
Updated `.python-version` from `cpython-3.12-any-any-any` -> `[PYTHON-3.11]`
----- stderr -----
"###);
@ -176,7 +176,7 @@ fn python_pin() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `pypy`
Updated `.python-version` from `[PYTHON-3.11]` -> `pypy`
----- stderr -----
warning: No interpreter found for PyPy in system path
@ -197,7 +197,7 @@ fn python_pin() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `3.7`
Updated `.python-version` from `pypy` -> `3.7`
----- stderr -----
warning: No interpreter found for Python 3.7 in system path
@ -222,7 +222,7 @@ fn python_pin_no_python() {
success: true
exit_code: 0
----- stdout -----
Pinned to `3.12`
Pinned `.python-version` to `3.12`
----- stderr -----
warning: No interpreter found for Python 3.12 in system path
@ -264,7 +264,7 @@ fn python_pin_resolve() {
success: true
exit_code: 0
----- stdout -----
Pinned to `[PYTHON-3.11]`
Pinned `.python-version` to `[PYTHON-3.11]`
----- stderr -----
"###);
@ -284,7 +284,7 @@ fn python_pin_resolve() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `[PYTHON-3.12]`
Updated `.python-version` from `[PYTHON-3.11]` -> `[PYTHON-3.12]`
----- stderr -----
"###);
@ -304,7 +304,7 @@ fn python_pin_resolve() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `[PYTHON-3.11]`
Updated `.python-version` from `[PYTHON-3.12]` -> `[PYTHON-3.11]`
----- stderr -----
"###);
@ -324,7 +324,7 @@ fn python_pin_resolve() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `[PYTHON-3.11]`
Pinned `.python-version` to `[PYTHON-3.11]`
----- stderr -----
"###);
@ -344,7 +344,7 @@ fn python_pin_resolve() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `[PYTHON-3.12]`
Updated `.python-version` from `[PYTHON-3.11]` -> `[PYTHON-3.12]`
----- stderr -----
"###);
@ -364,7 +364,7 @@ fn python_pin_resolve() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `[PYTHON-3.12]`
Pinned `.python-version` to `[PYTHON-3.12]`
----- stderr -----
"###);
@ -389,7 +389,7 @@ fn python_pin_resolve() {
success: true
exit_code: 0
----- stdout -----
Replaced existing pin with `[PYTHON-3.12]`
Pinned `.python-version` to `[PYTHON-3.12]`
----- stderr -----
"###);