Python 2.7 is scheduled to reach end of life (EOL) sometime in the year 2020. Several companies will continue maintenance on 2.7 for some time. This brings about two problems…
First, no matter how hard they try, over time the versions will differ and there is the strong possibility of vendor lock in. Second, the reason Python changed from Python 2 to Python 3 is because some of the decisions in the design of Python 2 were making change and upgrades to the language almost impossible.
Organizations now face a tough decision. Do we stay with Python 2.7 or do we move to Python 3.x?
When I first looked at this, the tool was 2 to 3. It crashed then. It doesn’t crash but it refused to convert a very simple program. I found it is no longer recommended.
I really wanted to know if there was any new work done in making the conversion. I was told about book, Python 2 and 3 Compatibility, from Apress. The book is about how to use the six and Python future libraries to write code that is executable by both Python 2 and Python 3. The ideas are well presented, and you can use them to write code that is both Python 2 and Python 3 executable. The libraries add a large burden on the programmer; the code is not as clean as I would expect Python to be, and I did not see a clear path moving from this Python 2/3 hybrid to just Python 3. If this was my only choice, I would maintain a Python 2 program and rewrite the code in Python 3. Fortunately, this is not the only solution.
In looking around, I found the site python-future.org. From the Overview:
It allows you to use a single, clean Python 3.x-compatible codebase to support both Python 2 and Python 3 with minimal overhead.
The general approach is to convert the program to run on Python 3 and then import modules that allow the script to run on Python 2. I see this as a possible method of moving from 2 to 3.
There are two steps. “Simple” conversion and Complex conversion. The site suggests doing them in stages. I always did both stages even when the simple state would make the conversion.
At the moment, I am interested in moving a code base in Python 2.7 to Python 3.6. I have divided my review into four sections and will report on what I find in each section.
Figure 1: Review Sections shows the sequence I will follow.
I installed Anaconda 2 for Python 2 and Anaconda 3 for Python 3 on a MacBook pro.
These installations modify the PATH variable to place the anaconda bin directories as the first directory to search. I added the two bash scripts, go2 and go3, seen in Figure 2 and Figure 3, to be able to change between using Python 2, source go2, and Python 3, source go3.
To use the methods of the site, you need to install the future package. This is done with the conda command once for Python 2 and once for Python 3. See Figure 4: Install future
I built the directory structure shown in Figure 5.
Python 2 is used to hold the original Python 2 program and Python 3 is where the converted script is written.
The best documentation on using the conversion program futurize was to execute futurize –help and read the screen.
I chose to use the following options shown in Figure 6.
Figure 7: print commands shows on the left the original python 2 only version and on the right shows the python 3 version with the additions to make it work in python 2. The only change necessary to make it Python 3 only is to remove the from __future__ line. I have added blank line and change the version number to make it easier to read.
Python 3 has dropped the Python 2 function input and renamed raw_input as input. Figure 8 shows the conversion for the raw_input and Figure 9 shows the conversion for the input command.
The input of Python 2 was converted to an input of Python 3. Python 3’s input always returns a string. The string is evaluated by eval which convert string integers to an integer, string floats to a float, and a string to a string. Neat trick and it works under Python 2 and Python 3.
Exceptions have become more orderly, a better inheritance hierarchy, in Python 3. Simple exceptions are shown in Figure 10: Simple Exceptions
There are some differences in the output between Python 2 and Python 3.
The specific exception returned by running the program under Python 3 is different than running it under Python 2. The error messages are the same and that is what most programmers print out. This is a minor problem.
You can create your own exceptions in Python 2 by creating a child of the Exception class.
class MyErrors(Exception): pass
And then call it with a raise statement.
raise MyErrors, “Number must be between 10 and 99”
You would then catch the exceptions with:
except MyError, error_string:
Figure 12: simple_user_exception.py has a script that uses this simple user exception
Figure 13: simple_user_exception.py After Conversion, shows the conversion.
There are several syntax changes and the specific exceptions is still wrong. It works under Python 2 and Python 3 and there is still an easy path to making it Python 3 only.
Figure 14: class_user_exception.py shows an exception written as a child of exception. Most textbooks on Python 2 use the simple method show in Figure 12 and 13.
I was happy to see no changes in the simple class definition. I saw nothing except the syntax changes and the required imports to make this all work.
Very often functions that returned a list in Python 2, return an iterator in Python 3. There has been a shift towards creating an iterator instead of a list. Figure 16: dictionary.py, shows a very simple script testing access to a dictionary and its conversion.
Notice in the Python 3 version that the dictionary.keys(), which in Python 3 returns an iterator, must be enclosed in a list. The first, count = len(list(dictionary.keys())), is not the best programming. This should have been converted to len(dictionary). A much more difficult task than just turning all generators into lists. In the second conversion in the for statement, the for, for very large dictionaries, could error out. It does make the script executable under 2 and 3. Yet there is a potential error when running this code in Python 3. I would have liked this to be highlighted as a potentially dangerous change.
A very promising beginning. I am looking forward to seeing how the constructs listed in Section 2 are converted.