import { spawn } from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; /** * Python路径检测工具 * 支持在Windows、macOS、Linux等多个系统上检测Python */ let cachedPythonPath: string | null = null; /** * 获取系统中可用的Python可执行文件路径 * 优先级: * 1. 环境变量 PYTHON_PATH * 2. 环境变量 FUNASR_PYTHON_PATH * 3. Windows特定位置 * 4. 系统PATH中的python3/python */ export function getPythonPath(): string { // 如果已缓存,直接返回 if (cachedPythonPath) { return cachedPythonPath; } // 1. 检查环境变量 if (process.env.PYTHON_PATH && fs.existsSync(process.env.PYTHON_PATH)) { cachedPythonPath = process.env.PYTHON_PATH; return cachedPythonPath; } if (process.env.FUNASR_PYTHON_PATH && fs.existsSync(process.env.FUNASR_PYTHON_PATH)) { cachedPythonPath = process.env.FUNASR_PYTHON_PATH; return cachedPythonPath; } // 2. Windows特定位置 if (process.platform === 'win32') { const windowsPythonPaths = [ // 微软应用商店安装 path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Python', 'Python312', 'python.exe'), path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Python', 'Python311', 'python.exe'), path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Python', 'Python310', 'python.exe'), path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Python', 'Python39', 'python.exe'), path.join(process.env.LOCALAPPDATA || '', 'Programs', 'Python', 'Python38', 'python.exe'), // 直接安装 'C:\\Python312\\python.exe', 'C:\\Python311\\python.exe', 'C:\\Python310\\python.exe', 'C:\\Python39\\python.exe', 'C:\\Python38\\python.exe', // Anaconda/Miniconda path.join(process.env.USERPROFILE || '', 'anaconda3', 'python.exe'), path.join(process.env.USERPROFILE || '', 'miniconda3', 'python.exe'), // WSL path.join(process.env.USERPROFILE || '', 'AppData', 'Local', 'Microsoft', 'WindowsApps', 'python.exe'), ]; for (const pythonPath of windowsPythonPaths) { if (fs.existsSync(pythonPath)) { cachedPythonPath = pythonPath; return cachedPythonPath; } } } // 3. macOS/Linux 特定位置 if (process.platform === 'darwin' || process.platform === 'linux') { const unixPythonPaths = [ '/usr/bin/python3', '/usr/bin/python', '/usr/local/bin/python3', '/usr/local/bin/python', '/opt/homebrew/bin/python3', // M1/M2 Mac path.join(process.env.HOME || '', '.pyenv', 'versions', 'default', 'bin', 'python'), ]; for (const pythonPath of unixPythonPaths) { if (fs.existsSync(pythonPath)) { cachedPythonPath = pythonPath; return cachedPythonPath; } } } // 4. 默认尝试 python 或 python3 // 这会依赖系统 PATH,Windows上可能不工作 if (process.platform === 'win32') { cachedPythonPath = 'python'; } else { cachedPythonPath = 'python3'; } return cachedPythonPath; } /** * 验证Python可执行文件是否可用 */ export function isValidPythonPath(pythonPath: string): boolean { return fs.existsSync(pythonPath); } /** * 检查Python是否安装了特定的库 */ export async function checkPythonModule(moduleName: string): Promise { return new Promise((resolve) => { const pythonPath = getPythonPath(); const process = spawn(pythonPath, ['-m', 'pip', 'show', moduleName], { stdio: ['pipe', 'pipe', 'pipe'], shell: true }); let stdout = ''; process.stdout.on('data', (data) => { stdout += data.toString(); }); process.on('close', (code) => { resolve(code === 0 && stdout.includes(`Name: ${moduleName}`)); }); process.on('error', () => { resolve(false); }); // 设置超时 setTimeout(() => { process.kill(); resolve(false); }, 5000); }); } /** * 获取Python版本信息 */ export async function getPythonVersion(): Promise { return new Promise((resolve) => { const pythonPath = getPythonPath(); const process = spawn(pythonPath, ['--version'], { stdio: ['pipe', 'pipe', 'pipe'], shell: true }); let stdout = ''; let stderr = ''; process.stdout.on('data', (data) => { stdout += data.toString(); }); process.stderr.on('data', (data) => { stderr += data.toString(); }); process.on('close', (code) => { if (code === 0) { resolve((stdout || stderr).trim()); } else { resolve(null); } }); process.on('error', () => { resolve(null); }); // 设置超时 setTimeout(() => { process.kill(); resolve(null); }, 5000); }); } /** * 清除缓存(用于测试) */ export function clearPythonPathCache(): void { cachedPythonPath = null; }