Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BLD: NumPy 2 compat for wheel builds #4620

Merged
merged 5 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions package/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ Enhancements
DOI 10.1021/acs.jpcb.7b11988. (Issue #2039, PR #4524)

Changes
* MDAnalysis now builds against numpy 2.0 rather than the
minimum supported numpy version (PR #4620)
* As per SPEC0 the minimum supported Python version has been raised
to 3.10 (PR #4502)
* MDAnalysis.analysis.hole2 is now directly imported from the mdakit
Expand Down
20 changes: 16 additions & 4 deletions package/MDAnalysis/converters/ParmEd.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,9 @@
import itertools
import warnings

import numpy as np
from numpy.lib import NumpyVersion

from . import base
from ..coordinates.base import SingleFrameReaderBase
from ..topology.tables import SYMB2Z
Expand Down Expand Up @@ -168,11 +171,20 @@
obj : AtomGroup or Universe or :class:`Timestep`
"""
try:
import parmed as pmd
# TODO: remove this guard when parmed has a release
# that supports NumPy 2
if NumpyVersion(np.__version__) < "2.0.0":
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just added this extra guard here since we were doing the same for RDKit.

import parmed as pmd
else:
raise ImportError

Check warning on line 179 in package/MDAnalysis/converters/ParmEd.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/converters/ParmEd.py#L179

Added line #L179 was not covered by tests
except ImportError:
raise ImportError('ParmEd is required for ParmEdConverter but '
'is not installed. Try installing it with \n'
'pip install parmed')
if NumpyVersion(np.__version__) >= "2.0.0":
ermsg = "ParmEd is not compatible with NumPy 2.0+"

Check warning on line 182 in package/MDAnalysis/converters/ParmEd.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/converters/ParmEd.py#L182

Added line #L182 was not covered by tests
else:
ermsg = ("ParmEd is required for ParmEdConverter but is not "

Check warning on line 184 in package/MDAnalysis/converters/ParmEd.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/converters/ParmEd.py#L184

Added line #L184 was not covered by tests
"installed. Try installing it with \n"
"pip install parmed")
raise ImportError(errmsg)

Check warning on line 187 in package/MDAnalysis/converters/ParmEd.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/converters/ParmEd.py#L187

Added line #L187 was not covered by tests
try:
# make sure to use atoms (Issue 46)
ag_or_ts = obj.atoms
Expand Down
10 changes: 8 additions & 2 deletions package/MDAnalysis/converters/RDKit.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@
from io import StringIO

import numpy as np
from numpy.lib import NumpyVersion

from . import base
from ..coordinates import memory
Expand All @@ -95,8 +96,13 @@
from ..exceptions import NoDataError

try:
from rdkit import Chem
from rdkit.Chem import AllChem
# TODO: remove this guard when RDKit has a release
# that supports NumPy 2
if NumpyVersion(np.__version__) < "2.0.0":
from rdkit import Chem
from rdkit.Chem import AllChem
else:
raise ImportError

Check warning on line 105 in package/MDAnalysis/converters/RDKit.py

View check run for this annotation

Codecov / codecov/patch

package/MDAnalysis/converters/RDKit.py#L105

Added line #L105 was not covered by tests
except ImportError:
pass
else:
Expand Down
16 changes: 7 additions & 9 deletions package/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
requires = [
"Cython>=0.28",
"packaging",
# lowest NumPy we can use for a given Python,
# In part adapted from: https://github.com/scipy/oldest-supported-numpy/blob/main/setup.cfg
# As per NEP29, we set the minimum version to 1.23.2 for Python <=3.11
# and 1.26.0 (first to support) for Python 3.12
"numpy==1.23.2; python_version<='3.11' and platform_python_implementation != 'PyPy'",
"numpy==1.26.0; python_version=='3.12' and platform_python_implementation != 'PyPy'",
# For unreleased versions of Python there is currently no known supported
# NumPy version. In that case we just let it be a bare NumPy install
"numpy<2.0; python_version>='3.13'",
# numpy requirement for wheel builds for distribution on PyPI - building
# against 2.x yields wheels that are also compatible with numpy 1.x at
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the range of compatible 1.x numpy versions with 2.0 published somewhere? I.e. checking if we need to bump the minimum NumPy version anywhere.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This document isn't too bad: https://numpy.org/doc/stable/dev/depending_on_numpy.html#for-downstream-package-authors. I think we may be "ok" on that front.

You can deduce the stability back to 1.25.x and then to 1.19.x based on that document. It looks like we have Python 3.10 and NumPy 1.23.2 as our runtime lower bounds, which is pretty close to where SciPy is (3.10 and 1.23.5 respectively) at the moment.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me do a quick check of the current CI failures to see if I can make a concrete suggestion for those as well.

# runtime.
# Note that building against numpy 1.x works fine too - users and
# redistributors can do this by installing the numpy version they like and
# disabling build isolation.
"numpy>=2.0.0",
# Set to minimum version of setuptools that allows pyproject.toml
"setuptools >= 40.9.0",
"wheel",
Expand Down
10 changes: 9 additions & 1 deletion testsuite/MDAnalysisTests/converters/test_parmed.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@
import pytest
import MDAnalysis as mda

import numpy as np
from numpy.testing import (assert_allclose, assert_equal)
from numpy.lib import NumpyVersion

from MDAnalysisTests.coordinates.base import _SingleFrameReader
from MDAnalysisTests.coordinates.reference import RefAdKSmall
Expand All @@ -41,7 +43,13 @@
PRM_UreyBradley,
)

pmd = pytest.importorskip('parmed')
# TODO: remove this guard when parmed has a release
# that support NumPy 2
if NumpyVersion(np.__version__) < "2.0.0":
pmd = pytest.importorskip('parmed')
else:
pmd = pytest.importorskip('parmed_skip_with_numpy2')



class TestParmEdReaderGRO:
Expand Down
3 changes: 3 additions & 0 deletions testsuite/MDAnalysisTests/converters/test_rdkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

import MDAnalysis as mda
import numpy as np
from numpy.lib import NumpyVersion
import pytest
from MDAnalysis.topology.guessers import guess_atom_element
from MDAnalysisTests.datafiles import GRO, PDB_full, PDB_helix, mol2_molecule
Expand Down Expand Up @@ -55,6 +56,8 @@
reason="only for min dependencies build")
class TestRequiresRDKit(object):
def test_converter_requires_rdkit(self):
if NumpyVersion(np.__version__) >= "2.0.0":
pytest.skip("RDKit not compatible with NumPy 2")
u = mda.Universe(PDB_full)
with pytest.raises(ImportError,
match="RDKit is required for the RDKitConverter"):
Expand Down
11 changes: 9 additions & 2 deletions testsuite/MDAnalysisTests/converters/test_rdkit_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,21 @@
import warnings
import pytest
import numpy as np
from numpy.lib import NumpyVersion
from numpy.testing import assert_equal

import MDAnalysis as mda
from MDAnalysisTests.topology.base import ParserBase
from MDAnalysisTests.datafiles import mol2_molecule, PDB_helix, SDF_molecule

Chem = pytest.importorskip('rdkit.Chem')
AllChem = pytest.importorskip('rdkit.Chem.AllChem')
# TODO: remove these shims when RDKit
# has a release supporting NumPy 2
if NumpyVersion(np.__version__) < "2.0.0":
Chem = pytest.importorskip('rdkit.Chem')
AllChem = pytest.importorskip('rdkit.Chem.AllChem')
else:
Chem = pytest.importorskip("RDKit_does_not_support_NumPy_2")
AllChem = pytest.importorskip("RDKit_does_not_support_NumPy_2")

class RDKitParserBase(ParserBase):
parser = mda.converters.RDKitParser.RDKitParser
Expand Down
16 changes: 13 additions & 3 deletions testsuite/MDAnalysisTests/core/test_atomselections.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
from io import StringIO
import itertools
import numpy as np
from numpy.lib import NumpyVersion
from numpy.testing import(
assert_equal,
)
Expand Down Expand Up @@ -539,7 +540,10 @@ def test_molnum(self, universe, selection_string, reference):

class TestSelectionRDKit(object):
def setup_class(self):
pytest.importorskip("rdkit.Chem")
if NumpyVersion(np.__version__) < "2.0.0":
pytest.importorskip("rdkit.Chem")
else:
pytest.skip("RDKit does not support NumPy 2")

@pytest.fixture
def u(self):
Expand Down Expand Up @@ -1413,13 +1417,19 @@ def test_negative_resid():
("aromaticity False", 15),
])
def test_bool_sel(selstr, n_atoms):
pytest.importorskip("rdkit.Chem")
if NumpyVersion(np.__version__) >= "2.0.0":
pytest.skip("RDKit does not support NumPy 2")
else:
pytest.importorskip("rdkit.Chem")
u = MDAnalysis.Universe.from_smiles("Nc1cc(C[C@H]([O-])C=O)c[nH]1")
assert len(u.select_atoms(selstr)) == n_atoms


def test_bool_sel_error():
pytest.importorskip("rdkit.Chem")
if NumpyVersion(np.__version__) >= "2.0.0":
pytest.skip("RDKit does not support NumPy 2")
else:
pytest.importorskip("rdkit.Chem")
u = MDAnalysis.Universe.from_smiles("Nc1cc(C[C@H]([O-])C=O)c[nH]1")
with pytest.raises(SelectionError, match="'fragrant' is an invalid value"):
u.select_atoms("aromaticity fragrant")
Expand Down
6 changes: 5 additions & 1 deletion testsuite/MDAnalysisTests/core/test_universe.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import warnings

import numpy as np
from numpy.lib import NumpyVersion
from numpy.testing import (
assert_allclose,
assert_almost_equal,
Expand Down Expand Up @@ -224,7 +225,10 @@ def test_universe_empty_ROMol(self):

class TestUniverseFromSmiles(object):
def setup_class(self):
pytest.importorskip("rdkit.Chem")
if NumpyVersion(np.__version__) < "2.0.0":
pytest.importorskip("rdkit.Chem")
else:
pytest.importorskip("RDKit_does_not_support_NumPy_2")

def test_default(self):
smi = "CN1C=NC2=C1C(=O)N(C(=O)N2C)C"
Expand Down
7 changes: 7 additions & 0 deletions testsuite/MDAnalysisTests/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@
import pytest

from numpy.testing import assert_warns
import numpy as np
from numpy.lib import NumpyVersion


def block_import(package):
Expand Down Expand Up @@ -112,6 +114,11 @@ def import_not_available(module_name):
msg="skip test as module_name could not be imported")

"""
# TODO: remove once these packages have a release
# with NumPy 2 support
if NumpyVersion(np.__version__) >= "2.0.0":
if module_name in {"rdkit", "parmed"}:
return True
try:
test = importlib.import_module(module_name)
except ImportError:
Expand Down
Loading