Description

This SPEC recommends that all projects across the Scientific Python ecosystem adopt a common time-based policy for dropping support of older Python and core package versions.

All versions refer to feature releases (i.e., Python 3.8.0, NumPy 1.19.0; not Python 3.8.1, NumPy 1.19.2).

Specifically, we recommend that:

  1. Support for a given version of Python be dropped 3 years after its initial release.
  2. Support for a given version of other core packages be dropped 2 years after their initial release.

We illustrate this SPEC with release dates of NumPy, SciPy, and Matplotlib.

gantt dateFormat YYYY-MM-DD axisFormat %m / %Y title Support Window section Python 3.7 : py37, 2018-06-27,2021-06-26 3.8 : py38, 2019-10-14,2022-10-13 3.9 : py39, 2020-10-05,2023-10-05 section NumPy 1.17 : np117, 2019-07-26,2021-07-25 1.18 : np118, 2019-12-22,2021-12-21 1.19 : np119, 2020-06-20,2022-06-20 section SciPy 1.3 : sp13, 2019-05-17,2021-05-16 1.4 : sp14, 2019-12-16,2021-12-15 1.5 : sp15, 2020-06-21,2022-06-21 1.6 : sp16, 2020-12-31,2022-12-31 section Matplotlib 3.1 : mpl31, 2019-05-18,2021-05-17 3.2 : mpl32, 2020-03-04,2022-03-04 3.3 : mpl33, 2020-07-16,2022-07-16

Motivation

Limiting the scope of supported dependencies is an effective way for packages to limit maintenance burden. Combinations of packages need to be tested, which impacts also on continuous integration times and infrastructure upkeep. Code itself also becomes more complicated when it has to be aware of various combinations of configurations.

Adoption of this SPEC will ensure a consistent support policy across packages, and reduce the need for individual projects to divise similar policies.

Ultimately, reduced maintenance burden frees up developer time, which translates into more features, bugfixes, and optimizations for users.

Background

In the past, longer support cycles were common. There were several reasons for this, including the Python 2 / 3 transition, difficulties installing packages, and users needing to use old, operating-system provided versions of Python. The situation has since improved due to improved installations via binary wheels, virtual environments becoming commonplace, and support for Python 2 being dropped.

Drop Schedule

On May 16, 2021 drop support for SciPy 1.3 (initially released on May 17, 2019)
On May 17, 2021 drop support for Matplotlib 3.1 (initially released on May 18, 2019)
On Jun 26, 2021 drop support for Python 3.7 (initially released on Jun 27, 2018)
On Jul 25, 2021 drop support for NumPy 1.17 (initially released on Jul 26, 2019)
On Dec 15, 2021 drop support for SciPy 1.4 (initially released on Dec 16, 2019)
On Dec 21, 2021 drop support for NumPy 1.18 (initially released on Dec 22, 2019)
On Mar 04, 2022 drop support for Matplotlib 3.2 (initially released on Mar 04, 2020)
On Jun 20, 2022 drop support for NumPy 1.19 (initially released on Jun 20, 2020)
On Jun 21, 2022 drop support for SciPy 1.5 (initially released on Jun 21, 2020)
On Jul 16, 2022 drop support for Matplotlib 3.3 (initially released on Jul 16, 2020)
On Oct 13, 2022 drop support for Python 3.8 (initially released on Oct 14, 2019)
On Dec 31, 2022 drop support for SciPy 1.6 (initially released on Dec 31, 2020)
On Oct 05, 2023 drop support for Python 3.9 (initially released on Oct 05, 2020)

Implementation

Core Project Endorsement

Ecosystem Adoption

Notes

  • This document builds on NEP 29, which describes several alternatives including ad hoc version support, all CPython supported versions, default version on Linux distribution, N minor versions of Python, and time window from the X.Y.1 Python release.

  • Code to generate support and drop schedule tables:

from datetime import datetime, timedelta

plus36 = timedelta(days=int(365 * 3))
plus24 = timedelta(days=int(365 * 2))

# Release data

py_releases = {
    3.7: "Jun 27, 2018",
    3.8: "Oct 14, 2019",
    3.9: "Oct 5, 2020",
}

np_releases = {
    1.17: "Jul 26, 2019",
    1.18: "Dec 22, 2019",
    1.19: "Jun 20, 2020",
}

sp_releases = {
    1.3: "May 17, 2019",
    1.4: "Dec 16, 2019",
    1.5: "Jun 21, 2020",
    1.6: "Dec 31, 2020",
}

mpl_releases = {
    3.1: "May 18, 2019",
    3.2: "Mar 4, 2020",
    3.3: "Jul 16, 2020",
}

# Get support window


def support_window(project, releases, support_time):
    windows = []
    for version, release_date in releases.items():
        release = datetime.strptime(release_date, "%b %d, %Y")
        drop = release + support_time
        windows.append((project, version, release, drop))
    return windows


py_support_window = support_window("Python", py_releases, plus36)
np_support_window = support_window("NumPy", np_releases, plus24)
sp_support_window = support_window("SciPy", sp_releases, plus24)
mpl_support_window = support_window("Matplotlib", mpl_releases, plus24)


# Print Gantt chart


def gantt_section(window, prefix):
    section = ""
    for project, version, release, drop in window:
        version_name = prefix + str(version).replace(".", "")
        release_date = release.strftime("%Y-%m-%d")
        drop_date = drop.strftime("%Y-%m-%d")
        section += f"{version}  :     {version_name}, {release_date},{drop_date}\n"
    return section


py_gantt_section = gantt_section(py_support_window, "py")
np_gantt_section = gantt_section(np_support_window, "np")
sp_gantt_section = gantt_section(sp_support_window, "sp")
mpl_gantt_section = gantt_section(mpl_support_window, "mpl")

gantt = f"""
<!-- prettier-ignore-start -->
{{

}} gantt dateFormat YYYY-MM-DD axisFormat %m / %Y title Support Window section Python {py_gantt_section} section NumPy {np_gantt_section} section SciPy {sp_gantt_section} section Matplotlib {mpl_gantt_section} {{
}}
<!-- prettier-ignore-end --> """ print(gantt) # Print drop schedule def get_drop_dates(window): return {" ".join([proj, str(ver)]): [rel, drop] for proj, ver, rel, drop in window} py_drop = get_drop_dates(py_support_window) np_drop = get_drop_dates(np_support_window) sp_drop = get_drop_dates(sp_support_window) mpl_drop = get_drop_dates(mpl_support_window) releases = py_drop | np_drop | sp_drop | mpl_drop releases = dict(sorted(releases.items(), key=lambda item: item[1][1])) for package, dates in releases.items(): print( f"On {dates[1].strftime('%b %d, %Y')} drop support for {package} " f"(initially released on {dates[0].strftime('%b %d, %Y')})" )