Как мне сказать POpen использовать / устанавливать определенные переменные окружения? - PullRequest
1 голос
/ 10 июля 2019

Я использую Python 3.7 и Django. Я использую ниже для запуска команды, которую я обычно запускаю в оболочке ...

out = Popen([settings.SELENIUM_RUNNER_CMD, file_path], stderr=STDOUT, stdout=PIPE)
t = out.communicate()[0], out.returncode

его умирает с ошибкой

b'env: node: No such file or directory\n'

Что я пытаюсь выяснить, так это как дать моей среде Python доступ к нормальным переменным среды, к которым у меня есть доступ, или найти способ установить их перед тем, как я выполню команду Python. Обычно "узел" легко найти, когда я проверяю себя

davea$ which node
/usr/local/bin/node

Но я не знаю, как сказать Python использовать тот же PATH, к которому у меня есть доступ.

Ответы [ 2 ]

4 голосов
/ 12 июля 2019

Если мы обращаемся к документации Попена , мы можем увидеть три соответствующих аргумента:

  1. cwd str или path -подобный объект, это текущий рабочий каталог
  2. env mapping (скажем, dict), это отображение среды, переданное вызываемой программе
  3. shell флаг, переносите ли вы программу внутри оболочки или нет

Давайте рассмотрим каждое решение.


Если вы можете себе это позволить, просто используйте cwd="where is node", например, если node в /usr/local/bin, вы можете просто использовать cwd=/usr/local/bin или cwd=os.path.join(USR_LOCAL, 'bin'), например. Но в этой папке будет создано все, что может быть не того, что вы хотите (журналы, предположения о текущем рабочем каталоге).


Теперь для окружающей среды:

Если env не None, это должно быть отображение, которое определяет переменные среды для нового процесса; они используются вместо поведения по умолчанию наследования среды текущего процесса. Он передается непосредственно Попену.

Вы можете просто скопировать текущую среду через os.environ и добавить в PATH что-то вроде этого:

new_env = os.environ.copy()
new_env['PATH'] = '{}:/usr/local/bin'.format(new_env['PATH'])

Затем передайте это new_env отображение, и вот вы!


Если вы действительно хотите положиться на оболочку, вы можете, но вот сведения о платформе:

POSIX-платформ

В POSIX с shell = True оболочкой по умолчанию является / bin / sh. Если args является строкой, строка определяет команду для выполнения через оболочку. Это означает, что строка должна быть отформатирована точно так же, как при вводе в командной строке. Это включает, например, кавычки или обратную косую черту, экранирующие имена файлов с пробелами в них. Если args - последовательность, первый элемент задает командную строку, и любые дополнительные элементы будут обрабатываться как дополнительные аргументы самой оболочки. То есть Попен делает эквивалент: Popen(['/bin/sh', '-c', args[0], args[1], ...])

Windows платформы

В Windows с shell = True переменная среды COMSPEC задает оболочку по умолчанию. Единственный раз, когда вам нужно указать shell = True в Windows, это когда команда, которую вы хотите выполнить, встроена в оболочку (например, dir или copy). Вам не нужно shell = True для запуска командного файла или исполняемого файла на консоли.

Вы можете использовать что-то вроде PATH=whatever и использовать все ваше shell-фу напрямую, но предостережения: соображения безопасности .


Бонусное решение

Просто переопределите PATH перед вызовом вашего процесса Python. Если вы используете Django, вы либо используете:

  1. Сервер разработки
  2. Сервер производственного уровня

В обоих случаях все, что вам нужно сделать, это переопределить среду родительского процесса, для производственного сервера, такого как Gunicorn, это возможно, и для этого есть документация. Для сервера разработки сделайте это на своем уровне оболочки (но предупреждайте! Возможно, вам придется задокументировать такое поведение или сказать кому-либо, использующему ваше программное обеспечение, что вы предполагаете, что node находится на пути, который… в большинстве случаев справедливый) .

0 голосов
/ 16 июля 2019

os.environ.copy() лучше всего подходит для того, что вы ищете.

import subprocess, os
my_env = os.environ.copy()
out = Popen([settings.SELENIUM_RUNNER_CMD, file_path], stderr=STDOUT, stdout=PIPE, env=my_env)
t = out.communicate()[0], out.returncode

И это должно быть!

...