Як я можу перевірити наявність версії Python у програмі, яка використовує нові мовні функції?


239

Якщо у мене є сценарій Python, який вимагає принаймні певної версії Python, який правильний спосіб виграшно вийти з ладу, коли для запуску сценарію використовується більш рання версія Python?

Як мені отримати контроль досить рано, щоб надіслати повідомлення про помилку та вийти?

Наприклад, у мене є програма, яка використовує оператор ternery (новий у 2.5) та блоки "with" (новий у 2.6). Я написав просту маленьку процедуру перевірки версії інтерпретатора, що перше, що закликав би сценарій ... за винятком того, що він не так далеко. Натомість сценарій виходить з ладу під час компіляції python, перш ніж мої підпрограми навіть будуть викликані. Таким чином, користувач сценарію бачить дуже незрозумілі відслідковування помилок синаксису - які, як правило, вимагають від експерта, щоб зробити висновок, що це просто випадок неправильної версії 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

(Спрямування для колеги.)


3
"Перевірте версію python. Проблема полягає в тому, що деякі синтаксиси є незаконними у старих версіях python." Я не розумію, як це проблема. Якщо ви можете перевірити версію, ви можете уникнути помилки синтаксису. Як перевірка версій не застосовується до синтаксису? Чи можете ви уточнити своє запитання?
— С.Лотт

4
@ S.Lott Ні, ви не помиляєтеся, лише у тому, що складність полягає в включенні коду десь там, де він також не буде прочитаний (проаналізований), а також не виконаний - це не відразу видно, як показують відповіді.
— Брендан

7
S.Lott, ви не можете виконати свій тест у старій версії python, оскільки він не компілюється. Натомість ви отримуєте загальну помилку синтаксису. Спробуйте приклад коду з інтерпретатором 2.4, і ви побачите, що не можете потрапити на тест версій.
— Марк Гаррісон

7
@ S.Lott Ну, це залежить від того, що ви вважаєте тривіальним - особисто я б не розглядав можливість створення окремих файлів для різних версій Python або нерестування додаткових процесів тривіально. Я б сказав, що це питання є цінним, особливо якщо ви вважаєте, що Python сповнений акуратних і часто дивних хитрощів - я приїхав сюди з Google, хочучи знати, чи є акуратна відповідь
— Брендан

7
Я думаю, що ми закінчили цю дискусію. Я поставив запитання про те, чого я не знав, як це зробити, і отримав відповідь, яка мені сказала, як це зробити. Я нічого не пропоную, я просто прийняв відповідь Оріпа, яка чудово працює для мене (насправді колега, для якого я спрямовую). Перелив Viva Le Stack!
— Марк Гаррісон

Відповіді:


111

Ви можете протестувати за допомогою eval:

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

Також, with є є в Python 2.5, просто додати from __future__ import with_statement.

EDIT: щоб отримати контроль досить рано, ви можете розділити його на різні .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 *

10
це фантастична відповідь. головне питання, що потребує вирішення, - це те, що програма повинна бути синтаксично правильною, щоб ця версія python навіть почала виконувати, тому використання нового синтаксису не дозволяє програмі запускатись на старих версіях інтерпретатора. навколо цього працює eval
— Autoplectic

7
Якщо пакет встановлюється setuptools, то байтове складання вихідних файлів не вдасться. Крім того, всі спотворення, які створюють повідомлення про помилку під час виконання, здаються трохи безглуздими - чому б не просто документувати вимоги і не залишати це при цьому?
— Джон Махін

2
Зауважте, що якщо ви намагаєтеся перевірити вираз, а не просту заяву, вам потрібно використовувати execзамість eval. Я придумав це, намагаючись написати функцію, яка буде друкувати в stderr і в py2k, і в py3k.
— Xiong Chiamiov

2
Я думаю, що більш чистим варіантом цього рішення було б поставити свої "чеки" в окремий модуль та імпортувати (оберніть importстанцію в спробі / винятком). Зауважте, що вам може знадобитися перевірити інші речі SyntaxError(наприклад, вбудовані функції або доповнення до стандартної бібліотеки)
— Стівен

103

Майте навколо програми обгортку, яка робить наступне.

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, але тоді у вас є деякі регулярні вирази.

І можуть бути більш елегантні способи зробити це.


7
FYI, "cur_version> = req_version" повинен працювати як умовний.
— orip

4
sys.version_infoне є функцією.
— nh2

3
Введення коду всередині успішного умовного подібного є досить поганою практикою, оскільки це непотрібне відступ та додавання логіки. Просто зробіть: if sys.version_info [: 2] <req_version: print "old"; sys.exit () - інакше продовжуйте як завжди.
— timss

1
Це так, як Тім Пітерс каже в "Дзен Пітона": "Квартира краще, ніж вкладена". (Ви можете побачити це, ввівши "імпортувати це" на python)
— Крістофер Шроба

1
@ChristopherShroba Дякую за import this. Прекрасна диверсія.
— Семюел Хармер

32

Спробуйте

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

Потрібно надати рядок типу "2.3.1". Якщо це не саме те, що ви хочете, є вбудований набір даних, доступний через вбудовану платформу. Те, що ви хочете, має бути десь там.


4
-1: Це не працює, як пояснено в оновленому запитанні. Якщо ви використовуєте будь-який синтаксис з нової версії Python, ваш файл не буде компілюватися, а якщо він не компілюється, він не може запустити і перевірити версію!
— Скотт Гріффітс

1
@ScottGriffiths Запуск print(platform.python_version())замість platform.python_version()!
— Suriyaa

@ScottGriffiths Також замовіть мою відповідь: stackoverflow.com/a/40633458/5157221 .
— Suriyaa

22

Мабуть, найкращий спосіб зробити це порівняння версій - використовувати sys.hexversion. Це важливо, оскільки порівняння кортежів версій не дасть бажаного результату у всіх версіях python.

import sys
if sys.hexversion < 0x02060000:
    print "yep!"
else:
    print "oops!"

Я думаю, що це найелегантніше, але, мабуть, не найлегше зрозуміти іншими дияволами.
— Нік Болтон

5
Чи можете ви пояснити, за яких обставин порівняння кортежів версій не дасть бажаного результату?
— SpoonMeiser

кортежі версії також можуть містити буквено-цифрові значення.
— sorin

8
-1: Це не працює, як пояснено в оновленому запитанні. Якщо ви використовуєте будь-який синтаксис з нової версії Python, ваш файл не буде компілюватися, а якщо він не компілюється, він не може запустити і перевірити версію!
— Скотт Гріффітс

На якій версії / платформі це не вдається?
— sorin

15
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")

7
Майте в виду , що в Python 2.6 і нижче, sys.version_infoце НЕ іменований кортеж. Вам потрібно буде використовувати sys.version_info[0]для основного номера версії та sys.version_info[1]для другорядного.
— coredumperror

9

Відповідь Нікакіна на 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 від Suriyaa Kudo


Корисні ресурси


7

Набори стали частиною основної мови в Python 2.4, щоб залишатися сумісними назад. Я зробив це ще тоді, що буде працювати і для вас:

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

3
краще перевірити функцію замість версії, ні? try: set except NameError: from sets import Set as set
— orip

@orip: Чому? Якщо ви знаєте, в яку версію була введена функція, як набори тут, просто скористайтеся вище кодом. Нічого поганого в цьому немає.
— Андре

7

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

Питання, на яке я відповідаю, таке: Як отримати контроль досить рано, щоб надсилати повідомлення про помилку перед запуском програми ?

Я можу відповісти на це набагато інакше, ніж на інші пости. Здається, відповіді поки що намагаються вирішити ваше питання з боку 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


Для тих, хто голосує вниз, будь ласка, не бійтеся пояснювати причини.
— DevPlayer

Саме я шукав. Дякую! Що %% H?
— Clocker

@Clocker python.exe -V поверне рядок "Python 2.7". Консоль ставить рядок "Python" в %% G, а рядок "2.7" в автоматично створеному os var %% H (наступна літера після G). Відлуння %% H | знайдіть "2.7" труби "2.7" в команді DOS знайдіть "2.7", яка встановлює рівень помилки на 1, якщо %% H знайдено в "2.7". Цей рівень помилок, в результаті якого команда find DOS знайдеться 1, дозволить нам перейти до пакетної мітки DOS: Python27
— DevPlayer

3
import sys
sys.version

буде отримувати відповідь, як це

'2.7.6 (за замовчуванням, 26 жовтня 2016, 20:30:19) \ n [GCC 4.8.4]'

тут 2.7.6 - версія


2

Як зазначалося вище, синтаксичні помилки трапляються під час компіляції, а не під час виконання. Хоча 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.


2
Серйозно? Дублювати код лише для того, щоб ви могли змінити деякі незначні структури? Гидота. Дуже ривок. А що стосується a and b or cзамість цього b if a else c, це не рівнозначно; якщо bфальшивий, то він вийде з ладу, aа не продукує b.
— Кріс Морган

1
Я не пропоную дублювати код, пропоную створити обгорткові функції для коду, що відповідає версії, підписи якого не змінюються в різних версіях, і розміщувати ці функції у конкретних версіях модулях. Я кажу про функції, довжиною яких може бути від 1 до 5 рядків. Це правда, що a і b або c не те саме, що b, якщо інакше c у випадках, коли b може оцінюватися як хибне. Тож я здогадуюсь, що якщоAThenBElseC (a, b, c) у common_ops_2_4.py має бути довжиною 2 або 3 рядки замість 1. Цей метод фактично зменшує ваш повний код шляхом інкапсуляції загальних ідіом у функції.
— Шавай

2

Помістіть наступне у верхній частині файлу:

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...

1
скоріше використовуйтеsys.version_info < (2, 7)
— Антті Хаапала

@AnttiHaapala для мене це ідеально, ви можете пояснити, чому порівнювати sys.version_infoтип з тюленом?
— jjj

@jjj sys.version_info раніше був кортежем; наприклад (2, 4, 6, 'final', 0); лише в Python 3 та 2.7 він був змінений на окремий тип, який, тим не менш, можна порівняти з кортежами.
— Антті Хаапала

@AnttiHaapala Мені подобається такий підхід краще, ніж мій ... дякую!
— jml

1

Я думаю, що найкращий спосіб - перевірити функціональність, а не версії. В одних випадках це банально, в інших не так.

наприклад:

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

Поки ти достатньо конкретний у використанні блоків спробу / за винятком блоків, ти можеш охопити більшість баз.


2
Ти маєш рацію. Ось що він запитав, як це зробити - іноді тестування функцій у версії Y навіть не компілюється в байт-код у версії X, тому це неможливо зробити безпосередньо.
— Оріп

1

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

Мені подобається ідея 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") )

Так, я знаю, що остаточна лінія декодування / смужки жахлива, але я просто хотів швидко схопити номер версії. Я збираюся це уточнити.

Наразі це працює досить добре для мене, але якщо хтось може вдосконалити його (або сказати мені, чому це жахлива ідея), це теж було б круто.


1

Для автономних скриптів python працює наступний трюк докстрингу модуля для забезпечення версії 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
[...]

Це також має працювати з відсутнім виконуваним пітоном, але має залежність від grep. Дивіться тут для тла.


0

Ви можете перевірити за допомогою 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 , навіть якщо це менш доброзичливо.

Я сподіваюся, що це вам допомогло!


0

Я розкриваю чудову відповідь Ахана, яка друкує корисне повідомлення ще до того, як сценарій 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.


-2

Як щодо цього:

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

5
-1: Це не працює, як пояснено в оновленому запитанні. Якщо ви використовуєте будь-який синтаксис з нової версії Python, ваш файл не буде компілюватися, а якщо він не компілюється, він не може запустити і перевірити версію!
— Скотт Гріффітс

-3

Проблема досить проста. Ви перевірили, чи версія менше 2,4, не менше або дорівнює . Отже, якщо версія Python становить 2,4, це не менше 2,4. Що ви повинні були:

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

, ні

    if sys.version_info < (2, 4):

4
прочитайте параграф 3 та оновлення. Ви не збираєтеся виконувати цей код, оскільки ваш код не компілюється на 2.4, якщо ви використовуєте нові мовні конструкції.
— Марк Гаррісон

Менше, ніж було добре, просто не вистачало овалів.
— Крейг
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.