Як я можу перевірити наявність версії 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.