Несоответствие между sys.executable и sys.version в Python

Установлены два интерпретатора Python:

[[email protected] ~]$ /usr/bin/python -V && /usr/local/bin/python -V
Python 2.4.3
Python 2.7.6

Судо изменяет PATH для каждой команды, выполняемой следующим образом:

[[email protected] ~]$ env | grep PATH && sudo env | grep PATH
PATH=/usr/kerberos/bin:/usr/local/bin:/usr/bin:/bin:/usr/X11R6/bin:/home/user/bin
PATH=/usr/bin:/bin

Я запускаю тест script:

[[email protected] ~]$ cat what_python.py
#!/usr/bin/env python

import sys
print sys.executable
print sys.version
[[email protected] ~]$ sudo python what_python.py
/usr/bin/python
2.7.6 (default, Feb 27 2014, 17:05:07) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-54)]

и получить путь к Python 2.4.3 в sys.executable и версии 2.7.6, описанной в sys.version. Ясно, что sys.executable и sys.version не совпадают. Принимая во внимание, как sudo изменяет PATH, я могу понять значение sys.executable. Однако почему sys.version сообщает версию 2.7.6, а не версию 2.4.3, которая будет соответствовать пути usr/bin/python, указанному sys.executable?

Это продолжение моего вопроса Судо меняет PATH, но выполняет тот же двоичный файл

Ответ 1

Оба @Graeme

Тот факт, что python не может получить это, говорит о том, что это делает свой собственный поиск PATH (...)

и @twalberg

(...) похоже, что sys.executable ищет текущий PATH вместо разрешая argv [0] (или, может быть, потому, что argv [0] - это простой питон в этом случай...), (...)

были в основном правы. Я не хотел верить, что Python делает что-то настолько простое (глупо?) Как использование PATH, чтобы найти себя, но это правда.

Модуль Python sys реализован в файле Python/sysmodule.c. Начиная с версии 2.7.6, sys.executable устанавливается в строке 1422 следующим образом:

 SET_SYS_FROM_STRING("executable",
                     PyString_FromString(Py_GetProgramFullPath()));

Py_GetProgramFullPath() функция определена в файле Modules/getpath.c, начиная с строки 701:

char *
Py_GetProgramFullPath(void)
{
    if (!module_search_path)
        calculate_path();
    return progpath;
}

Функция calcuate_path() определена в том же файле и содержит следующий комментарий:

/* If there is no slash in the argv0 path, then we have to
 * assume python is on the user $PATH, since there no
 * other way to find a directory to start the search from.  If
 * $PATH isn't exported, you lose.
 */

Как видно в моем случае, каждый проигрывает, когда первый Python при экспорте $PATH отличается от выполняемого Python.

Более подробную информацию о процессе расчета размещения исполняемого файла интерпретатора можно найти в top файла getpath.c:

Прежде чем выполнять какие-либо проверки, местоположение исполняемого файла определяется. Если argv [0] имеет одну или несколько косых черт, он используется без изменений. В противном случае он должен быть вызван из пути оболочки, поэтому мы ищем $PATH для именованного исполняемого файла и используем его. Если исполняемый файл не найден в $PATH (или не было среды $PATH переменная) используется исходная строка argv [0].

Далее, проверяемое место проверки проверяется, является ли оно символическим ссылка. Если это так, ссылка преследуется (правильно интерпретируя относительную путь, если он найден) и используется каталог целевого объекта ссылки.

Сделайте пару тестов, чтобы проверить это:

Если argv [0] имеет одну или несколько косых черт, он используется без изменений.

[[email protected] ~]$ sudo /usr/local/bin/python what_python.py
/usr/local/bin/python
2.7.6 (default, Feb 27 2014, 17:05:07) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-54)]

Ok.

Если исполняемый файл не был найден в $PATH (или не было переменной среды $PATH), используется исходная строка argv [0].

[[email protected] ~]$ sudo PATH= python what_python.py
<empty line>
2.7.6 (default, Feb 27 2014, 17:05:07) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-54)]

Неправильно. В этом случае утверждение из sys-модуля documentation истинно. Если Python не может получить реальный путь к его исполняемому файлу, sys.executable будет пустая строка или None..

Посмотрим, добавляет ли добавление двоичного кода python в PATH (после удаления sudo) проблема:

[[email protected] ~]$ sudo PATH=$PATH python what_python.py
/usr/local/bin/python
2.7.6 (default, Feb 27 2014, 17:05:07) 
[GCC 4.1.2 20080704 (Red Hat 4.1.2-54)]

Он делает.

Связанный:

  • Проблема с Python 7774 - sys.executable: неправильное расположение, если изменен аргумент нулевой команды.
  • Проблема с Python 10835 - sys.executable default и altinstall
  • список рассылки python-dev thread - к более строгому определению sys.executable
  • Stackoverflow вопрос - как найти местоположение исполняемого файла в C

Ответ 2

Я думаю, что /usr/local/bin/python - исполняемый файл. Строка версии почти наверняка скомпилирована в python, поэтому она вряд ли ошибается. Посмотрите документацию для sys.executable:

sys.executable

Строка, дающая абсолютный путь исполняемого двоичного файла для интерпретатора Python, в системах, где это имеет смысл. Если Python не может получить реальный путь к его исполняемому файлу, sys.executable будет пустой строкой или None.

Тот факт, что python может быть не в состоянии получить это, предполагает, что он выполняет свой собственный поиск PATH с помощью PATH sudo set (который согласно моему ответу на предыдущий вопрос не совпадает с один из них использовался для поиска исполняемого файла).

Единственный способ быть уверенным здесь в том, чтобы прорвать реализацию python, но обычно я бы сказал, что строка версии, скорее всего, будет той, которой вы можете доверять. С другой стороны, sudo использует execve для выполнения команды (по крайней мере, согласно странице man). Вы должны указать полный путь исполняемого файла к execve (некоторые из вариантов exec выполняют свой собственный поиск PATH, этого нет). Поэтому для python не нужно создавать sys.executable.

Я не знаю, есть ли способ получить фактический argv[0] для интерпретатора python (sys.argv[0] всегда является именем script или -c), но это было бы интересно видеть. Если это /usr/local/bin/python, это будет ошибкой в ​​python.

Я думаю, что лучше всего просто установить secure_path в /etc/sudoers, надеюсь, тогда вы получите некоторую согласованность.

Update

Фактически execve принимает аргумент для исполняемого пути, а затем массив argv, поэтому argv[0] необязательно /usr/local/bin/python. Вы все еще можете найти это, создав script как:

import time
time.sleep(60)

Затем запустите его и получите ps, чтобы дать вам полный аргумент:

sudo python sleep.py &
ps -o args= -C python

Также, чтобы убедиться, что python выполняется, вы можете сделать:

sudo ls -l /proc/PID/exe

во время работы программы.

Ответ 3

Каждый раз, когда вы запускаете интерпретатор python, оболочка переходит в /usr/bin/python и выполняет его (попробуйте следующее: python -c "import os; print (os.environ ['_'])" ).

Ну тогда, как вы можете видеть себя ln -l | grep python/usr/bin/python - это мягкая ссылка на исполняемый файл интерпретатора python.

Что я сделал:

  • Установите последнюю версию python (перейдите на сайт python, загрузите последний код и configure && make && make install)
  • Проверить расположение исполняемого файла последней версии.
  • Удалить /usr/bin/python софт-ссылку ## (вам потребуется разрешение root Судо)
  • ln -s <location последней исполняемой версии python > /usr/bin/python ## (вполне вероятно, необходимо sudo)
  • Выполнить python из командной строки.
  • import sys

sys.version ## он должен быть последним.