From caa04ec8583fb01d0aa967592240e01159c23617 Mon Sep 17 00:00:00 2001 From: Oliver Beckstein Date: Fri, 18 Oct 2024 19:24:04 -0700 Subject: [PATCH] pin distopia<0.3.0 (#4740) - fix #4739 - pin distopia<0.3.0 - temporarily restrict distopia to >=0.2.0,<0.3.0 until MDAnalysis has caught up with distopia API changes - updated CHANGELOG - version check distopia - only enable HAS_DISTOPIA if the appropriate version of distopia is installed - issue RuntimeWarning if incorrect version present - added test --- .github/actions/setup-deps/action.yaml | 2 +- package/CHANGELOG | 1 + package/MDAnalysis/lib/_distopia.py | 15 ++++++- .../MDAnalysisTests/lib/test_distances.py | 42 +++++++++++++++++-- 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/.github/actions/setup-deps/action.yaml b/.github/actions/setup-deps/action.yaml index 97112b09159..cbfd91df7e1 100644 --- a/.github/actions/setup-deps/action.yaml +++ b/.github/actions/setup-deps/action.yaml @@ -59,7 +59,7 @@ inputs: dask: default: 'dask' distopia: - default: 'distopia>=0.2.0' + default: 'distopia>=0.2.0,<0.3.0' h5py: default: 'h5py>=2.10' hole2: diff --git a/package/CHANGELOG b/package/CHANGELOG index de7fddd2660..37c60672327 100644 --- a/package/CHANGELOG +++ b/package/CHANGELOG @@ -78,6 +78,7 @@ Enhancements DOI 10.1021/acs.jpcb.7b11988. (Issue #2039, PR #4524) Changes + * only use distopia < 0.3.0 due to API changes (Issue #4739) * The `fetch_mmtf` method has been removed as the REST API service for MMTF files has ceased to exist (Issue #4634) * MDAnalysis now builds against numpy 2.0 rather than the diff --git a/package/MDAnalysis/lib/_distopia.py b/package/MDAnalysis/lib/_distopia.py index 7170cf2a556..5344393fe14 100644 --- a/package/MDAnalysis/lib/_distopia.py +++ b/package/MDAnalysis/lib/_distopia.py @@ -27,6 +27,7 @@ This module is a stub to provide distopia distance functions to `distances.py` as a selectable backend. """ +import warnings # check for distopia try: @@ -36,10 +37,22 @@ else: HAS_DISTOPIA = True + # check for compatibility: currently needs to be >=0.2.0,<0.3.0 (issue + # #4740) No distopia.__version__ available so we have to do some probing. + needed_funcs = ['calc_bonds_no_box_float', 'calc_bonds_ortho_float'] + has_distopia_020 = all([hasattr(distopia, func) for func in needed_funcs]) + if not has_distopia_020: + warnings.warn("Install 'distopia>=0.2.0,<0.3.0' to be used with this " + "release of MDAnalysis. Your installed version of " + "distopia >=0.3.0 will NOT be used.", + category=RuntimeWarning) + del distopia + HAS_DISTOPIA = False + + from .c_distances import ( calc_bond_distance_triclinic as _calc_bond_distance_triclinic_serial, ) -import warnings import numpy as np diff --git a/testsuite/MDAnalysisTests/lib/test_distances.py b/testsuite/MDAnalysisTests/lib/test_distances.py index 3fe4b2852b8..4f7cd238bab 100644 --- a/testsuite/MDAnalysisTests/lib/test_distances.py +++ b/testsuite/MDAnalysisTests/lib/test_distances.py @@ -20,6 +20,8 @@ # MDAnalysis: A Toolkit for the Analysis of Molecular Dynamics Simulations. # J. Comput. Chem. 32 (2011), 2319--2327, doi:10.1002/jcc.21787 # +import sys +from unittest.mock import Mock, patch import pytest import numpy as np from numpy.testing import assert_equal, assert_almost_equal, assert_allclose @@ -788,9 +790,10 @@ def test_pbc_wrong_wassenaar_distance(self, backend): # expected. assert np.linalg.norm(point_a - point_b) != dist[0, 0] -@pytest.mark.parametrize("box", + +@pytest.mark.parametrize("box", [ - None, + None, np.array([10., 15., 20., 90., 90., 90.]), # otrho np.array([10., 15., 20., 70.53571, 109.48542, 70.518196]), # TRIC ] @@ -835,6 +838,39 @@ def distopia_conditional_backend(): return ["serial", "openmp"] +def test_HAS_DISTOPIA_incompatible_distopia(): + # warn if distopia is the wrong version and set HAS_DISTOPIA to False + sys.modules.pop("distopia", None) + sys.modules.pop("MDAnalysis.lib._distopia", None) + + # fail any Attribute access for calc_bonds_ortho_float, + # calc_bonds_no_box_float but pretend to have the distopia + # 0.3.0 functions (from + # https://github.com/MDAnalysis/distopia/blob/main/distopia/__init__.py + # __all__): + mock_distopia_030 = Mock(spec=[ + 'calc_bonds_ortho', + 'calc_bonds_no_box', + 'calc_bonds_triclinic', + 'calc_angles_no_box', + 'calc_angles_ortho', + 'calc_angles_triclinic', + 'calc_dihedrals_no_box', + 'calc_dihedrals_ortho', + 'calc_dihedrals_triclinic', + 'calc_distance_array_no_box', + 'calc_distance_array_ortho', + 'calc_distance_array_triclinic', + 'calc_self_distance_array_no_box', + 'calc_self_distance_array_ortho', + 'calc_self_distance_array_triclinic', + ]) + with patch.dict("sys.modules", {"distopia": mock_distopia_030}): + with pytest.warns(RuntimeWarning, + match="Install 'distopia>=0.2.0,<0.3.0' to"): + import MDAnalysis.lib._distopia + assert not MDAnalysis.lib._distopia.HAS_DISTOPIA + class TestCythonFunctions(object): # Unit tests for calc_bonds calc_angles and calc_dihedrals in lib.distances # Tests both numerical results as well as input types as Cython will silently @@ -1597,7 +1633,7 @@ def test_empty_input_self_capped_distance(self, empty_coord, min_cut, box, assert_equal(res[1], np.empty((0,), dtype=np.float64)) else: assert_equal(res, np.empty((0, 2), dtype=np.int64)) - + @pytest.mark.parametrize('box', boxes[:2]) @pytest.mark.parametrize('backend', ['serial', 'openmp']) def test_empty_input_transform_RtoS(self, empty_coord, box, backend):