After Python2 Death, How to Convert Your Code Into Python3

With 2to3 library

Since January 1, 2020 Python2 has been no longer supported nor maintained. This means no improvement to Python2 should happen after that day even if someone finds a security problem in it. If you still have projects/applications written in Python2, you should start converting them. In this tutorial, I’ll discuss how to do that with 2to3.

2to3 (read like two to three) is a Python2 to Python3 code translation library.

Python3’s Print & Input

Let’s see example.py

def greet(name):
print "Hello, {0}!".format(name)

name = raw_input()
greet(name)

It’s clearly written in Python2 so when we run it with Python2, we get a prompt asking for your name:

$python2 example.py What's your name? Ezz Hello, Ezz! while with Python3, you get a SyntaxError $ python3 example.py
File "/<path>/example.py", line 2
print "Hello, {0}!".format(name)
^
SyntaxError: invalid syntax

Let’s convert that code into Python3 with 2to3 automated Python translation from version 2 to 3. Check first that it already exists in your machine. If not, you can install it with pip:

$pip install 2to3 and then we can run it on example.py and see the difference between the original Python2 code version and Python3 that we want to refactor to: $ 2to3 example.py
RefactoringTool: Skipping optional fixer: buffer
RefactoringTool: Skipping optional fixer: idioms
RefactoringTool: Skipping optional fixer: set_literal
RefactoringTool: Skipping optional fixer: ws_comma
RefactoringTool: Refactored example.py
--- example.py  (original)
+++ example.py  (refactored)
@@ -1,6 +1,6 @@
def greet(name):
-    print "Hello, {0}!".format(name)
+    print("Hello, {0}!".format(name))

-name = raw_input()
+name = input()
greet(name)
RefactoringTool: Files that need to be modified:
RefactoringTool: example.py

But this printed the difference between the source code written in Python2 and the refactored one. If you want to write the changes to the same file you can use the write option -w

$2to3 -w example.py Now example.py is the Python3 version of the code: def greet(name): print("Hello, {0}!".format(name)) print("What's your name? ") name = input() greet(name) If you run it with Python3, you get the same response as Python2: $ python3 example.py
What's your name?
Ezz
Hello, Ezz!

And don’t worry about the Python2 script because it’s backed up in this file example.py.bak

Python3’s Range Function

Let’s see another example experimenting with this file xrange_example.py:

for i in xrange(5):
print i

What really 2to3 program does, is that it has a list of fixers and applies transformation on the Python2.x code to be converted into valid Python3.x code. By default, it applies almost all fixers to the source code. You can list them by the 2to3 -l command. But if you need to choose a specific fixer, you can use the -f option and choose whatever fixer you like.

For example, let’s fix the xrange function on the latest example

$2to3 -f xrange xrange_example.py This will only convert xrange to range and forget about the rest which is the print statement. If you include all as the value of fixers or just ignore the fixer option at all, you get both xrange and print transformed into Python3 code. To exclude specific fixer, you can use -x option: $ 2to3 -x xrange xrange_example.py
...
--- xrange_example.py   (original)
+++ xrange_example.py   (refactored)
@@ -1,2 +1,2 @@
for i in xrange(5):
-    print i
+    print(i)
...

2to3 Drawback

Honestly, this library can’t solve all your problems with Python2.

For example, let’s see this time conversion using datetime standard library:

import datetime

print((datetime.datetime.fromtimestamp(1621718115)))

This code simply converts the timestamp to a datetime format and it runs by default in both Python2 and Python3 according to the time zone that your machine has.

But what if you want to change the timezone? you’ll then need to pass the value of the region to the TZ environment variable like this:

import datetime
import os

os.environ['TZ'] = 'UTC'
print((datetime.datetime.fromtimestamp(1621718115)))

And when you run it and assuming your timezone is not UTC, Python2 will output the UTC datetime. The problem is with Python3 because it will output the default timezone of your system.

So we should add this line time.tzset() to set the new timezone:

import datetime
import os
import time

os.environ['TZ'] = 'UTC'
time.tzset()
print((datetime.datetime.fromtimestamp(1621718115)))

Conclusion

2to3 should be your first step to automatically convert your Python2 application/library into Python3. On the other hand, it’s not a silver bullet; you still need to patiently review the differences and make sure you do unit tests.