是否可以在运行时修改PYTHONPATH?

时间:2011-03-15 21:43:11

标签: c++ python boost-python python-c-api python-embedding

我有一个动态链接到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的当前值。或许我误解了它应该如何运作?

7 个答案:

答案 0 :(得分:7)

我发现了跨平台解决方案。在调用任何其他python代码之前,只需执行以下python行:

import sys
sys.path.append("C:\\source\\\\modules")

答案 1 :(得分:3)

如果您一次使用多个C运行时库,则会发生这种情况。在这种情况下,您的应用程序和Python DLL可能与不同的CRT相关联。每个CRT都有自己的一组环境变量;使用不同CRT进行的getenv调用无法看到使用putenv从一个CRT进行的环境变化。

请参阅http://msdn.microsoft.com/en-us/library/ms235460%28v=vs.80%29.aspx上的“readEnv”示例。

你可以通过确保只使用一个CRT来解决这个问题,但这在实践中很棘手。程序的调试版本通常使用调试CRT(它支持诸如堆检查和API断言之类的事情);生产DLL,即使在调试中使用,通常也使用MSVCRT,即生产线程安全版本。我通过完全禁用调试CRT,将所有构建设置为“多线程动态”来解决这个问题,因为维护单独的调试DLL太麻烦了。这样做会失去一些调试功能。

答案 2 :(得分:3)

退房:

void PySys_SetPath(char * path)将sys.path设置为路径中找到的路径的列表对象,该路径应该是使用平台的搜索路径分隔符分隔的路径列表(:在Unix上,在Windows上)。

Py_SetProgramName(argv的[0]);这会为您的PYTHONPATH添加dirname(argv [0])。

答案 3 :(得分:2)

正如其他人所说,您可能遇到了CRT不匹配问题。我能够使用Python 2.6和Visual C ++ 2008:

#include "stdafx.h"
#include "Python.h"

int _tmain(int argc, _TCHAR* argv[])
{
  _putenv_s("PYTHONPATH", "C:\\source\\\\modules");
  Py_Initialize();
  PyRun_SimpleString("import sys\nprint sys.path");
  PyRun_SimpleString("raw_input()");
  return 0;
}

此输出:

['C:\\Python26\\lib\\site-packages\\distribute-0.6.13-py2.6.egg', 'C:\\Python26\
\lib\\site-packages\\virtualenv-1.4.9-py2.6.egg', 'C:\\source\\modules', ...

另一种选择可能是更改到该目录,因为当前目录通常最终在路径上,例如:

    _chdir("c:\\");
    Py_Initialize();
[...]

给了我:

['C:\\Python26\\lib\\site-packages\\distribute-0.6.13-py2.6.egg', 'C:\\Python26\
\lib\\site-packages\\virtualenv-1.4.9-py2.6.egg', 'C:\\Windows\\system32\\python
26.zip', 'C:\\Python26\\Lib', 'C:\\Python26\\DLLs', 'C:\\Python26\\Lib\\lib-tk',
 'c:\\', ...

答案 4 :(得分:1)

Python DLL有可能在加载时获得自己的环境副本。在更改环境后,尝试使用LoadLibrary和GetProcAddress加载它,看看是否有任何改变。

答案 5 :(得分:1)

#include "Python.h"
int main()
{
  Py_Initialize();
  PyRun_SimpleString("import sys");
  PyRun_SimpleString("sys.path.append(\"<some_path>\")");
  return 0;
}

这适用于所有python版本(2.6,2.7,3.1,3.2,3.3和3.4) 关于<some_path>的几点说明:

  • 它应该只包含单个目录。具有有效分隔符(d:/path1;d:/path2等)的目录列表无效
  • Windows路径如:d:\\path1仅适用于Python 3之前的python版本,因为应使用更高版本d:\\\\path1。我建议用unix分隔符替换windows路径分隔符。以下代码块执行此操作。

    std::string my_path = "<some_path>"; std::replace(my_path.begin(), my_path.end(), '\\', '/');

温和的建议: 如果您想支持不同的python版本,请不要浪费时间尝试使用以下任一API方法修改PYTHONPATH:

  • Py_SetPythonHome() - 对于python 2,需要一个ascii字符串,对于python 3 - 一个unicode字符串,但对于大于3.1的版本不能可靠地工作
  • Py_SetPath() - 在python 3中引入,但是有bug(参见http://bugs.python.org/issue11320

通常,上面列出的API方法在Py_Initialize()调用后不会影响。

答案 6 :(得分:1)

您可以python3 -c "import sys; sys.path.append('C:\Path\To\Modules')"