Как я могу проверить версию Python в программе, которая использует новые возможности языка?

Если у меня есть сценарий Python, для которого требуется как минимум определенная версия Python, как правильно выполнить корректный сбой при использовании более ранней версии Python для запуска сценария?

Как получить контроль достаточно рано, чтобы выдать сообщение об ошибке и выйти?

Например, у меня есть программа, которая использует оператор ternery (новый в 2.5) и блоки "with" (новый в 2.6). Я написал простую небольшую подпрограмму проверки версии интерпретатора, которая является первой вещью, которую вызовет сценарий... за исключением того, что она не зашла так далеко. Вместо этого сценарий завершается ошибкой во время компиляции Python, прежде чем мои процедуры будут даже вызваны. Таким образом, пользователь сценария видит некоторые очень неясные трассировки ошибок Synax, которые в значительной степени требуют от эксперта, чтобы сделать вывод, что это просто случай запуска неправильной версии Python.

Я знаю, как проверить версию Python. Проблема в том, что какой-то синтаксис недопустим в старых версиях Python. Рассмотрим эту программу:

import sys
if sys.version_info < (2, 4):
    raise "must use python 2.5 or greater"
else:
    # syntax error in 2.4, ok in 2.5
    x = 1 if True else 2
    print x

При запуске под 2.4, я хочу этот результат

$ ~/bin/python2.4 tern.py 
must use python 2.5 or greater

а не этот результат

$ ~/bin/python2.4 tern.py 
  File "tern.py", line 5
    x = 1 if True else 2
           ^
SyntaxError: invalid syntax

(Ченнелинг для коллеги.)

19 ответов

Решение

Вы можете проверить с помощью eval:

try:
  eval("1 if True else 2")
except SyntaxError:
  # doesn't have ternary

Также, with доступно в Python 2.5, просто добавьте from __future__ import with_statement,

РЕДАКТИРОВАТЬ: чтобы получить контроль достаточно рано, вы можете разделить его на разные .py файлы и проверьте совместимость в главном файле перед импортом (например, в __init__.py в упаковке):

# __init__.py

# Check compatibility
try:
  eval("1 if True else 2")
except SyntaxError:
  raise ImportError("requires ternary support")

# import from another module
from impl import *

Имейте обертку вокруг своей программы, которая делает следующее.

import sys

req_version = (2,5)
cur_version = sys.version_info

if cur_version >= req_version:
   import myApp
   myApp.run()
else:
   print "Your Python interpreter is too old. Please consider upgrading."

Вы также можете рассмотреть возможность использования sys.version()Если вы планируете встретить людей, которые используют интерпретаторы Python до 2.0, но у вас есть регулярные выражения.

И могут быть более элегантные способы сделать это.

Пытаться

импортная платформа
platform.python_version()

Должен дать вам строку вроде "2.3.1". Если это не совсем то, что вам нужно, есть богатый набор данных, доступных через встроенную платформу. То, что вы хотите, должно быть где-то там.

Вероятно, лучший способ сделать это сравнение версий - это использовать sys.hexversion, Это важно, потому что сравнение кортежей версий не даст желаемого результата во всех версиях Python.

import sys
if sys.hexversion < 0x02060000:
    print "yep!"
else:
    print "oops!"
import sys    
# prints whether python is version 3 or not
python_version = sys.version_info.major
if python_version == 3:
    print("is python 3")
else:
    print("not python 3")

Ответ от Nykakin в AskUbuntu:

Вы также можете проверить версию Python из самого кода, используя platform модуль из стандартной библиотеки.

Есть две функции:

  • platform.python_version() (возвращает строку).
  • platform.python_version_tuple() (возвращает кортеж).

Код Python

Создайте файл, например: version.py)

Простой способ проверить версию:

import platform

print(platform.python_version())
print(platform.python_version_tuple())

Вы также можете использовать eval метод:

try:
  eval("1 if True else 2")
except SyntaxError:
  raise ImportError("requires ternary support")

Запустите файл Python из командной строки:

$ python version.py 
2.7.11
('2', '7', '11')

Вывод Python с CGI через сервер WAMP в Windows 10:

Снимок экрана 2016-11-16 14.39.01 от Сурия Кудо


Полезные ресурсы

Наборы стали частью основного языка в Python 2.4 для обеспечения обратной совместимости. Я сделал это тогда, что будет работать и для вас:

if sys.version_info < (2, 4):
    from sets import Set as set

Хотя вопрос таков:как получить контроль достаточно рано, чтобы выдать сообщение об ошибке и выйти?

Вопрос, на который я отвечаю:как получить контроль достаточно рано, чтобы выдать сообщение об ошибке перед запуском приложения?

Я могу ответить на это по-другому, чем другие посты. Кажется, ответы до сих пор пытаются решить ваш вопрос изнутри Python.

Я говорю, сделайте проверку версии перед запуском Python. Я вижу, ваш путь - Linux или Unix. Однако я могу предложить вам только сценарий Windows. Я представляю, что адаптировать его к синтаксису сценариев Linux не будет слишком сложно.

Вот сценарий DOS с версией 2.7:

@ECHO OFF
REM see http://ss64.com/nt/for_f.html
FOR /F "tokens=1,2" %%G IN ('"python.exe -V 2>&1"') DO ECHO %%H | find "2.7" > Nul
IF NOT ErrorLevel 1 GOTO Python27
ECHO must use python2.7 or greater
GOTO EOF
:Python27
python.exe tern.py
GOTO EOF
:EOF

Это не запускает какую-либо часть вашего приложения и, следовательно, не вызовет исключение Python. Он не создает временный файл и не добавляет никаких переменных среды ОС. И это не заканчивает ваше приложение исключением из-за различных правил синтаксиса версий. Это три менее возможных точки безопасности доступа.

FOR /F линия является ключом.

FOR /F "tokens=1,2" %%G IN ('"python.exe -V 2>&1"') DO ECHO %%H | find "2.7" > Nul

Для нескольких версий Python проверьте URL: http://www.fpschultze.de/modules/smartfaq/faq.php?faqid=17

И моя версия взлома:

[MS скрипт; Проверка версии Python перед запуском модуля Python] http://pastebin.com/aAuJ91FQ

import sys
sys.version

будет получать ответ, как это

'2.7.6 (по умолчанию, 26 октября 2016 г., 20:30:19) \n[GCC 4.8.4]'

здесь 2.7.6 - версия

Как отмечено выше, синтаксические ошибки возникают во время компиляции, а не во время выполнения. Хотя Python является "интерпретируемым языком", код Python фактически не интерпретируется напрямую; он компилируется в байтовый код, который затем интерпретируется. Существует этап компиляции, который происходит, когда модуль импортируется (если нет уже скомпилированной версии в виде файла.pyc или.pyd), и именно тогда вы получаете ошибку, а не (совершенно точно), когда ваш код работает.

Вы можете отложить шаг компиляции и сделать это во время выполнения для одной строки кода, если хотите, используя eval, как отмечалось выше, но я лично предпочитаю избегать этого, потому что это заставляет Python выполнять потенциально ненужная компиляция во время выполнения, с одной стороны, и с другой, она создает то, что для меня ощущается как беспорядок в коде. (Если вы хотите, вы можете сгенерировать код, который генерирует код, который генерирует код - и у вас есть абсолютно невероятное время, чтобы изменить и отладить его через 6 месяцев.) Так что вместо этого я бы порекомендовал что-то вроде этого:

import sys
if sys.hexversion < 0x02060000:
    from my_module_2_5 import thisFunc, thatFunc, theOtherFunc
else:
    from my_module import thisFunc, thatFunc, theOtherFunc

... что я бы сделал, даже если бы у меня была только одна функция, использующая новый синтаксис, и она была очень короткой. (На самом деле я бы принял все разумные меры, чтобы минимизировать количество и размер таких функций. Я мог бы даже написать функцию типа ifTrueAElseB(cond, a, b) с этой единственной строкой синтаксиса в нем.)

Еще одна вещь, на которую, возможно, стоит обратить внимание (на которую я немного удивлен, пока никто не указал), это то, что хотя ранние версии Python не поддерживали такой код, как

value = 'yes' if MyVarIsTrue else 'no'

..это действительно поддерживает код как

value = MyVarIsTrue and 'yes' or 'no'

Это был старый способ написания троичных выражений. У меня еще не установлен Python 3, но, насколько мне известно, этот "старый" способ работает до сих пор, так что вы можете сами решить, стоит ли его условно использовать новый синтаксис, если вам нужно поддерживать использование более старых версий Python.

Поместите следующее в самый верх вашего файла:

import sys

if float(sys.version.split()[0][:3]) < 2.7:
    print "Python 2.7 or higher required to run this code, " + sys.version.split()[0] + " detected, exiting."
    exit(1)

Затем продолжите с нормальным кодом Python:

import ...
import ...
other code...

Для автономных сценариев Python работает следующий трюк docstring модуля для принудительного применения версии Python (здесь v2.7.x) (протестировано на *nix).

#!/bin/sh
''''python -V 2>&1 | grep -q 2.7 && exec python -u -- "$0" ${1+"$@"}; echo "python 2.7.x missing"; exit 1 # '''

import sys
[...]

Это должно обрабатывать и отсутствующий исполняемый файл Python, но зависит от grep. Смотрите здесь для фона.

Я думаю, что лучший способ - это проверить функциональность, а не версии. В некоторых случаях это тривиально, а в других - нет.

например:

try :
    # Do stuff
except : # Features weren't found.
    # Do stuff for older versions.

Если вы достаточно конкретны в использовании блоков try/ Кроме того, вы можете охватить большинство ваших баз.

Я только нашел этот вопрос после быстрого поиска, пытаясь решить проблему самостоятельно, и я придумал гибрид, основанный на нескольких предложениях выше.

Мне нравится идея DevPlayer об использовании скрипта-обертки, но недостатком является то, что вы заканчиваете тем, что поддерживаете несколько оберток для разных ОС, поэтому я решил написать обертку на python, но использовать ту же базовую логику "захватить версию, запустив exe" и придумал это.

Я думаю, что это должно работать для 2.5 и далее. Я проверил это на 2.66, 2.7.0 и 3.1.2 на Linux и 2.6.1 на OS X до сих пор.

import sys, subprocess
args = [sys.executable,"--version"]

output, error = subprocess.Popen(args ,stdout = subprocess.PIPE, stderr = subprocess.PIPE).communicate()
print("The version is: '%s'"  %error.decode(sys.stdout.encoding).strip("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLMNBVCXZ,.+ \n") )

Да, я знаю, что финальная строка декодирования / полосы ужасна, но я просто хотела быстро получить номер версии. Я собираюсь уточнить это.

Пока это работает достаточно хорошо для меня, но если кто-то сможет улучшить его (или скажите мне, почему это ужасная идея), это тоже будет круто.

Простой способ напечатать полезное сообщение, если пользователь запускает ваш скрипт Python 3 с исполняемым файлом Python 2:

Поместите это как первую строку кода:

      f' Error: This script requires Python 3.6 or later.'

Он ничего не делает в Python 3.6+ (когда были введены f-строки), но не компилируется в Python 2 и выводит на консоль:

  Файл "test.py", строка 34
    f 'Ошибка: Ошибка: для этого скрипта требуется Python 3.6 или более поздней версии.'
                                                                         ^
SyntaxError: неверный синтаксис

Я расширяю превосходный ответ Ахана, который печатает полезное сообщение еще до того, как скрипт Python будет скомпилирован.

Если вы хотите убедиться, что скрипт выполняется с Python 3.6 или новее, добавьте эти две строки в начало вашего скрипта Python:

#!/bin/sh
''''python3 -c 'import sys; sys.exit(sys.version_info < (3, 6))' && exec python3 -u -- "$0" ${1+"$@"}; echo 'This script requires Python 3.6 or newer.'; exit 1 # '''

(Примечание. Вторая строка начинается с четырех одинарных кавычек и заканчивается тремя одинарными кавычками. Это может показаться странным, но это не опечатка.)

Преимущество этого решения в том, что print(f'Hello, {name}!') не вызовет SyntaxError если используется версия Python старше 3.6. Вместо этого вы увидите это полезное сообщение:

This script requires Python 3.6 or newer.

Конечно, это решение работает только на Unix-подобных оболочках и только тогда, когда скрипт вызывается напрямую (например: ./script.py) и с установленными битами разрешений eXecute.

Вы можете проверить с sys.hexversion или же sys.version_info,

sys.hexversion не очень удобен для человека, потому что это шестнадцатеричное число. sys.version_info это кортеж, поэтому он более дружелюбен к человеку.

Проверьте Python 3.6 или новее с sys.hexversion:

import sys, time
if sys.hexversion < 0x30600F0:
    print("You need Python 3.6 or greater.")
    for _ in range(1, 5): time.sleep(1)
    exit()

Проверьте Python 3.6 или новее с sys.version_info:

import sys, time
if sys.version_info[0] < 3 and sys.version_info[1] < 6:
    print("You need Python 3.6 or greater.")
    for _ in range(1, 5): time.sleep(1)
    exit()

sys.version_info является более дружественным к человеку, но принимает больше персонажей. Я бы порекомендовал sys.hexversionхотя это менее дружественно к человеку.

Я надеюсь, что это помогло вам!

Как насчет этого:

import sys

def testPyVer(reqver):
  if float(sys.version[:3]) >= reqver:
    return 1
  else:
    return 0

#blah blah blah, more code

if testPyVer(3.0) == 1:
  #do stuff
else:
  #print python requirement, exit statement

Проблема довольно проста. Вы проверили, была ли версия меньше 2,4, не меньше или равна. Так что, если версия Python 2.4, она не меньше 2.4. Что вы должны были иметь:

    if sys.version_info **<=** (2, 4):

не

    if sys.version_info < (2, 4):
Другие вопросы по тегам