Можно ли изменить PYTHONPATH во время выполнения?

У меня есть приложение C++, динамически связанное с интерпретатором Python. Я хочу иметь возможность импортировать модули Python из определенного каталога. Я хочу изменить PYTHONPATH для моего процесса, чтобы sys.путь будет включать пути, которые я добавил к PYTHONPATH. Похоже, что это работает в соответствии с этой документацией:

http://docs.python.org/c-api/intro.html#embedding-python

однако, когда я печатаю sys.путь из страны Python он имеет оригинальное содержание 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 sysnprint sys.path"), global, global);
}

PS - Я знаю, что есть другие способы достижения моей цели, но это не то, что я прошу об. Мне интересно, почему Py_Initialize () не использует текущее значение PYTHONPATH при настройке sys.путь. Или, возможно, я неправильно понял, как это должно работать?

7 ответов


Я нашел кросс-платформенное решение. Перед вызовом любого другого кода python просто выполните следующие строки python:

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

Это происходит, если вы используете более одной библиотеки времени выполнения C. В этом случае ваше приложение и DLL Python, вероятно, связаны с различными CRTs. Каждая ЭЛТ имеет свой собственный набор переменных среды; изменения в среде, сделанные с помощью putenv из одной ЭЛТ, не видны из вызовов getenv, сделанных с другой ЭЛТ.

см. пример "readEnv" вhttp://msdn.microsoft.com/en-us/library/ms235460%28v=vs.80%29.aspx.

вы можно исправить это, убедившись, что используется только один ЭЛТ, но это сложно на практике. Отладочные сборки программ обычно используют отладочные CRT (которые включают такие вещи, как проверки кучи и утверждения API); производственные библиотеки DLL, даже когда они используются в отладке, обычно используют MSVCRT, производственную, потокобезопасную версию. Я работал вокруг этого, полностью отключив отладочные CRTs, установив все сборки на "многопоточную динамику", так как поддержание отдельных отладочных DLL-библиотек слишком много хлопот. Вы теряете некоторую отладку возможности делать это.


Проверьте:

void PySys_SetPath (char * path) установить sys.путь к объекту списка путей, найденных в path, который должен быть списком путей, разделенных разделителем пути поиска платформы (: в Unix, ; в Windows).

или

Py_SetProgramName(argv[0]); это добавляет dirname (argv[0]) к вашему PYTHONPATH для вас.


как говорили другие люди, Вы можете столкнуться с несоответствием 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:\', ...

возможно, что DLL Python получает свою собственную копию среды при ее загрузке. Попробуйте загрузить его с помощью LoadLibrary и GetProcAddress после того, как вы изменили среду, и посмотрите, изменится ли что-нибудь.


#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>:

  • он должен содержать только один

вы могли бы просто сделать python3 -c "import sys; sys.path.append('C:\Path\To\Modules')"