Skip to content

Nexus: End Support for Python < 3.10 and package versions past EOL #5816

@brockdyer03

Description

@brockdyer03

Current Status

The significant breadth of Python versions and package versions supported by Nexus is nothing short of a miracle, however in the words of Matt Smith playing the 11th Doctor, "Times change, and so must I."

The current minimum supported version policy for Nexus is as follows (versions from the pyproject.toml file in #5742, dates from nexus/nexus/versions.py or PyPI):

python     >= 3.6.0   # 2016-12-23
numpy      >= 1.13.0  # 2017-06-07
scipy      >= 0.19.0  # 2017-03-09
h5py       >= 2.6.0   # 2016-04-08
matplotlib >= 2.0.0   # 2017-01-17
spglib     >= 1.12.0  # 2019-01-29
cif2cell   >= 2.1.0   # 2023-10-05
pydot      >= 1.2.4   # 2017-12-25
seekpath   >= 1.9.0   # 2019-07-25

These versions have all been verified with Python 3.6.0 using a Docker container based on Ubuntu 18.04.

The Problem

The sheer number of versions supported by Nexus creates a significant number of problems with respect to development. For instance, there are several places in Nexus that require checking the version of various packages prior to an operation being performed.

Additionally, versions.py is designed for checking dependencies and their versions, but is often not updated and generally is a roundabout way to just have a centralized version set that would be provided by a pyproject.toml file.

Version-Specific Code

(testing.py, lines 1-8)

try:
    import numpy as np
    if np.lib.NumpyVersion(np.__version__) >= '2.0.0b1':
        np.set_printoptions(legacy="1.25")
    numpy_available = True
except:
    numpy_available = False
#end try

(qdens, lines 825-832)

# Use np.trapz for backwards and forwards compatibility.
np_version = tuple(map(int, np.__version__.split('.')[:2]))
use_trapz = np_version < (2, 0)
if use_trapz:
    total_electrons = np.trapz(lmean, r)
else:
    # In NumPy >= 2.0, prefer np.trapezoid
    total_electrons = np.trapezoid(lmean, r)

(generic.py, lines 516-540)

try:
    tmp = pickle.load(fobj)
except ModuleNotFoundError:
    try:
        # Old pickles from before Nexus was packaged (PR #5700, December 20 2025)
        # won't have the correct module path. The custom unpickler will handle this by 
        # prepending "nexus." to the module path
        tmp = NexusUnpickler(fobj).load()
    except UnpicklingError:
        try:
            # NumPy pickles can use latin1 encoding
            # They will likely still fail from an underflow since they are not pickle-compliant
            tmp = NexusUnpickler(fobj).load(encoding='latin1')
        except UnpicklingError:
            # fallback for files created with protocol 5
            # in environments that only support up to protocol 4
            try:
                import pickle5
                tmp = pickle5.load(fobj)
            except ImportError:
                have_pickle5 = False
                error("Highest pickle protocol in current python version is {}, but {} is written using a higher protocol. Install pickle5, e.g. via pip, to enable protocol 5 in python <= 3.7.x".format(pickle.HIGHEST_PROTOCOL, fpath))
        #end try
    #end try
#end try

(structure.py, lines 5895-5915)

try:
    import seekpath
    from seekpath import get_explicit_k_path
    version = seekpath.__version__

    try:
        version = [int(i) for i in version.split('.')]
        if len(version) < 3:
            raise ValueError
        #end if
    except ValueError:
        raise ValueError("Unable to parse version number")
    #end try
    if tuple(version) < (1, 8, 3):
        raise ValueError("Invalid seekpath version, need >= 1.8.4")
    #end if
    del version
    del seekpath
except:
    get_explicit_k_path = unavailable('seekpath','get_explicit_k_path')
#end try

Proposed Version Support

I propose that we follow the support timeline of Python and the support timeline of NumPy. This means Python >=3.10 and NumPy >= 2.0.0.

This policy has several main motivators.

  1. Non-EOL Python versions do not receive security updates, leaving them more vulnerable to outside attacks.
  2. By supporting only NumPy >= 2.0.0 we will no longer need to worry about bugs that are specific to Numpy < 2.
  3. Prospective contributors do not need to put excessive thought into using a recently-introduced feature.
  4. We can limit the amount of version-specific documenting (e.g. Nexus: Installable as a Python package #5742 currently includes notes for pip < 10, which only comes with Python 3.6)
  5. New updates now pose less of a threat to the stability of Nexus as it is far less likely that syntax has changed or functions have been deprecated.

Functions Introduced After Python 3.6

Python Functions and Utilities

  • Dataclasses (Python 3.7)
  • Postponed evaluation of annotations (Python 3.7)
  • Dictionary union via the union operator | (Python 3.9)
  • Built-in methods to remove prefixes and suffixes from strings (Python 3.9)
  • Updated math.gcd to accept more than 2 arguments (Python 3.9)
  • Added math.lcm (Python 3.9)
  • Add pathlib.Path.readlink for resolving symlinks (Python 3.9)
  • Add structural pattern matching (match statements, equivalent to switch in C/C++) (Python 3.10)
  • Add support for parenthesized context managers (Python 3.10)
  • Improved the __repr__ and __str__ of Enums (Python 3.10)
  • Updated pprint to be able to print dataclasses (Python 3.10)

Numpy Functions and Utilities

Non-EOL Operating System Python Defaults

Here is a list of some relevant operating systems that have not hit EOL and their default or oldest available Python versions:

  • Ubuntu 22.04 LTS
    • Python 3.10
  • Debian 12
    • Python 3.11
  • RHEL 9 (ditto for CentOS 9)
    • Python 3.9
  • RHEL 10 (ditto for CentOS 10)
    • Python 3.12
  • Fedora 42
    • Python 3.13
  • OpenSUSE 16
    • Python 3.13
  • MacOS (Homebrew)
    • Python 3.10.19

Diagrammatic Representation

---
config:
    theme: 'dark'
    themeVariables:
        activeTaskBorderColor: '#03a200'
        activeTaskBkgColor: '#81B1DB'
        critBorderColor: '#E83737'
        todayLineColor: '#03a200'
        vertLineColor: '#ff0000'
---
gantt
dateFormat YYYY-MM-DD
axisFormat %m / %Y
title Support Window

Current Nexus Support Policy : vert, 2017-06-07, 3650d

section Python
3.6 : crit, active, 2016-12-23,2021-12-22
3.7 : crit, active, 2018-06-27,2023-06-26
3.8 : crit, active, 2019-10-14,2024-10-12
3.9 : crit, active, 2020-10-05,2025-10-04
3.10 : active, 2021-10-04,2026-10-03
3.11 : active, 2022-10-24,2027-10-23
3.12 : active, 2023-10-02,2028-09-30
3.13 : active, 2024-10-07,2029-10-06
3.14 : active, 2025-10-07,2030-10-06

section NumPy
1.13.0 : crit, active, 2017-06-07,2019-06-07
1.14.0 : crit, active, 2018-01-06,2020-01-06
1.15.0 : crit, active, 2018-07-23,2020-07-22
1.16.0 : crit, active, 2019-01-14,2021-01-13
1.17.0 : crit, active, 2019-07-26,2021-07-25
1.18.0 : crit, active, 2019-12-22,2021-12-21
1.19.0 : crit, active, 2020-06-20,2022-06-20
1.20.0 : crit, active, 2021-01-30,2023-01-30
1.21.0 : crit, active, 2021-06-22,2023-06-22
1.22.0 : crit, active, 2021-12-31,2023-12-31
1.23.0 : crit, active, 2022-06-22,2024-06-21
1.24.0 : crit, active, 2022-12-18,2024-12-17
1.25.0 : crit, active, 2023-06-17,2025-06-16
1.26.0 : crit, active, 2023-09-16,2025-09-15
2.0.0 : active, 2024-06-16,2026-06-16
2.1.0 : active, 2024-08-18,2026-08-18
2.2.0 : active, 2024-12-08,2026-12-08
2.3.0 : active, 2025-06-07,2027-06-07
2.4.0 : active, 2025-12-20,2027-12-20

section SciPy
0.19.0 : crit, active, 2017-03-09,2019-03-09
1.0.0 :  crit, active, 2017-10-25,2019-10-25
1.1.0 :  crit, active, 2018-05-05,2020-05-04
1.2.0 :  crit, active, 2018-12-18,2020-12-17
1.3.0 :  crit, active, 2019-05-17,2021-05-16
1.4.0 :  crit, active, 2019-12-16,2021-12-15
1.5.0 :  crit, active, 2020-06-21,2022-06-21
1.6.0 :  crit, active, 2020-12-31,2022-12-31
1.7.0 :  crit, active, 2021-06-20,2023-06-20
1.8.0 :  crit, active, 2022-02-05,2024-02-05
1.9.0 :  crit, active, 2022-07-29,2024-07-28
1.10.0 : crit, active, 2023-01-03,2025-01-02
1.11.0 : crit, active, 2023-06-25,2025-06-24
1.12.0 : crit, active, 2024-01-20,2026-01-19
1.13.0 : active, 2024-04-02,2026-04-02
1.14.0 : active, 2024-06-24,2026-06-24
1.15.0 : active, 2025-01-03,2027-01-03
1.16.0 : active, 2025-06-22,2027-06-22
1.17.0 : active, 2026-01-10,2028-01-10

section Matplotlib
2.0.0 :  crit, active, 2017-01-17,2019-01-17
2.1.0 :  crit, active, 2017-10-07,2019-10-07
2.2.0 :  crit, active, 2018-03-06,2020-03-05
3.0.0 :  crit, active, 2018-09-18,2020-09-17
3.1.0 :  crit, active, 2019-05-18,2021-05-17
3.2.0 :  crit, active, 2020-03-04,2022-03-04
3.3.0 :  crit, active, 2020-07-16,2022-07-16
3.4.0 :  crit, active, 2021-03-26,2023-03-26
3.5.0 :  crit, active, 2021-11-16,2023-11-16
3.6.0 :  crit, active, 2022-09-16,2024-09-15
3.7.0 :  crit, active, 2023-02-13,2025-02-12
3.8.0 :  crit, active, 2023-09-15,2025-09-14
3.9.0 : active, 2024-05-15,2026-05-15
3.10.0 : active, 2024-12-14,2026-12-14
Loading

Metadata

Metadata

Assignees

No one assigned

    Labels

    dependenciesPull requests that update a dependency filediscussionnexuspythonPull requests that update python code

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions