Можно ли изменить PYTHONPATH во время выполнения? - PullRequest
9 голосов
/ 16 марта 2011

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

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

Однако, когда я печатаю sys.path из Python-land, он имеет исходное содержимое 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 () не использует текущее значение PYTHONPATH при настройке sys.path.Или, может быть, я неправильно понял, как это должно работать?

Ответы [ 7 ]

7 голосов
/ 18 ноября 2011

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

import sys
sys.path.append("C:\\source\\\\modules")
3 голосов
/ 27 января 2014

Проверить:

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

или

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

3 голосов
/ 16 марта 2011

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

См. Пример "readEnv" на http://msdn.microsoft.com/en-us/library/ms235460%28v=vs.80%29.aspx.

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

2 голосов
/ 16 марта 2011

Как уже говорили другие люди, вы можете столкнуться с несоответствием ЭЛТ.Я смог заставить это работать с 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:\\', ...
1 голос
/ 10 июня 2018

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

1 голос
/ 05 мая 2015
#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>:

  • Он должен содержать только каталог single . Список каталогов с допустимыми разделителями (d:/path1;d:/path2 и т. Д.) Не работает
  • Пути Windows, такие как: d:\\path1 будут работать только для версий Python до Python 3, для более поздних версий следует использовать d:\\\\path1. Я бы посоветовал заменить разделители пути Windows на Unix. Следующий фрагмент кода делает это.

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

Деликатный совет: Не тратьте свое время на попытки изменить PYTHONPATH любым из следующих методов API, если вы хотите поддерживать разные версии Python :

  • Py_SetPythonHome() - для python 2 требуется строка ascii, для python 3 - строка в кодировке Unicode, но она не работает надежно для версий более 3.1
  • Py_SetPath() - введено в python 3, но содержит ошибки (см. http://bugs.python.org/issue11320)

Обычно перечисленные выше методы API не влияют после Py_Initialize() вызова.

1 голос
/ 16 марта 2011

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

...