Как изящно справиться с неудачными импортом будущих функций (__future__) из-за старой версии интерпретатора?

Как вы изящно справляетесь с неудачным импортом в будущем? Если пользователь работает с Python 2.5 и первый оператор в моем модуле:

from __future__ import print_function

Компиляция этого модуля для Python 2.5 завершится неудачно с:

  File "__init__.py", line 1
    from __future__ import print_function
SyntaxError: future feature print_function is not defined

Я хотел бы сообщить пользователю, что ему нужно перезапустить программу с Python> = 2.6, и, возможно, предоставить некоторые инструкции о том, как это сделать. Однако, чтобы процитироватьОПТОСОЗ 236:

Единственные строки, которые могут появляться перед атрибутом future_statement:

Строка документации модуля (если есть).Комментарии.Пустые строкиДругие будущие заявления.

Поэтому я не могу сделать что-то вроде:

import __future__

if hasattr(__future__, 'print_function'):
    from __future__ import print_function
else:
    raise ImportError('Python >= 2.6 is required')

Потому что это дает:

  File "__init__.py", line 4
    from __future__ import print_function
SyntaxError: from __future__ imports must occur at the beginning of the file

Этот фрагмент из PEP, похоже, дает надежду сделать это встроенным:

Q: Я хочу обернуть future_statements в блоки try / кроме, чтобы я мог использовать другой код в зависимости от того, какую версию Python я использую. Почему я не могу?

A: Извините! попытка / исключение - это функция времени выполнения; future_statements - это, прежде всего, трюки во время компиляции, и ваша попытка / исключение происходит спустя много времени после завершения компиляции. То есть к тому времени, когда вы попытаетесь / исключить, семантика, действующая для модуля, уже сделана. Так как попытка / кроме не будет выполнять то, что онвыглядит Как это должно быть достигнуто, это просто не разрешено. Мы также хотим, чтобы эти специальные заявления были очень легко найти и распознать.

Обратите внимание, что выМожно импортируйте __future__ напрямую и используйте содержащуюся в нем информацию вместе с sys.version_info, чтобы выяснить, в каком выпуске вы работаете, относительно статуса данной функции.

Идеи?

 rundekugel20 янв. 2016 г., 14:54
Вы можете использовать простойif во время выполнения. Также дляimport.
 sdaau26 апр. 2013 г., 19:05
Я только столкнулся с этой проблемой, и я не могу перестать задавать себе вопрос: если "future_statements - это в основном трюки времени компиляции" - тогда почему Python не вводит#ifdef как трюк во время компиляции, так что я могу справиться со всем этим__future__ вещи изящно в одном файле? AAAARRGGGHHH .....

Ответы на вопрос(3)

Решение Вопроса

«Я хотел бы сообщить пользователю, что ему нужно перезапустить программу с Python> = 2. и, возможно, предоставить некоторые инструкции о том, как это сделать».

Разве для этого не нужен файл README?

Вот твой вариант. «Обертка»: маленький кусочек Python, который проверяет среду перед запуском вашей целевой библиотеки.

Файл: appwrapper.py

import sys
major, minor, micro, releaselevel, serial = sys.version_info
if (major,minor) <= (2,5):
    # provide advice on getting version 2. or higher.
    sys.exit(2)
import app
app.main()

Что означает «прямой импорт». Вы можете изучить содержимое__future__, Вы все еще связаны тем фактом, чтоfrom __future__ import print_function это информация для компилятора, но вы можете покопаться перед импортом модуля, который выполняет настоящую работу.

import __future__, sys
if hasattr(__future__, 'print_function'): 
    # Could also check sys.version_info >= __future__. print_function.optional
    import app
    app.main()
else:
    print "instructions for upgrading"
 Antti Haapala06 февр. 2015 г., 20:24
52 голосов, и никто не указал, что'print_function' in __future__ на самом деле не работает ... должно бытьhasattr(__future__, 'print_function')
 porglezomp12 окт. 2014 г., 03:32
@ JürgenA.Erhard Я думаю, что в целом промежуточные переменные добавляют ясности в отношении того, что вы делаете, и поскольку это будет сделано только тогда, когда это действительно не повредит.
 Jürgen A. Erhard04 мар. 2013 г., 14:30
Почему бы не "if sys.version_info [: 2] <= (2, 5)" Более конкретно, я думаю, и мне не нравится создавать переменные, которые используются только один раз (или не используются вообще для всего, после "незначительного" в этом пример)
 Greg Hewgill23 дек. 2008 г., 04:56
Или даже лучше, «если (основной, второстепенный) <= (2, 5)»
 Bruno Bronosky06 апр. 2015 г., 17:58
@porglezomp, я согласен, что явное лучше, чем неявное. В таком случае я думаю, что это более питонmajor, minor = sys.version_info[:2] хотя я часто вижу подчеркивание, используемое для выбрасывания переменныхmajor, minor, _, _, _ = sys.version_info
 Greg Hewgill23 дек. 2008 г., 04:51
Возможно, проверьте «если мажор <2 или мажор == 2 и минор <= 5», иначе это не сработает в гипотетической версии 1.7.

но простой метод, который я использовал ранее, состоит в том, чтобы использовать тот факт, что байтовые литералы были введены в Python 2.6, и использовать что-то вроде этого в начале файла:

b'This module needs Python 2.6 or later. Please do xxx.'

Это безопасно для Python 2.6 или новее, ноSyntaxError в любых более ранних версиях. Любой, кто попытается скомпилировать ваш файл, все равно получит ошибку, но он также получит любое сообщение, которое вы хотите дать.

Вы можете подумать, что, поскольку вам придется иметь эту строку после вашегоfrom __future__ import print_function тогда это будет импорт, который генерируетSyntaxError и вы не увидите полезного сообщения об ошибке, но, как ни странно, более поздняя ошибка имеет приоритет. Я подозреваю, что, поскольку ошибка при импорте сама по себе не является синтаксической ошибкой, она не возникает при первом проходе компиляции, и поэтому сначала возникают реальные синтаксические ошибки (но я предполагаю).

Это может не соответствовать вашим критериям «изящности», и это очень специфично для Python 2.6, но это быстро и легко сделать.

 RobM13 авг. 2010 г., 14:20
Элегантный! Я использовал это без назначения только под моей строкой shebang:b'This script requires Python 2.6, this line is a SyntaxError in earlier versions' что является довольно ясным сообщением для пользователя, читающего трассировку исключений, и для программиста, читающего источник.
 Scott Griffiths13 авг. 2010 г., 14:54
@RobM: Рад, что тебе понравилось, и ты прав, что это назначение не нужно - я его уберу.
 Ivan X18 мая 2014 г., 13:52
Я думаю, что это работает потому, что он интерпретирует первый строковый литерал как строку документации для модуля, что является единственной вещью, разрешенной перед оператором «из будущего». Я использовал эту умную технику, а затем обнаружил, что она больше не работает, когда я добавил правильную строку документации, потому что тогда он стал вторым строковым литералом перед "из будущего". Мое решение состояло в том, чтобы поместить уведомление о Python 2.6 на дружественном языке как последнюю строку строки документа, с "" ", тянущимся на той же строке, что делает его все еще отображаемым в SyntaxError, и как безвредную информацию в строке документации.

Просто поместите комментарий в ту же строку с"from __future__ import ...", как это:

from __future__ import print_function, division  # We require Python 2.6 or later

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

    from __future__ import print_function, division  # We require Python 2.6 or later
SyntaxError: future feature print_function is not defined

Ваш ответ на вопрос