fix(pip3): prefer python when available (#471)
This commit is contained in:
@@ -15,7 +15,7 @@ use crate::command::{CommandExt, Utf8Output};
|
||||
use crate::execution_context::ExecutionContext;
|
||||
use crate::executor::ExecutorOutput;
|
||||
use crate::terminal::{print_separator, shell};
|
||||
use crate::utils::{self, require, require_option, which, PathExt, REQUIRE_SUDO};
|
||||
use crate::utils::{self, check_is_python_2_or_shim, require, require_option, which, PathExt, REQUIRE_SUDO};
|
||||
use crate::Step;
|
||||
use crate::HOME_DIR;
|
||||
use crate::{
|
||||
@@ -368,7 +368,18 @@ pub fn run_mamba_update(ctx: &ExecutionContext) -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn run_pip3_update(ctx: &ExecutionContext) -> Result<()> {
|
||||
let python3 = require("python3")?;
|
||||
let py = require("python").and_then(check_is_python_2_or_shim);
|
||||
let py3 = require("python3").and_then(check_is_python_2_or_shim);
|
||||
|
||||
let python3 = match (py, py3) {
|
||||
// prefer `python` if it is available and is a valid Python 3.
|
||||
(Ok(py), _) => py,
|
||||
(Err(_), Ok(py3)) => py3,
|
||||
(Err(py_err), Err(py3_err)) => {
|
||||
return Err(SkipStep(format!("Skip due to following reasons: {} {}", py_err, py3_err)).into());
|
||||
}
|
||||
};
|
||||
|
||||
Command::new(&python3)
|
||||
.args(["-m", "pip"])
|
||||
.output_checked_utf8()
|
||||
|
||||
40
src/utils.rs
40
src/utils.rs
@@ -2,10 +2,12 @@ use std::env;
|
||||
use std::ffi::OsStr;
|
||||
use std::fmt::Debug;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use color_eyre::eyre::Result;
|
||||
use tracing::{debug, error};
|
||||
|
||||
use crate::command::CommandExt;
|
||||
use crate::error::SkipStep;
|
||||
|
||||
pub trait PathExt
|
||||
@@ -152,9 +154,6 @@ pub fn hostname() -> Result<String> {
|
||||
|
||||
#[cfg(target_family = "windows")]
|
||||
pub fn hostname() -> Result<String> {
|
||||
use crate::command::CommandExt;
|
||||
use std::process::Command;
|
||||
|
||||
Command::new("hostname")
|
||||
.output_checked_utf8()
|
||||
.map_err(|err| SkipStep(format!("Failed to get hostname: {err}")).into())
|
||||
@@ -217,3 +216,38 @@ pub mod merge_strategies {
|
||||
// Skip causes
|
||||
// TODO: Put them in a better place when we have more of them
|
||||
pub const REQUIRE_SUDO: &str = "Require sudo or counterpart but not found, skip";
|
||||
|
||||
/// Return `Err(SkipStep)` if `python` is a Python 2 or shim.
|
||||
///
|
||||
/// # Shim
|
||||
/// On Windows, if you install `python` through `winget`, an actual `python`
|
||||
/// is installed as well as a `python3` shim. Shim is invokable, but when you
|
||||
/// execute it, the Microsoft App Store will be launched instead of a Python
|
||||
/// shell.
|
||||
///
|
||||
/// We do this check through `python -V`, a shim will just give `Python` with
|
||||
/// no version number.
|
||||
pub fn check_is_python_2_or_shim(python: PathBuf) -> Result<PathBuf> {
|
||||
let output = Command::new(&python).arg("-V").output_checked_utf8()?;
|
||||
// "Python x.x.x\n"
|
||||
let stdout = output.stdout;
|
||||
// ["Python"] or ["Python", "x.x.x"], the newline char is trimmed.
|
||||
let mut split = stdout.split_whitespace();
|
||||
|
||||
if let Some(version) = split.nth(1) {
|
||||
let major_version = version
|
||||
.split('.')
|
||||
.next()
|
||||
.expect("Should have a major version number")
|
||||
.parse::<u32>()
|
||||
.expect("Major version should be a valid number");
|
||||
if major_version == 2 {
|
||||
return Err(SkipStep(format!("{} is a Python 2, skip.", python.display())).into());
|
||||
}
|
||||
} else {
|
||||
// No version number, is a shim
|
||||
return Err(SkipStep(format!("{} is a Python shim, skip.", python.display())).into());
|
||||
}
|
||||
|
||||
Ok(python)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user