【问题标题】:Is it possible to modify PYTHONPATH at runtime?是否可以在运行时修改 PYTHONPATH?
【发布时间】:2011-07-16 03:54:17
【问题描述】:

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

【问题讨论】:

  • 看起来应该可以了。 (尽管您可能希望在源代码和模块之间使用 2 个,而不是 4 个反斜杠。) sys.path 的值打印什么?
  • ['C:\\source\\test','C:\\windows\\system32\\python27.zip', 'C:\\Python27\\Lib', 'C: \\Python27\\DLLs', 'C:\\Python27\\lib\\lib-tk', 'C:\\Python27', 'C:\\Python27\\lib\\site-packages'] 请注意我的系统 PYTHONPATH 设置为 C:\source\test,我的代码将其更改为 C:\source\modules。所以,sys.path 正在获取原始值。
  • 您是否检查过_putenv_s 是否成功,即返回0?
  • 奇怪。假设您确实成功地更改了它们,我看不到嵌入式 python 解释器如何在您在此过程中更改它们之前看到环境变量。 2 种不太可能的可能性:putenv 失败,或者 putenv 仅影响环境的 CRT 副本,并且 Python 解释器正在使用由 SetEnvironmentVariable 维护的单独的 Win32 环境副本。 lists.boost.org/Archives/boost/2002/01/23504.php 说 putenv 应该改变两者,但是尝试 SetEnvironmentVariable 看看它是否有帮助?
  • 既然是嵌入Python,应该可以直接调整sys.path,完全不用环境变量的方法。

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


【解决方案1】:

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

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

【讨论】:

  • 这对我来说非常有效!在 boost::python 中,只需要 import("sys").attr("path").attr("append")("path/to/files")
  • 我相信你可以从 C++ 中做到这一点: PyRun_SimpleString("import sys; sys.path.append('/some/path')\n");唯一的优点是它使嵌入式 python 和正在执行嵌入的 C++ 中的魔力保持不变(也便于在 python 中进行路径操作)。
  • @dementedhedgehog - 好吧,我已经提到过这种方式是跨平台的,这意味着(至少对我而言)在 C++ 上使用路径进行操作比在 Python 上更困难
  • @dewfy 我同意你的观点,python 路径操作比 c++ 容易得多 - 如果我没有说清楚,我深表歉意。作为说明,我使用 Py_SetProgramName(argv[0]); 为自己解决了这个问题。这会将 dirname(prog) 添加到您的 PYTHONPATH 中。
【解决方案2】:

如果您一次使用多个 C 运行时库,就会发生这种情况。在这种情况下,您的应用程序和 Python DLL 可能链接到不同的 CRT。每个 CRT 都有自己的一组环境变量;从一个 CRT 使用 putenv 对环境所做的更改在使用不同 CRT 进行的 getenv 调用中是不可见的。

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

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

【讨论】:

  • 是的,正如我在之前的评论中所怀疑的那样,这正是正在发生的事情。
【解决方案3】:

退房:

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

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

【讨论】:

    【解决方案4】:

    正如其他人所说,您可能遇到了 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:\\', ...
    

    【讨论】:

      【解决方案5】:

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

      【讨论】:

        【解决方案6】:
        #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)。
        关于&lt;some_path&gt;的几点说明:

        • 它应该只包含一个单个目录。具有有效分隔符(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 中引入,但有问题(参见 http://bugs.python.org/issue11320

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

        【讨论】:

          【解决方案7】:

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

          【讨论】:

          • 这是一个 13:37 的答案。
          猜你喜欢
          • 2017-04-26
          • 1970-01-01
          • 2010-10-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2010-09-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多