Die Tage sind gezählt: End of Life für Python 2

Der Support für Python 2 endet 2019, und es ist höchste Zeit für Entwickler, Maßnahmen zum Umstieg zu ergreifen.

Sprachen  –  138 Kommentare

Nach langer Zeit ist es nun soweit: Die Unterstützung von Python 2 wird eingestellt. Am 3. Dezember 2008 erblickte Python 3 das Licht der Welt. Gut zehn Jahre bestand die Koexistenz der inkompatiblen Versionen Python 2 und Python 3. Dieser unglückliche Zwischenzustand neigt sich dem Ende zu: Wie 2015 angekündigt, endet der Support für Python 2 am 31. Dezember 2019.

Python 2 oder Python 3, das ist die Frage

Die jahrelang andauernde doppelte Unterstützung für beide Python-Versionen wirft die Frage auf, welche Strategien sich entwickelt haben, mit dem Zwischenzustand umzugehen. Kurz gesagt, kamen alle denkbaren Strategien zum Einsatz. Viele haben bestehende Projekte einfach in Python 2 weitergeführt. Entwickler haben teilweise sogar neue Projekte in Python 2 begonnen. Dafür gab es viele Gründe: Zum einen sprachen sie fließend Python 2, zum anderen waren notwendige Bibliotheken noch nicht nach Python 3 portiert. Zudem haben Kunden durchaus Python-2-Code verlangt. Manche Teams haben einfach die Codebasis in Python 2 und Python 3 gesplittet und mussten beide Versionen pflegen.

Zwar sind Python 2 und Python 3 inkompatibel, es ist jedoch durchaus möglich, Python-Code umzuschreiben, sodass er sowohl mit Python 2 als auch mit Python 3 arbeitet. Für diesen Schritt helfen die Skripte futurize und modernize. Letzteres ist bei der Änderung des Python-Codes konservativer als ersteres. Für die Modifikation müssen Projekte zwei Voraussetzung mitbringen: Eine Testabdeckung und Code, der auf Python 2.7 migriert ist.

Der Spickzettel "Cheat Sheet: Writing Python 2-3 compatible code" beschreibt im Detail, welche syntaktischen Unterschiede zwischen Python 2 und Python 3 bestehen und wie sich Python-Code schreiben lässt, den sowohl Python 2 als auch Python 3 unterstützt.

Migration von Python 2 nach Python 3

Zum Portieren von Python-2-Code nach Python 3 zeichnet sich ein klar definierter Pfad ab (s. Abb. 1), wobei Entwickler nach jedem Schritt den Code testen und Probleme beseitigen müssen.

Der Migrationspfad von Python 2 nach Python 3 (Abb. 1)

Folgende Codezeilen sollen als Beispiel für die Migration von Python 2 nach 3 dienen. Alle Zeilen des Beispiels verwenden funktionale Komponenten von Python, da sich bei diesen Built-in-Funktionen einige Veränderungen vollzogen haben.

print "sum of the integers: " , 
apply(lambda a,b,c: a+b+c , (2, 3, 4))

print "factorial of 10 :",
reduce(lambda x,y: x*y, range(1,1 1) )

print "titles in text: ", filter( lambda word: word.istitle(),
"This is a long Test".split())

print "titles in text: ",
[ word for word in "This is a long Test".split()
if word.istitle()]

Die erste Funktion berechnet die Summe der drei Zahlen 2, 3 und 4, indem sie die Argumente auf die Lambda-Funktion anwendet. Die Built-in-Funktion reduce() reduziert sukzessive die Liste aller Zahlen von 1 bis einschließlich 10, indem sie das Ergebnis der letzten Multiplikation mit der nächsten Zahl aus der Sequenz multipliziert. Die letzten zwei Funktionen filtern aus dem String alle Wörter heraus, die mit einem Großbuchstaben beginnen. Der Code funktioniert bereits unter Python 2.6, sodass nur noch die Schritte 3 und 4 für die Portierung zu vollziehen sind.

Ein Aufruf des Python-2.7-Interpreters mit der Option -3 zeigt die Inkompatibilitäten zur Version 3 (s. Abb. 2): Sowohl apply() als auch reduce() sind in Python 3 keine Built-in-Funktionen mehr.

Der Interpreter zeigt die Inkompatibilitäten zu Python 3 (Abb. 2).

Der Code ist schnell repariert, damit die Deprecation-Warnungen unterbleiben:

print "sum of the integers: " , 
(lambda a,b,c: a+b+c)(*(2, 3, 4))

import functools

print "factorial of 10 :",
functools.reduce(lambda x,y: x*y, range(1, 11) )

print "titles in text: ",
filter( lambda word: word.istitle(),
"This is a long Test".split())

print "titles in text: ",
[ word for word in "This is a long Test".split()
if word.istitle()]

Das Script 2to3.py (s. Abb. 3).erweist sich bei der Korrektur des Python-2-Codes als hilfreich, denn es erzeugt im letzten Schritt automatisch Code für Python 3. Dazu bietet das Tool mehrere Optionen an.

2to3 zeigt beim Aufruf mit dem --help-Switch die Optionen (Abb. 3.).

Der direkte Weg besteht darin, die Ursprungsdatei zu überschreiben: python <path to 2to3.py> port.py -w. Das Ergebnis ist der nach Python 3 portierte Quellcode. Interessanterweise hat der Codegenerator den filter()-Ausdruck durch eine äquivalente List-Comprehension ersetzt:

print("sum of the integers: " , 
(lambda a,b,c: a+b+c)(*(2,3,4)))

import functools

print("factorial of 10 :",
functools.reduce(lambda x,y: x*y, list(range(1,11)) ))

print("titles in text: ",
[word for word in "This is a long Test".split()
if word.istitle()])

print("titles in text: ",
[word for word in "This is a long Test".split()
if word.istitle()])