Why Python 2.7 Will Never Die
Python is a programming language originally developed in the late 1980s. Since then, it has seen continuous growth and remains one of the most popular programming languages, especially in data science. Many programmers learn Python as their first language, and it has a wide range of uses. Its second iteration, Python 2.0, landed in 2000, after extensive development throughout the 90s. This version included many features that modern Python developers take for granted, including better memory management and a software development process more closely aligned with other open-source projects.
Python 3 was released in 2008. However, it has not proven as successful as its older sibling. Community adoption has been much slower due to the many backwards-incompatible changes between Python 3 and Python 2.7.
Even today, some thirteen years later, many production workloads are still running Python 2.7. As just one example, the statement
print 'Hello World' is valid code in Python 2.7 but will throw an error in Python 3 unless modified to
print('Hello World'). One can imagine the effort involved in making many such changes across a large codebase.
In this article, we’ll look at some of the reasons why Python 3 has struggled to achieve widespread adoption and why Python 2.7 will likely be around in some form for a very long time.
As the migration from Python 2.7 to Python 3 started to go less and less smoothly, and it became clear that Python 2.7 was going to be around for some time, forks of the main language started to appear.
One such fork, Tauthon, is a “backwards-compatible fork of the Python 2.7.18 interpreter with new syntax, builtins, and libraries backported from Python 3.x.” It includes some incredible features. For instance, you can take advantage of async/await syntax (as shown in this example from Tauthon’s repo):
>>> import types >>> @types.coroutine ... def delayed_print(): ... printme = yield ... print printme ... >>> async def main(): ... while True: ... await delayed_print() ... >>> coro = main() >>> coro.send(None) >>> coro.send("hello") hello >>> coro.send("there") there >>> coro.send("friend") friend
Developers using Tauthon get all the cool add-ons of Python 3 without having to give up on interpreters of or upgrade from Python 2.7. As a fork of the original language, support for it continues, whereas maintenance of the original v2.7 was discontinued in 2020. Because the Tauthon project is maintained and supported, Tauthon users don’t have to worry about their production workloads relying on an unsupported underlying language.
In addition, PyPy is a project that, because of its inherent architecture, has publicly committed to support Python 2 as long as they are around. So, there are multiple options for anyone looking to continue running a version of Python 2.7.
To say that switching from Python 2 to Python 3 “isn’t a priority” would be incorrect. In fact, many, if not most, teams see it as a liability.
Firstly, upgrading from Python 2.7 to Python 3 would likely cause extended downtime or considerably delay implementing new features.
Due to Python 3’s backwards-incompatible changes, there are many code incompatibilities across codebases that traditionally run Python 2.7. Moving to Python 3 would require an audit and rewrite on a scale beyond the ability or desire of many teams, such as those on small open-source projects, long-running academic experiments, or even commercial projects that don’t get much attention.
Given the extent of work required, finding and fixing all of a project’s code incompatibilities and bringing it to a level that’s compatible with the Python 3 interpreter is often put in the backlog. In fact, it might never even be considered at all, especially for projects with limited funding or that are run as side projects.
Attempts have been made to automate this process through various Python packages. However, in most cases, automated code fixes such as those performed by this package should be manually reviewed, especially if the codebase in question doesn’t have solid automated test coverage.
Most Python projects pull in at least one third-party library. To upgrade a codebase to Python 3, all of the libraries that the codebase uses have to be compatible with Python 3 as well. However, those libraries suffer from the same limits that prevent first-party projects from upgrading to Python 3, so many of them still won’t have a version that supports Python 3.
When auditing your application for a potential Python 3 upgrade, it’s important to investigate the libraries it uses for compatibility with Python 3. If one or more isn’t, then it can make any upgrade discussion of your first-party code irrelevant, as that library will hold you back until there is a Python 3 compatible version. There are a wide variety of libraries that haven’t upgraded to Python 3 for one reason or another. It’s important to check if there is Python 3 support for all of your libraries before taking this upgrade process on yourself.
As previously mentioned, automated fixers can make Python 2.7 codebases compatible with Python 3. They work relatively well if you don’t want to wait on a Python 3 port, especially with smaller-scale libraries.
However, it’s worth restating that these automated fixers are not 100% reliable and should always be accompanied by 1) frequent manual testing and 2) a strong set of automated tests. This will check that there are no regressions or other breakages due to the fixer missing specific incompatibilities or some other quirk in the codebase.
On the flip side of the projects mentioned earlier that are either too small or don’t get enough attention to warrant a full-scale migration to Python 3, there are large, mission-critical projects that cannot afford any downtime or any potential hiccups that might come from a Python 2-to-3 migration. Take COBOL, for instance. Many production-grade projects still run COBOL, a decades-old language with many arguably better modern alternatives. These applications continue to run this outdated language because they deal with large amounts of money or are mission-critical in other ways and can simply not tolerate the uncertainty that a major re-platforming would entail.
The platforms that currently run these projects are a known quantity and, in many cases, have been running for years. In much of the same way, migrating to Python 3 might introduce new bugs or even regressions that other developers previously fixed. In addition, many of these legacy or larger-scale projects don’t have adequate test coverage for teams to be confident about such a migration.
For these reasons, these sorts of projects still using 2.7 are likely to continue using it … as long as there is reasonable support for the platform, of course. As mentioned, even though Python 2.7 is no longer officially supported, there are ways to continue running older Python infrastructure without the risk of running an unsupported version of the Python interpreter.
For all the reasons highlighted in this article, Python 2.7 (or some evolution of it) is likely here to stay for the foreseeable future. As mentioned, it is difficult to find suitable tools and infrastructure that continue to support Python 2.7 even as the migration to Python 3 continues.
Rookout is a tool for debugging distributed cloud systems and is committed to supporting Python 2.7 as long as customers need it. It’s a practical solution if you’re looking to debug your current systems or even if you’re considering transitioning your current codebase to Python 3 and want to make sure you don’t introduce any new bugs.
Whether you’re looking to migrate from Python 2 to Python 3, or wondering why a codebase you recently inherited is still on Python 2.7, or somewhere in between, hopefully, you now understand the reasons why Python 2.7 will likely be around for a long time, as well as some of the upgrade challenges that are in place and some ways these challenges can be handled.