Wie gehe ich mit fehlgeschlagenen zukünftigen Feature-Importen (__future__) aufgrund der alten Interpreter-Version ordnungsgemäß um?

69

Wie gehen Sie mit fehlgeschlagenen zukünftigen Feature-Importen um? Wenn ein Benutzer mit Python 2.5 ausgeführt wird und die erste Anweisung in meinem Modul lautet:

from __future__ import print_function

Das Kompilieren dieses Moduls für Python 2.5 schlägt fehl mit:

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

Ich möchte den Benutzer darüber informieren, dass er das Programm mit Python> = 2.6 erneut ausführen muss, und möglicherweise einige Anweisungen dazu geben. Um jedoch PEP 236 zu zitieren :

Die einzigen Zeilen, die vor einer future_statement erscheinen können, sind:

  • Das Modul docstring (falls vorhanden).
  • Bemerkungen.
  • Leerzeilen.
  • Andere future_statements.

Also kann ich so etwas nicht machen:

import __future__

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

Weil es ergibt:

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

Dieser Ausschnitt aus dem PEP scheint Hoffnung zu geben, es inline zu tun:

F: Ich möchte future_statements in try / exception-Blöcke einschließen, damit ich je nach der von mir ausgeführten Python-Version unterschiedlichen Code verwenden kann. Warum kann ich nicht?

Tut mir leid! try / Except ist eine Laufzeitfunktion. future_statements sind in erster Linie Gimmicks zur Kompilierungszeit, und Ihr Versuch / Ihre Ausnahme erfolgt lange nach Abschluss des Compilers. Das heißt, zu dem Zeitpunkt, an dem Sie versuchen / ausnehmen, ist die für das Modul geltende Semantik bereits abgeschlossen. Da der Versuch / außer nicht das erreichen würde, wie es aussehen sollte, ist es einfach nicht erlaubt. Wir möchten auch, dass diese besonderen Aussagen sehr leicht zu finden und zu erkennen sind.

Beachten Sie, dass Sie können direkt importieren __future__, und verwenden Sie die Informationen in sie, zusammen mit sys.version_info, um herauszufinden , wo die Freisetzung Sie unter steht in Bezug auf eine bestimmte Funktion Status ausführen.

Ideen?

cdleary
quelle
3
Ich bin gerade auf dieses Problem gestoßen, und ich kann nicht aufhören, mich zu fragen: Wenn "future_statements hauptsächlich Gimmicks zur Kompilierungszeit sind" - warum führt Python dann kein #ifdefGimmick zur Kompilierungszeit ein, damit ich all diese __future__Dinge in einem einzigen anmutig behandeln kann Datei? AAAARRGGGHHH .....
Sdaau
Sie können einfach ifzur Laufzeit verwenden. Auch für import.
Rundekugel

Antworten:

60

"Ich möchte den Benutzer darüber informieren, dass er das Programm mit Python> = 2.6 erneut ausführen muss, und möglicherweise einige Anweisungen dazu geben."

Ist das nicht das, wofür eine README-Datei ist?

Hier ist deine Alternative. Ein "Wrapper": Ein kleiner Python-Blob, der die Umgebung überprüft, bevor Sie Ihr Ziel aop ausführen.

Datei: appwrapper.py

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

Was "direkter Import" bedeutet. Sie können den Inhalt von überprüfen __future__. Sie sind immer noch an die Tatsache gebunden, dass a from __future__ import print_functionInformationen für den Compiler sind, aber Sie können herumstöbern, bevor Sie das Modul importieren, das die eigentliche Arbeit erledigt.

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"
S.Lott
quelle
Vielleicht testen Sie "wenn Dur <2 oder Dur == 2 und Moll <= 5", sonst würde dies bei einer hypothetischen Version 1.7 nicht fehlschlagen.
Greg Hewgill
20
Oder noch besser: "if (Dur, Moll) <= (2, 5)"
Greg Hewgill
10
Warum nicht "if sys.version_info [: 2] <= (2, 5)" Mehr auf den Punkt, denke ich, und ich mag es nicht, Variablen zu erstellen, die nur einmal verwendet werden (oder überhaupt nicht für alles nach "minor" Beispiel)
Jürgen A. Erhard
@ JürgenA.Erhard Ich denke, dass Zwischenvariablen im Allgemeinen Klarheit darüber schaffen, was Sie tun, und da dies nur getan wird, wenn es nicht wirklich weh tut.
Porglezomp
3
52 Upvotes und niemand wies darauf hin, dass 'print_function' in __future__das eigentlich nicht funktioniert ... sollte seinhasattr(__future__, 'print_function')
Antti Haapala
47

Eine ziemlich hackige, aber einfache Methode, die ich zuvor verwendet habe, besteht darin, die Tatsache auszunutzen, dass Byte-Literale in Python 2.6 eingeführt wurden, und am Anfang der Datei so etwas zu verwenden:

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

Dies ist in Python 2.6 oder höher harmlos, in SyntaxErrorfrüheren Versionen jedoch harmlos . Jeder, der versucht, Ihre Datei zu kompilieren, erhält weiterhin eine Fehlermeldung, aber er erhält auch die Nachricht, die Sie geben möchten.

Sie könnten denken, dass, da Sie diese Zeile nach Ihrem from __future__ import print_functionhaben müssen, es der Import ist, der die generiert, SyntaxErrorund Sie die nützliche Fehlermeldung nicht sehen werden, aber seltsamerweise hat der spätere Fehler Vorrang. Ich vermute, dass der Fehler beim Import nicht wirklich ein Syntaxfehler an sich ist, sondern nicht beim ersten Kompilierungsdurchlauf ausgelöst wird und daher zuerst echte Syntaxfehler auftreten (aber ich vermute).

Dies erfüllt möglicherweise nicht die Kriterien für "anmutig" und ist sehr Python 2.6-spezifisch, aber schnell und einfach durchzuführen.

Scott Griffiths
quelle
6
Elegant! Ich habe dies ohne Zuweisung direkt unter meiner Shebang-Zeile verwendet: Dies b'This script requires Python 2.6, this line is a SyntaxError in earlier versions'ist eine ziemlich klare Nachricht an den Benutzer, der eine Ausnahmespur liest, und an den Programmierer, der die Quelle liest.
RobM
@RobM: Schön, dass es dir gefällt und du hast Recht, dass die Aufgabe nicht benötigt wird - ich werde sie entfernen.
Scott Griffiths
1
Ich denke, der Grund, warum dies funktioniert, ist, dass es das erste Zeichenfolgenliteral als Dokumentzeichenfolge für das Modul interpretiert, was das einzige ist, was vor einer "from future" -Anweisung zulässig ist. Ich benutzte diese clevere Technik und stellte dann fest, dass sie nicht mehr funktionierte, als ich einen richtigen Docstring hinzufügte, weil sie dann vor "from future" ein zweites String-Literal wurde. Meine Lösung bestand darin, den Hinweis zu Python 2.6 in einer benutzerfreundlichen Sprache als letzte Zeile der Dokumentzeichenfolge einzufügen, wobei "" "in derselben Zeile steht. Dadurch wird er weiterhin im SyntaxError angezeigt und als harmlose Information in der Dokumentzeichenfolge.
Ivan X
40

Setzen Sie einfach einen Kommentar in die gleiche Zeile "from __future__ import ..."wie folgt :

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

Da Python die Zeile mit dem Fehler anzeigt, wird beim Versuch, das Modul mit Python 2.5 auszuführen, ein netter, beschreibender Fehler angezeigt:

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