diff --git a/swift/common/utils/checksum.py b/swift/common/utils/checksum.py index 9f9a273ebc..dc1b88bd6d 100644 --- a/swift/common/utils/checksum.py +++ b/swift/common/utils/checksum.py @@ -43,14 +43,20 @@ def find_isal(): # with isal baked in? try: import pyeclib # noqa - from importlib.metadata import files as pkg_files # py38+ + from importlib.metadata import \ + files as pkg_files, PackageNotFoundError # py38+ except ImportError: pass else: - pyeclib_files = pkg_files('pyeclib') - if not pyeclib_files: - # see https://docs.python.org/3/library/importlib.metadata.html - raise RuntimeError('pyeclib installed but missing files') + # Assume busted installs won't have it + try: + pyeclib_files = pkg_files('pyeclib') + if pyeclib_files is None: + # Have a dist-info, but no RECORD file?? + pyeclib_files = [] + except PackageNotFoundError: + # Could import pyeclib, but no dist-info directory?? + pyeclib_files = [] isal_libs = [f for f in pyeclib_files if f.name.startswith("libisal")] if len(isal_libs) == 1: diff --git a/test/unit/common/utils/test_checksum.py b/test/unit/common/utils/test_checksum.py index 87ffe29bac..a17642d534 100644 --- a/test/unit/common/utils/test_checksum.py +++ b/test/unit/common/utils/test_checksum.py @@ -12,7 +12,6 @@ # implied. # See the License for the specific language governing permissions and # limitations under the License. - import sys import unittest from unittest import mock @@ -25,16 +24,52 @@ from test.unit import requires_crc32c, requires_crc64nvme class TestModuleFunctions(unittest.TestCase): + def test_find_isal_sys_package_preferred(self): + with mock.patch('ctypes.util.find_library', return_value='my-isal.so'): + with mock.patch('ctypes.CDLL', return_value='fake') as mock_cdll: + self.assertEqual('fake', checksum.find_isal()) + self.assertEqual([mock.call('my-isal.so')], mock_cdll.call_args_list) + + @unittest.skipIf( + sys.version_info.major == 3 and sys.version_info.minor < 8, + "importlib.metadata not available until py3.8") + def test_find_isal_pyeclib_install_found(self): + mock_pkg = mock.MagicMock() + mock_pkg.locate = mock.MagicMock(return_value='fake-pkg') + with mock.patch('ctypes.util.find_library', return_value=None): + with mock.patch('ctypes.CDLL', return_value='fake') as mock_cdll: + with mock.patch('importlib.metadata.files', + return_value=[mock_pkg]): + self.assertEqual('fake', checksum.find_isal()) + self.assertEqual([mock.call('fake-pkg')], mock_cdll.call_args_list) + + @unittest.skipIf( + sys.version_info.major == 3 and sys.version_info.minor < 8, + "importlib.metadata not available until py3.8") + def test_find_isal_pyeclib_install_not_found(self): + mock_pkg = mock.MagicMock() + mock_pkg.locate = mock.MagicMock(return_value='fake-pkg') + with mock.patch('ctypes.util.find_library', return_value=None): + with mock.patch('importlib.metadata.files', return_value=[]): + self.assertIsNone(checksum.find_isal()) + @unittest.skipIf( sys.version_info.major == 3 and sys.version_info.minor < 8, "importlib.metadata not available until py3.8") def test_find_isal_pyeclib_dist_missing_files(self): with mock.patch('ctypes.util.find_library', return_value=None): with mock.patch('importlib.metadata.files', return_value=None): - with self.assertRaises(RuntimeError) as cm: - checksum.find_isal() - self.assertEqual('pyeclib installed but missing files', - str(cm.exception)) + self.assertIsNone(checksum.find_isal()) + + @unittest.skipIf( + sys.version_info.major == 3 and sys.version_info.minor < 8, + "importlib.metadata not available until py3.8") + def test_find_isal_pyeclib_dist_info_missing(self): + from importlib.metadata import PackageNotFoundError + with mock.patch('ctypes.util.find_library', return_value=None): + with mock.patch('importlib.metadata.files', + side_effect=PackageNotFoundError): + self.assertIsNone(checksum.find_isal()) # If you're curious about the 0xe3069283, see "check" at