@writeshh/nepse-sdk
Version:
Node.js wrapper around NepseUnofficialApi (Python)
109 lines (98 loc) • 3.92 kB
JavaScript
/*
Ensures Python (>=3.11) is available and installs NepseUnofficialApi.
- Prefers pip for the current python3
- Falls back to pipx if available
*/
const { execSync } = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
function run(cmd) {
try {
return execSync(cmd, { stdio: 'pipe' }).toString().trim();
} catch (err) {
return null;
}
}
function has(cmd) {
return !!run(`command -v ${cmd}`);
}
function ensurePython() {
const py = has('python3') ? 'python3' : has('python') ? 'python' : null;
if (!py) {
console.error('[nepse-node] Python 3.11+ not found. Please install Python >=3.11');
process.exit(0); // do not hard-fail install
}
const ver = run(`${py} -c "import sys; print('.'.join(map(str, sys.version_info[:3])))"`);
if (!ver) return py;
const [maj, min] = ver.split('.').map(Number);
if (maj < 3 || (maj === 3 && min < 11)) {
console.warn(`[nepse-node] Detected Python ${ver}. NepseUnofficialApi requires >=3.11. You may encounter issues.`);
}
return py;
}
function ensurePackage(py) {
// Prefer an isolated virtualenv inside the package to avoid PEP 668 issues
const projectRoot = path.resolve(__dirname, '..');
const venvDir = path.join(projectRoot, '.venv');
const isWin = process.platform === 'win32';
const venvPython = isWin ? path.join(venvDir, 'Scripts', 'python.exe') : path.join(venvDir, 'bin', 'python');
const venvPip = isWin ? path.join(venvDir, 'Scripts', 'pip.exe') : path.join(venvDir, 'bin', 'pip');
try {
if (!fs.existsSync(venvPython)) {
console.log('[nepse-node] Creating virtual environment for Python dependencies');
execSync(`${py} -m venv ${JSON.stringify(venvDir)}`, { stdio: 'inherit' });
}
} catch (e) {
console.warn('[nepse-node] Failed to create venv, will try system pip. Error:', e.message);
}
function isNepseInstalled(pythonPath) {
const out = run(`${pythonPath} -c "import importlib, sys; sys.exit(0 if importlib.util.find_spec('nepse') else 1)"`);
return out !== null;
}
// Try venv first
if (fs.existsSync(venvPython)) {
if (!isNepseInstalled(venvPython)) {
try {
// Try upgrading pip first
execSync(`${JSON.stringify(venvPython)} -m pip install --upgrade pip`, { stdio: 'inherit' });
} catch (_) {}
try {
execSync(`${JSON.stringify(venvPip)} install --quiet git+https://github.com/basic-bgnr/NepseUnofficialApi`, { stdio: 'inherit' });
} catch (e1) {
console.warn('[nepse-node] venv pip install failed, retrying with break-system-packages');
try {
execSync(`${JSON.stringify(venvPip)} install --quiet --break-system-packages git+https://github.com/basic-bgnr/NepseUnofficialApi`, { stdio: 'inherit' });
} catch (e2) {
console.warn('[nepse-node] venv pip install still failed:', e2.message);
}
}
}
// Record the venv python path for the bridge
try {
const hintFile = path.join(projectRoot, 'bridge', '.python_path');
fs.writeFileSync(hintFile, venvPython, 'utf8');
} catch {}
if (isNepseInstalled(venvPython)) return;
}
// Fallback to system pip/pipx
const pip = has('pip3') ? 'pip3' : has('pip') ? 'pip' : null;
if (pip) {
try {
execSync(`${pip} install --user --quiet git+https://github.com/basic-bgnr/NepseUnofficialApi`, { stdio: 'inherit' });
return;
} catch (_) {}
}
if (has('pipx')) {
try {
execSync(`pipx install git+https://github.com/basic-bgnr/NepseUnofficialApi`, { stdio: 'inherit' });
return;
} catch (_) {}
}
console.warn('[nepse-node] Failed to auto-install NepseUnofficialApi. Please install manually:\n pip3 install --user git+https://github.com/basic-bgnr/NepseUnofficialApi');
}
(function main() {
const py = ensurePython();
if (!py) return;
ensurePackage(py);
})();