Long story short: I’m finally starting to drop Python 2 (and a few slightly older Python 3s) from my projects, in a phased manner. Background and details follow.

A brief timeline

Writing this out made me feel #old.

  • 2004: I’m introduced to Python (2.2/2.3) at the end of my time serving a CS degree.
  • 2008: My OSS career kicks into gear as I take over Fabric. Python 2.4 and 2.5 were stable, 2.6 comes out in the fall, and 3.0 in winter.
    • 3.x has been around the entire time I’ve maintained Fabric!
  • 2010: Python 2.7 is released. That’s 11 years ago!
  • 2012: Python 3.3, considered the first “really usable” version (especially for 2+3 codebases), comes out.
  • 2013-2014: Invoke and Fabric 2 start development, with Python 3 support right out the gate; Paramiko gains Python 3 support.
  • 2016: Python 3.6 is is born, with f-strings and ordered dicts.
  • 2018-2019: Every widespread Linux distribution switches to Python 3, if they hadn’t already.
    • The exception is RHEL/CentOS, which switches in 2020.
    • Python 3.4 reaches EOL (End of Life) in mid 2019.
    • Around this time (if not sooner) many Python 3-only packages declare Python 3.6 as their baseline supported version.
  • 2020:
    • Python 2, in its entirety, is EOL’d in January. This is an ex-interpreter!
    • Python 3.5 reaches EOL.
    • I admit burnout.
  • Early 2021: Pip drops support for Python 2, as does Cryptography (a Paramiko dependency which is unsafe to pin for long).

And here we are.

But why???

The timeline is a bit implicit; let’s be explicit. Why am I doing this now?

  • It’s past time. I’m quite conservative on the maintainer spectrum, but by now – with the interpreter, package installer, many/most packages, and most/all Linux distributions all leaving Python 2 behind – I’m in good company.
  • It’s decreasingly likely that my software is the first thing pushing you to upgrade. If you’re still on Python 2 in 2021, you’re already committed to a lack of updates from all of the above sources. I’m just one more.
  • The iron triangle has shifted. An old post is still relevant; ease of maintenance (because burnout) and speed (the current release cadence is unacceptable) are tipping the scales against stability (in the “things don’t change” sense).
  • Most downloads have been Python 3 for years. Going by PyPIStats, even the most conservative userbase I answer to (surprisingly Fabric, not Paramiko) is only 33% Python 2.
    • I don’t have a useful breakdown, but I’d also guess that 33% is heavily slated towards Fabric 1 users, who are already largely unsupported.
    • Paramiko is down to <=20% Python 2.
    • Invoke (and by extension, most users of Fabric 2) has always been <=10%
    • Alabaster is at 10%; etc.
  • Combining the above two: I’d prefer to think of this as improving quality and release cadence for a majority of users, instead of worrying about its impacts on a recalcitrant minority.
  • Finally: nothing’s getting deleted. Same as any backwards incompatible release: existing Python 2-supporting releases remain on PyPI.

How, exactly?

I am planning a phased removal of support for Pythons 2.7, 3.4 and 3.5, as follows:

  • The first step will be to remove CI support for old Pythons.
    • This will happen as I migrate to CircleCI from the now-defunct Travis-CI over the next few weeks/months.
    • Doing it this way saves me from reproducing a lot of frustrating busywork in my scripting, and also slims the test matrix – which is important now that CI providers track how much CPU time we burn!
    • But it means that there’s still a short grace period for users (see below), as my local env will still be doing basic Python 2 test runs.
  • The next step, which may happen at the same time, is to update development dependencies to assume Python 3.6+.
  • There will likely be 1-2 more release cycles which include Python 2 artifacts.
    • This works because I still generate those artifacts locally and not from CI (yet).
    • Included in this is Fabric 1’s still-planned Python 3 support merge, for a final release of that line. This provides users an upgrade path: Python 3, then later Fabric 2+.
  • Finally, the last trio of changes will happen around the same time:
  • Project tooling will drop old Python support, including build/publish tasks and packaging metadata.
  • Python 2-related syntax will be removed from the code itself.
  • Versions of most packages will bump their major qualifier to signify the change (though it’s unlikely any other backwards incompatible changes will occur):
    • Invoke will go to 2.0
    • Paramiko will go to 3.0
    • Fabric will remain on 2.x because it’s got a more complicated history here
      • thus, this will probably be Fabric 2.8 or 2.9, with 2.7 or 2.8 being the last Python 2-supporting release.