← Whispering

fix: install.sh pip detection - use $PYTHON_CMD, handle active venvs

a1d217fb53f8546cea3cab2e923141271ab48adf · Claude · Feb 10, 2026 17:53

- Detect python/python3 command dynamically instead of hardcoding python3
- Move pip check to after venv activation (fixes "pip not found" when
  running inside conda/venv where python3 doesn't have pip but python does)
- Auto-install pip via ensurepip if missing in the environment
- Skip venv creation if VIRTUAL_ENV or CONDA_PREFIX is already set
- Use $PYTHON_CMD -m pip consistently instead of bare pip commands

https://claude.ai/code/session_01965m1v3f1cK6gDV1JHioA8

+67 −39 · 1 file changed

  • scripts/install.sh +67 −39
diff --git a/scripts/install.sh b/scripts/install.sh
index 95cb426..438cbe6 100755
--- a/scripts/install.sh
+++ b/scripts/install.sh
@@ -42,14 +42,23 @@ for arg in "$@"; do
     esac
 done
 
-# Check Python version
-echo -e "${YELLOW}Checking Python version...${NC}"
-if ! command -v python3 &> /dev/null; then
-    echo -e "${RED}Error: python3 not found. Please install Python 3.8 or higher.${NC}"
+# Detect a usable Python command (python3, python, or whatever the venv exposes)
+PYTHON_CMD=""
+for cmd in python3 python; do
+    if command -v "$cmd" &> /dev/null; then
+        PYTHON_CMD="$cmd"
+        break
+    fi
+done
+
+if [ -z "$PYTHON_CMD" ]; then
+    echo -e "${RED}Error: python3/python not found. Please install Python 3.8 or higher.${NC}"
     exit 1
 fi
 
-PYTHON_VERSION=$(python3 --version | cut -d' ' -f2)
+# Check Python version
+echo -e "${YELLOW}Checking Python version...${NC}"
+PYTHON_VERSION=$($PYTHON_CMD --version | cut -d' ' -f2)
 PYTHON_MAJOR=$(echo "$PYTHON_VERSION" | cut -d'.' -f1)
 PYTHON_MINOR=$(echo "$PYTHON_VERSION" | cut -d'.' -f2)
 
@@ -58,44 +67,63 @@ if [ "$PYTHON_MAJOR" -lt 3 ] || ([ "$PYTHON_MAJOR" -eq 3 ] && [ "$PYTHON_MINOR"
     exit 1
 fi
 
-echo -e "${GREEN}✓ Python $PYTHON_VERSION found${NC}"
-echo
-
-# Check for pip
-echo -e "${YELLOW}Checking for pip...${NC}"
-if ! python3 -m pip --version &> /dev/null; then
-    echo -e "${RED}Error: pip not found. Please install pip first.${NC}"
-    exit 1
-fi
-echo -e "${GREEN}✓ pip found${NC}"
+echo -e "${GREEN}✓ Python $PYTHON_VERSION found (${PYTHON_CMD})${NC}"
 echo
 
-# Create virtual environment
-echo -e "${YELLOW}Creating virtual environment...${NC}"
-if [ -d ".venv" ]; then
-    echo -e "${BLUE}Virtual environment already exists. Skipping creation.${NC}"
+# Create virtual environment (skip if one is already active)
+if [ -n "$VIRTUAL_ENV" ]; then
+    echo -e "${BLUE}Virtual environment already active: $VIRTUAL_ENV${NC}"
+elif [ -n "$CONDA_PREFIX" ]; then
+    echo -e "${BLUE}Conda environment already active: $CONDA_PREFIX${NC}"
 else
-    python3 -m venv .venv
-    echo -e "${GREEN}✓ Virtual environment created${NC}"
+    echo -e "${YELLOW}Creating virtual environment...${NC}"
+    if [ -d ".venv" ]; then
+        echo -e "${BLUE}Virtual environment already exists. Activating it.${NC}"
+    else
+        $PYTHON_CMD -m venv .venv
+        echo -e "${GREEN}✓ Virtual environment created${NC}"
+    fi
+    echo
+
+    # Activate virtual environment
+    echo -e "${YELLOW}Activating virtual environment...${NC}"
+    source .venv/bin/activate
+    echo -e "${GREEN}✓ Virtual environment activated${NC}"
 fi
 echo
 
-# Activate virtual environment
-echo -e "${YELLOW}Activating virtual environment...${NC}"
-source .venv/bin/activate
-echo -e "${GREEN}✓ Virtual environment activated${NC}"
+# After venv is active, prefer 'python' (the venv binary)
+for cmd in python python3; do
+    if command -v "$cmd" &> /dev/null; then
+        PYTHON_CMD="$cmd"
+        break
+    fi
+done
+
+# Check for pip (now inside the venv)
+echo -e "${YELLOW}Checking for pip...${NC}"
+if ! $PYTHON_CMD -m pip --version &> /dev/null; then
+    echo -e "${YELLOW}pip not found in environment. Installing pip...${NC}"
+    $PYTHON_CMD -m ensurepip --default-pip 2>/dev/null || {
+        echo -e "${RED}Error: Could not install pip. Please install pip manually:${NC}"
+        echo -e "${RED}  $PYTHON_CMD -m ensurepip --default-pip${NC}"
+        echo -e "${RED}  or: curl -sS https://bootstrap.pypa.io/get-pip.py | $PYTHON_CMD${NC}"
+        exit 1
+    }
+fi
+echo -e "${GREEN}✓ pip found${NC}"
 echo
 
 # Upgrade pip
 echo -e "${YELLOW}Upgrading pip...${NC}"
-python -m pip install --upgrade pip --quiet
+$PYTHON_CMD -m pip install --upgrade pip --quiet
 echo -e "${GREEN}✓ pip upgraded${NC}"
 echo
 
 # Install base requirements
 echo -e "${YELLOW}Installing base requirements...${NC}"
 echo -e "${BLUE}This may take a few minutes...${NC}"
-pip install -r requirements.txt
+$PYTHON_CMD -m pip install -r requirements.txt
 echo -e "${GREEN}✓ Base requirements installed${NC}"
 echo
 
@@ -111,7 +139,7 @@ if command -v nvidia-smi &> /dev/null; then
     read -r response
     if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
         echo -e "${YELLOW}Installing CUDA libraries...${NC}"
-        pip install nvidia-cublas-cu12 nvidia-cudnn-cu12
+        $PYTHON_CMD -m pip install nvidia-cublas-cu12 nvidia-cudnn-cu12
         echo -e "${GREEN}✓ CUDA libraries installed${NC}"
     else
         echo -e "${BLUE}Skipping CUDA libraries. You can install them later with:${NC}"
@@ -166,14 +194,14 @@ install_chatterbox() {
     fi
 
     # Install chatterbox without dependencies (deps already in requirements.txt)
-    pip install chatterbox-tts --no-deps 2>/dev/null && {
+    $PYTHON_CMD -m pip install chatterbox-tts --no-deps 2>/dev/null && {
         echo -e "${GREEN}✓ Chatterbox TTS installed${NC}"
     } || {
         echo -e "${YELLOW}pip install failed. Trying source installation...${NC}"
         # Fallback: clone and install from source
         TEMP_DIR=$(mktemp -d)
         git clone --depth 1 https://github.com/resemble-ai/chatterbox "$TEMP_DIR/chatterbox" 2>/dev/null && {
-            pip install --no-deps -e "$TEMP_DIR/chatterbox" 2>/dev/null && {
+            $PYTHON_CMD -m pip install --no-deps -e "$TEMP_DIR/chatterbox" 2>/dev/null && {
                 echo -e "${GREEN}✓ Chatterbox TTS installed from source${NC}"
             } || {
                 # Last resort: copy the module directly
@@ -182,7 +210,7 @@ install_chatterbox() {
                     echo -e "${GREEN}✓ Chatterbox TTS module copied to src/${NC}"
                 else
                     echo -e "${RED}✗ Failed to install Chatterbox TTS${NC}"
-                    echo -e "${BLUE}  You can try manually: pip install chatterbox-tts --no-deps${NC}"
+                    echo -e "${BLUE}  You can try manually: $PYTHON_CMD -m pip install chatterbox-tts --no-deps${NC}"
                 fi
             }
         } || {
@@ -192,7 +220,7 @@ install_chatterbox() {
     }
 
     # Verify installation
-    python -c "from chatterbox.tts import ChatterboxTTS; print('  ✓ Chatterbox import successful')" 2>/dev/null || {
+    $PYTHON_CMD -c "from chatterbox.tts import ChatterboxTTS; print('  ✓ Chatterbox import successful')" 2>/dev/null || {
         echo -e "${YELLOW}  Note: Chatterbox import test failed. It may still work at runtime.${NC}"
     }
     echo
@@ -200,17 +228,17 @@ install_chatterbox() {
 
 install_qwen3() {
     echo -e "${YELLOW}Installing Qwen3-TTS...${NC}"
-    pip install qwen-tts 2>/dev/null && {
+    $PYTHON_CMD -m pip install qwen-tts 2>/dev/null && {
         echo -e "${GREEN}✓ Qwen3-TTS installed${NC}"
     } || {
         echo -e "${YELLOW}pip install failed. Trying from source...${NC}"
         TEMP_DIR=$(mktemp -d)
         git clone --depth 1 https://github.com/QwenLM/Qwen3-TTS.git "$TEMP_DIR/qwen3-tts" 2>/dev/null && {
-            pip install -e "$TEMP_DIR/qwen3-tts" 2>/dev/null && {
+            $PYTHON_CMD -m pip install -e "$TEMP_DIR/qwen3-tts" 2>/dev/null && {
                 echo -e "${GREEN}✓ Qwen3-TTS installed from source${NC}"
             } || {
                 echo -e "${RED}✗ Failed to install Qwen3-TTS${NC}"
-                echo -e "${BLUE}  You can try manually: pip install qwen-tts${NC}"
+                echo -e "${BLUE}  You can try manually: $PYTHON_CMD -m pip install qwen-tts${NC}"
             }
         } || {
             echo -e "${RED}✗ Failed to clone Qwen3-TTS repository${NC}"
@@ -224,7 +252,7 @@ install_qwen3() {
     read -r flash_response
     if [[ "$flash_response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
         echo -e "${YELLOW}Installing flash-attn...${NC}"
-        MAX_JOBS=4 pip install -U flash-attn --no-build-isolation 2>/dev/null && {
+        MAX_JOBS=4 $PYTHON_CMD -m pip install -U flash-attn --no-build-isolation 2>/dev/null && {
             echo -e "${GREEN}✓ flash-attn installed${NC}"
         } || {
             echo -e "${YELLOW}  flash-attn installation failed (optional - TTS will still work)${NC}"
@@ -232,7 +260,7 @@ install_qwen3() {
     fi
 
     # Verify installation
-    python -c "from qwen_tts import Qwen3TTSModel; print('  ✓ Qwen3-TTS import successful')" 2>/dev/null || {
+    $PYTHON_CMD -c "from qwen_tts import Qwen3TTSModel; print('  ✓ Qwen3-TTS import successful')" 2>/dev/null || {
         echo -e "${YELLOW}  Note: Qwen3-TTS import test failed. It may still work at runtime.${NC}"
     }
     echo
@@ -297,7 +325,7 @@ fi
 
 # Verify TTS status
 echo -e "${YELLOW}Checking TTS backend status...${NC}"
-python -c "
+cd "$PROJECT_DIR/src" && $PYTHON_CMD -c "
 from tts_provider import get_available_backends
 backends = get_available_backends()
 for name, avail in backends.items():
@@ -305,7 +333,7 @@ for name, avail in backends.items():
     print(f'  {name}: {status}')
 if not any(backends.values()):
     print('  No TTS backends available. Install with: ./scripts/install.sh --tts')
-" 2>/dev/null || echo -e "${YELLOW}  Could not check TTS status (run from src/ directory)${NC}"
+" 2>/dev/null && cd "$PROJECT_DIR" || { cd "$PROJECT_DIR"; echo -e "${YELLOW}  Could not check TTS status${NC}"; }
 echo
 
 # Installation complete