是否可以在运行时修改 PYTHONPATH?
我有一个动态链接到 Python 解释器的 C++ 应用程序。我希望能够从特定目录导入 python 模块。我想修改我的进程的 PYTHONPATH ,以便 sys.path 将包含我添加到 PYTHONPATH 的路径。根据本文档,这似乎是它的工作方式:
http:// docs.python.org/c-api/intro.html#embedding-python
但是,当我从 Python-land 打印 sys.path 时,它具有 PYTHONPATH 的原始内容,而不是我设置的内容。这是我正在做的示例(使用 Boost.Python):
int main(int argc, char* argv[])
{
_putenv_s("PYTHONPATH", "C:\\source\\\\modules");
Py_Initialize();
object main = import("__main__");
object global = (main.attr("__dict__"));
exec("import sys\nprint sys.path"), global, global);
}
PS - 我知道还有其他方法可以实现我的目标,但这不是我要问的。我想知道为什么 Py_Initialize() 在设置 sys.path 时不使用 PYTHONPATH 的当前值。或者也许我误解了它应该如何工作?
I have a C++ application dynamically linked to the Python interpreter. I want to be able to import python modules from a particular directory. I want to modify the PYTHONPATH for my process so that sys.path will include the paths that I added to the PYTHONPATH. That seems to be the way it works according to this documentation:
http://docs.python.org/c-api/intro.html#embedding-python
However, when I print sys.path from Python-land it has the original contents of PYTHONPATH and not the one I set. Here's an example of what I'm doing (using Boost.Python):
int main(int argc, char* argv[])
{
_putenv_s("PYTHONPATH", "C:\\source\\\\modules");
Py_Initialize();
object main = import("__main__");
object global = (main.attr("__dict__"));
exec("import sys\nprint sys.path"), global, global);
}
PS - I know there are other ways to accomplish my goal, but that's not what I'm asking about. I am wondering why Py_Initialize() doesn't use the current value of PYTHONPATH when setting up sys.path. Or perhaps I've misunderstood how it is supposed to work?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
我找到了跨平台的解决方案。在调用任何其他 python 代码之前,只需执行以下 python 行:
I found cross-platform solution. Before invoke any other python code just execute following python lines:
如果您一次使用多个 C 运行时库,就会发生这种情况。在这种情况下,您的应用程序和 Python DLL 可能针对不同的 CRT 进行链接。每个 CRT 都有自己的一组环境变量;从一个 CRT 中使用 putenv 对环境所做的更改对于使用不同 CRT 进行的 getenv 调用是不可见的。
请参阅 http:// msdn.microsoft.com/en-us/library/ms235460%28v=vs.80%29.aspx。
您可以通过确保仅使用单个 CRT 来解决此问题,但这在实践中很棘手。程序的调试版本通常使用调试 CRT(它支持堆检查和 API 断言等功能);生产 DLL,即使在调试中使用,通常也使用 MSVCRT(生产线程安全版本)。我通过完全禁用调试 CRT,将所有构建设置为“多线程动态”来解决这个问题,因为维护单独的调试 DLL 太麻烦了。这样做你会失去一些调试能力。
This happens if you're using more than one C runtime library at a time. In this case, your application and the Python DLL are probably linked against different CRTs. Each CRT has its own set of environment variables; changes to the environment made with putenv from one CRT are not visible from getenv calls made with a different CRT.
See the "readEnv" example at http://msdn.microsoft.com/en-us/library/ms235460%28v=vs.80%29.aspx.
You can fix this by making sure to use only a single CRT, but that's tricky in practice. Debug builds of programs typically use debug CRTs (which enable things like heap checks and API assertions); production DLLs, even when used in debugging, typically use MSVCRT, the production, threadsafe version. I've worked around this by disabling the debug CRTs entirely, setting all builds to "multithreaded dynamic", since maintaining separate debug DLLs is too much of a hassle. You lose some debugging capabilities by doing that.
查看:
void PySys_SetPath(char *path) 将 sys.path 设置为在 path 中找到的路径的列表对象,该对象应该是用平台的搜索路径分隔符分隔的路径列表(在 Unix 上为 ; 在 Windows 上)。
或
Py_SetProgramName(argv[0]);这会将 dirname(argv[0]) 添加到您的 PYTHONPATH 中。
Check out:
void PySys_SetPath(char *path) Set sys.path to a list object of paths found in path which should be a list of paths separated with the platform’s search path delimiter (: on Unix, ; on Windows).
or
Py_SetProgramName(argv[0]); That adds dirname(argv[0]) to your PYTHONPATH for you.
正如其他人所说,您可能会遇到 CRT 不匹配的情况。我能够让它与 Python 2.6 和 Visual C++ 2008 一起使用:
此输出:
另一个选项可能是更改到该目录,因为当前目录通常最终在路径上,例如:
这给了我:
As other people have said, you may be running into a CRT mismatch. I was able to get this to work with Python 2.6 and Visual C++ 2008:
This output:
Another option may be to change to that directory, since the current directory typically ends up on the path, e.g.:
which gives me:
你可以这样做
python3 -c "import sys; sys.path.append('C:\Path\To\Modules')"
You could just do
python3 -c "import sys; sys.path.append('C:\Path\To\Modules')"
Python DLL 在加载时可能会获得自己的环境副本。更改环境后尝试使用 LoadLibrary 和 GetProcAddress 加载它,看看是否会发生任何变化。
It is possible that the Python DLL gets its own copy of the environment when it is loaded. Try loading it with LoadLibrary and GetProcAddress after you've changed the environment and see if that changes anything.
这适用于所有 python 版本(2.6、2.7、3.1、3.2、3.3 和 3.4)。
关于
的几点注意事项:d:/path1;d:/path2
等)不起作用Windows 路径如:
d:\\path1
将起作用仅适用于 Python 3 之前的 Python 版本,对于更高版本,应使用d:\\\\path1
。我建议用 unix 分隔符替换 windows 路径分隔符。以下代码块执行此操作。std::string my_path = "";
std::replace(my_path.begin(), my_path.end(), '\\', '/');
一个温和的建议:
如果您想支持不同的 python 版本,请不要浪费时间尝试使用以下任一 API 方法修改 PYTHONPATH:
Py_SetPythonHome()
- 对于 python 2 需要 ascii string,对于 python 3 - 一个 unicode 字符串,但对于高于 3.1 的版本不能可靠地工作Py_SetPath()
- 在 python 3 中引入,但有错误(参见 http://bugs.python.org/issue11320)一般来说,上面列出的 API 方法不会影响
Py_Initialize()
打电话。This worked for all python version (2.6, 2.7, 3.1, 3.2, 3.3 and 3.4).
Several notes regarding
<some_path>
:d:/path1;d:/path2
etc.) is not workingWindows paths like:
d:\\path1
will work only for python versions prior to Python 3, for later versionsd:\\\\path1
should be used. I'd advice replacing windows path delimiters with unix delimiters. Following code chunk does this.std::string my_path = "<some_path>";
std::replace(my_path.begin(), my_path.end(), '\\', '/');
A gentle advise:
Don't waste your time trying to modify PYTHONPATH with either of the following API methods if you want to support different python versions:
Py_SetPythonHome()
- for python 2 requires an ascii string, for python 3 - a unicode string, but doesn't work reliably for versions greater than 3.1Py_SetPath()
- introduced in python 3, but is buggy (see http://bugs.python.org/issue11320)In general API methods listed above do not affect after
Py_Initialize()
call.