【问题标题】:Get Python to look in different location for Lib using Py_SetPath()使用 Py_SetPath() 让 Python 在不同的位置查找 Lib
【发布时间】:2018-10-07 15:21:00
【问题描述】:

我已在应用程序foo.exe 中嵌入了 Python。当它运行时,Python 被调用并立即寻找Lib。我可以让它工作的唯一方法是将Lib(Python的模块目录库)放在foo.exe的位置。

有没有办法可以将 Python 重定向到其他地方,例如 Python/Lib ?我无法更改PATH(这是 Windows),我不想破解 Python 源代码。

基本上,我无法让 Py_SetPath() 工作,我也无法在互联网上找到任何实际示例。

更新:

好的,这可行:

  #define MYMAXPATHLEN 1000
  static wchar_t progpath[MYMAXPATHLEN + 1];

  wchar_t* pdir = L"\\My_New_Location\\Python\\Lib";
  wchar_t* pdelim = L";";
  wchar_t* pypath = NULL;
  GetModuleFileNameW(NULL, progpath, MYMAXPATHLEN);
  reduce(progpath);
  wcscat(progpath,pdir);
  // I get the present module path and add the extra dirs to access Lib code

  wcscat(progpath, pdelim);  // I add a path delimiter

  pypath = Py_GetPath();
  wcscat(progpath, pypath);
  // I add the paths that Py_GetPath() produces.

  Py_SetPath(progpath);

  Py_Initialize();

我也调用 Py_SetProgramName();之后 Py_Initialize();我不确定是否需要所有这些额外的东西,但较小的解决方案似乎失败了。

似乎在初始化之后调用 Py_SetProgamName() 对于让嵌入调用正常工作非常重要。

【问题讨论】:

  • 如果Lib 不存在,那么也许您可以创建一个联结mklink /J c:\...\Lib c:\...\Python\Lib
  • 来自 Python 代码:“Py_SetPath() 可用于覆盖此机制。在调用 Py_Initialize 之前使用分号分隔的路径调用 Py_SetPath。”

标签: python python-3.x python-module lib


【解决方案1】:

在导入库之前,运行以下行:

sys.path.append('C:\path to Lib')

详情可见here

【讨论】:

  • 我认为它必须在进程的早期(某个 Python API 调用?)Python 失败,然后才能向它发送命令。
【解决方案2】:

我让它工作(在 Linux 上)执行以下操作:

// method to inspect PyObjects 
static void reprint(PyObject *obj) 
{
    PyObject* repr = PyObject_Repr(obj);
    PyObject* str = PyUnicode_AsEncodedString(repr, "utf-8", "~E~");
    const char *bytes = PyBytes_AS_STRING(str);

    printf("REPR: %s\n", bytes);
    Py_XDECREF(repr);
    Py_XDECREF(str);
}

.

int main()
{
// for our manually compiled and installed usr-local Python 3.6
//#define PATH            L"/usr/local/bin:/usr/local/lib:/usr/local/lib/python3.6/lib-dynload"
//#define PREFIX          L"/usr/local"
//#define EXEC_PREFIX     L"/usr/local"
//#define FULL_PROG_PATH  L"/usr/local/bin/python3.6"

// for apt installed Python 3.7
//#define PATH            L"/usr/bin:/usr/lib:/usr/lib/python3.7/lib-dynload"
//#define PREFIX          L"/usr"
//#define EXEC_PREFIX     L"/usr"
//#define FULL_PROG_PATH  L"/usr/bin/python3.7"

// for venv (which uses the /usr/local/lib python 3.6)
#define PATH            L"/home/me/venv_dir/bin:/home/me/venv_dir/lib:/usr/local/lib/python3.6:/usr/local/lib/python3.6/lib-dynload"
#define PREFIX          L"/home/me/venv_dir"
#define EXEC_PREFIX     L"/usr/local"
#define FULL_PROG_PATH  L"/usr/local/bin/python3.6"

// ------------------------------------------------

#define CHANGE_THE_INTERPRETER

#ifdef CHANGE_THE_INTERPRETER

// TODO : Look at using this: https://www.python.org/dev/peps/pep-0587/

Py_SetPath(PATH);

// change the built-in prefix/exec-prefix, in place.
wchar_t* wpPrefix     = Py_GetPrefix();
wchar_t* wpExecPrefix = Py_GetExecPrefix();
wchar_t* wpProgramFullPath = Py_GetProgramFullPath();
wcscpy (wpPrefix, PREFIX);
wcscpy (wpExecPrefix, EXEC_PREFIX);
wcscpy (wpProgramFullPath, FULL_PROG_PATH);

#endif //CHANGE_THE_INTERPRETER

// inspect the environment variables.  With the #define commented out above the "defaults" appear as indicated to the right hand side
wchar_t* xx;                        //defaults
xx = Py_GetPrefix();                //<prefix> L"/usr/local"
xx = Py_GetExecPrefix();            //<exec_prefix> L"/usr/local"
xx = Py_GetPath();                  //L"/usr/local/lib/python36.zip:/usr/local/lib/python3.6:/usr/local/lib/python3.6:/usr/local/lib/python3.6/lib-dynload"
xx = Py_GetProgramName();           //L"python3"
xx = Py_GetPythonHome();            //null
xx = Py_GetProgramFullPath();       //<progpath> L"/usr/local/bin/python3"

Py_Initialize();

一些额外的位

int x2 = PyRun_SimpleString ("import site; print (site.getsitepackages())");
int x3 = PyRun_SimpleString ("import datetime");
int x4 = PyRun_SimpleString ("import numpy as np");

//inspect sys info
PyObject* sys_executable = PySys_GetObject((char*)"executable");  reprint(sys_executable);
PyObject* sys_version    = PySys_GetObject((char*)"version");  reprint(sys_version);

PyObject* sys_realPrefix = PySys_GetObject((char*)"real_prefix");  reprint(sys_realPrefix);
PyObject* sys_basePrefix = PySys_GetObject((char*)"base_prefix");  reprint(sys_basePrefix);

需要注意的几点:

如果您查看 Python 模块 getpath.c,您将看到以下缓冲区:

static wchar_t prefix[MAXPATHLEN+1];
static wchar_t exec_prefix[MAXPATHLEN+1];
static wchar_t progpath[MAXPATHLEN+1];

Py_GetProgramFullPath等方法执行如下:

if (!module_search_path)
    calculate_path();
return progpath;

...所以可以使用这些方法来获取缓冲区指针和wcscpy 直接将值放入缓冲区。 请注意,目前只有以这种方式实现 getpath 才有可能!

需要lib-dynload来保证某些模块(例如日期时间)可以拉入

另请注意,此方法可确保可以找到 .../python3.x/encodings 目录,从而防止 Py_Initialize 中出现运行时错误

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-10
    • 1970-01-01
    • 2013-11-15
    • 2018-06-24
    • 2020-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多