致命的Python错误:无法获取随机数来初始化Python
环境 Windows 10、VSC 15
使用 CreateProcessA winapi 并传递 lp 环境变量来使用脚本运行 python。 当 lpenvironment 传递 null 时,它工作正常。 如果我设置环境变量 PATH 和 PYTHONPATH = "paths",并传递 LPSTR(env.c_Str()),它会在运行时抛出上述错误。 python版本是3.5.6
有什么帮助吗?
更多细节。
由于某种原因,CreateProcessA 中的第 7 个参数失败,如果为 null,则 python.exe 运行成功,否则打印“Fatal Python error: failed to get random number to Initialize Python”。
std::string Base = Configuration::getBasePath();
std::string environPython = Base;
environPython.append("\\Python;");
environPython.append(Base);
environPython.append("\\Python\\Scripts;");
environPython.append(Base);
environPython.append("\\Python\\include;");
environPython.append(Base);
environPython.append("\\Python\\Lib;");
environPython.append(Base);
environPython.append("\\Python\\libs;");
environPython.append(Base);
environPython.append("\\Python\\Lib\\site-packages;");
environPython.append(Base);
environPython.append("\\Python\\_ML;");
environPython.push_back('\0');
std::string environPath = Base;
environPath.append("\\Python;");
environPath.append(Base);
environPath.append("\\Python\\Lib;");
environPath.append(Base);
environPath.append("\\Python\\Scripts;");
environPath.append(Base);
environPath.append("\\Python\\libs;");
environPath.push_back('\0');
std::string cmd = Base;
cmd.append("\\Python\\python.exe");
std::string params = "\"";
params.append(cmd);
params.append("\" \"");
params.append(Base);
params.append("\\Python\\_ML\\mlprocessor_server.py\"");
std::map env = { { "PYTHONPATH", environPython.data() }, { "PATH", environPath.data() }};
// example for generating block of strings
std::vector<char> envBlock;
std::for_each(env.begin(), env.end(),
[&envBlock](const std::pair<std::string, std::string> & p) {
std::copy(p.first.begin(), p.first.end(), std::back_inserter(envBlock));
envBlock.push_back('=');
std::copy(p.second.begin(), p.second.end(), std::back_inserter(envBlock));
envBlock.push_back('\0');
}
);
envBlock.push_back('\0');
// feed this into ::CreateProcess()
LPVOID lpEnvironment = (LPVOID)envBlock.data();
bool result = CreateProcessA(cmd.c_str(), (LPSTR)params.c_str(), NULL, NULL, FALSE, CREATE_NO_WINDOW, lpEnvironment, NULL, &info, &pi);
结果始终为真,python.exe 未显示在任务管理器中,并给出致命的 Python 错误:无法获取随机数来初始化 Python。
如果 lpEnvironment 为 NULL,则 python.exe 将显示在任务管理器中。
传递给
CreateProcessA
的环境必须包含SYSTEMROOT
,否则在初始化期间在python内部调用Win32 API调用CryptAcquireContext
将会失败。
当传入 NULL 作为 lpEnvironment 时,新进程将继承调用进程的环境,该进程已定义
SYSTEMROOT
。
接下来举一个例子,说明如何在现实世界中的纯 Python 软件中轻松触发此操作,有时 Python 打开自身实例来执行某些任务是有用的,其中子任务需要设置特定的
PYTHONPATH
。 通常,这可以在不那么挑剔的平台(即不是 Windows)上懒惰地完成,如下所示:
import sys
from subprocess import Popen
p = Popen([sys.executable, '-c', 'print("hello world")'], env={
'PYTHONPATH': '', # set it to somewhere
})
但是,在 Windows 上这样做,会导致以下令人费解的失败:
Python 3.8.10 (...) [MSC v.1928 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> from subprocess import Popen
>>> p = Popen([sys.executable, '-c', 'print("hello world")'], env={
... 'PYTHONPATH': ''
... })
Fatal Python error: _Py_HashRandomization_Init: failed to get random numbers to initialize Python
Python runtime state: preinitialized
解决方法很明显:克隆
os.environ
以确保SYSTEMROOT
就位,从而避免@Joe Savage的答案指出的问题,例如:
>>> import os
>>> env = os.environ.copy()
>>> env['PYTHONPATH'] = ''
>>> p = Popen([sys.executable, '-c', 'print("hello world")'], env=env)
hello world
需要此类修复的现实示例:
setuptools
(仅在测试中,但作为一个示例,显示几乎任何人都可以很容易地犯此错误)。