-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathresolve_python.js
More file actions
80 lines (74 loc) · 3.41 KB
/
Copy pathresolve_python.js
File metadata and controls
80 lines (74 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/**
* resolve_python.js — 解析出可用的 python 路径(打印到 stdout, 供 run_daily.bat 使用)。
*
* 为什么需要: Windows 计划任务是"非 conda 激活"环境, 裸 `python` 命令常找不到(conda
* 默认不写系统 PATH), 导致定时任务里的 `python dispatch\notify.py` 报错、不推群不发邮件。
* 这里复用与 provinces/全国.js 相同的探测顺序, 优先选"装了 curl_cffi"的那个 python,
* 没有则退而选任意能运行的, 实在没有就回退 'python'。
*
* 用法: node resolve_python.js -> stdout 打印一个 python 路径
* 指定: 环境变量 NATIONAL_PYTHON / NATIONAL_CONDA_ENV, 或 crawl_config.json
*/
const { spawnSync } = require('child_process');
const fs = require('fs'), path = require('path'), os = require('os');
function loadCfg() {
try { return JSON.parse(fs.readFileSync(path.join(__dirname, 'crawl_config.json'), 'utf8')); }
catch { return {}; }
}
function condaRoots() {
const roots = new Set();
const add = p => { if (p) roots.add(p); };
if (process.env.CONDA_PREFIX) {
const cp = process.env.CONDA_PREFIX;
const i = cp.toLowerCase().indexOf(`${path.sep}envs${path.sep}`.toLowerCase());
add(i >= 0 ? cp.slice(0, i) : cp);
}
if (process.env._CONDA_ROOT) add(process.env._CONDA_ROOT);
if (process.env.CONDA_EXE) add(path.dirname(path.dirname(process.env.CONDA_EXE)));
const home = os.homedir();
for (const b of ['anaconda3', 'miniconda3', 'Anaconda3', 'Miniconda3']) {
add(path.join(home, b));
add(path.join('C:\\ProgramData', b));
}
return [...roots];
}
// 读 conda 注册的所有环境路径(~\.conda\environments.txt), 不管conda装在哪都能找到
function condaEnvList() {
try {
const f = path.join(os.homedir(), '.conda', 'environments.txt');
return fs.readFileSync(f, 'utf8').split(/\r?\n/).map(s => s.trim()).filter(Boolean);
} catch { return []; }
}
function candidates() {
const cfg = loadCfg();
const roots = condaRoots();
const listed = condaEnvList();
const list = [];
const pyPath = process.env.NATIONAL_PYTHON || cfg.national_python;
const envName = process.env.NATIONAL_CONDA_ENV || cfg.national_python_env;
if (pyPath) list.push(pyPath);
if (envName) {
// 先从conda环境清单里按名字精确匹配(装在哪都行), 再按常见根目录拼
for (const p of listed) if (path.basename(p).toLowerCase() === String(envName).toLowerCase()) list.push(path.join(p, 'python.exe'));
for (const r of roots) list.push(path.join(r, 'envs', envName, 'python.exe'));
}
if (process.env.CONDA_PYTHON_EXE) list.push(process.env.CONDA_PYTHON_EXE);
if (process.env.CONDA_PREFIX) list.push(path.join(process.env.CONDA_PREFIX, 'python.exe'));
for (const p of listed) list.push(path.join(p, 'python.exe')); // 所有已注册环境(兜底:自动找带curl_cffi的)
for (const r of roots) list.push(path.join(r, 'python.exe'));
list.push('python', 'python3', 'py');
const seen = new Set();
return list.filter(c => {
if (!c || seen.has(c)) return false;
seen.add(c);
if (path.isAbsolute(c) && !fs.existsSync(c)) return false;
return true;
});
}
function runs(cmd, code) {
try { const r = spawnSync(cmd, ['-c', code], { stdio: 'ignore' }); return !r.error && r.status === 0; }
catch { return false; }
}
const cands = candidates();
const chosen = cands.find(c => runs(c, 'import curl_cffi')) || cands.find(c => runs(c, 'pass')) || 'python';
process.stdout.write(chosen);