Skip to content

Commit

Permalink
Merge pull request #4476 from bartoldeman/remove-strictversion
Browse files Browse the repository at this point in the history
Eliminate use of `distutils.version.StrictVersion`
  • Loading branch information
boegel committed Mar 13, 2024
2 parents 3ec1f6a + a6fda46 commit 6203780
Show file tree
Hide file tree
Showing 6 changed files with 41 additions and 36 deletions.
10 changes: 0 additions & 10 deletions easybuild/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,4 @@
__path__ = __import__('pkgutil').extend_path(__path__, __name__)


import distutils.version
import warnings
from easybuild.tools.loose_version import LooseVersion # noqa(F401)


class StrictVersion(distutils.version.StrictVersion):
"""Temporary wrapper over distuitls StrictVersion that silences the deprecation warning"""
def __init__(self, *args, **kwargs):
with warnings.catch_warnings():
warnings.simplefilter("ignore", category=DeprecationWarning)
distutils.version.StrictVersion.__init__(self, *args, **kwargs)
16 changes: 16 additions & 0 deletions easybuild/tools/loose_version.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ def version(self):
"""Readonly access to the parsed version (list or None)"""
return self._version

def is_prerelease(self, other, markers):
"""Check if this is a prerelease of other
Markers is a list of strings that denote a prerelease
"""
if isinstance(other, str):
vstring = other
else:
vstring = other._vstring
if self._vstring.startswith(vstring):
prerelease = self._vstring[len(vstring):]
for marker in markers:
if prerelease.startswith(marker):
return True
return False

def __str__(self):
return self._vstring

Expand Down
27 changes: 10 additions & 17 deletions easybuild/tools/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
import shlex

from easybuild.base import fancylogger
from easybuild.tools import StrictVersion
from easybuild.tools import LooseVersion
from easybuild.tools.build_log import EasyBuildError, print_warning
from easybuild.tools.config import ERROR, IGNORE, PURGE, UNLOAD, UNSET
from easybuild.tools.config import EBROOT_ENV_VAR_ACTIONS, LOADED_MODULES_ACTIONS
Expand Down Expand Up @@ -144,11 +144,11 @@ class ModulesTool(object):
COMMAND_SHELL = None
# option to determine the version
VERSION_OPTION = '--version'
# minimal required version (StrictVersion; suffix rc replaced with b (and treated as beta by StrictVersion))
# minimal required version (cannot include -beta or rc)
REQ_VERSION = None
# deprecated version limit (support for versions below this version is deprecated)
DEPR_VERSION = None
# maximum version allowed (StrictVersion; suffix rc replaced with b (and treated as beta by StrictVersion))
# maximum version allowed (cannot include -beta or rc)
MAX_VERSION = None
# the regexp, should have a "version" group (multiline search)
VERSION_REGEXP = None
Expand Down Expand Up @@ -239,14 +239,6 @@ def set_and_check_version(self):
if res:
self.version = res.group('version')
self.log.info("Found %s version %s", self.NAME, self.version)

# make sure version is a valid StrictVersion (e.g., 5.7.3.1 is invalid),
# and replace 'rc' by 'b', to make StrictVersion treat it as a beta-release
self.version = self.version.replace('rc', 'b').replace('-beta', 'b1')
if len(self.version.split('.')) > 3:
self.version = '.'.join(self.version.split('.')[:3])

self.log.info("Converted actual version to '%s'" % self.version)
else:
raise EasyBuildError("Failed to determine %s version from option '%s' output: %s",
self.NAME, self.VERSION_OPTION, txt)
Expand All @@ -259,24 +251,25 @@ def set_and_check_version(self):
elif build_option('modules_tool_version_check'):
self.log.debug("Checking whether %s version %s meets requirements", self.NAME, self.version)

version = LooseVersion(self.version)
if self.REQ_VERSION is not None:
self.log.debug("Required minimum %s version defined: %s", self.NAME, self.REQ_VERSION)
if StrictVersion(self.version) < StrictVersion(self.REQ_VERSION):
if version < self.REQ_VERSION or version.is_prerelease(self.REQ_VERSION, ['rc', '-beta']):
raise EasyBuildError("EasyBuild requires %s >= v%s, found v%s",
self.NAME, self.REQ_VERSION, self.version)
else:
self.log.debug('%s version %s matches requirement >= %s', self.NAME, self.version, self.REQ_VERSION)

if self.DEPR_VERSION is not None:
self.log.debug("Deprecated %s version limit defined: %s", self.NAME, self.DEPR_VERSION)
if StrictVersion(self.version) < StrictVersion(self.DEPR_VERSION):
if version < self.DEPR_VERSION or version.is_prerelease(self.DEPR_VERSION, ['rc', '-beta']):
depr_msg = "Support for %s version < %s is deprecated, " % (self.NAME, self.DEPR_VERSION)
depr_msg += "found version %s" % self.version
self.log.deprecated(depr_msg, '6.0')

if self.MAX_VERSION is not None:
self.log.debug("Maximum allowed %s version defined: %s", self.NAME, self.MAX_VERSION)
if StrictVersion(self.version) > StrictVersion(self.MAX_VERSION):
if self.version > self.MAX_VERSION and not version.is_prerelease(self.MAX_VERSION, ['rc', '-beta']):
raise EasyBuildError("EasyBuild requires %s <= v%s, found v%s",
self.NAME, self.MAX_VERSION, self.version)
else:
Expand Down Expand Up @@ -1390,7 +1383,7 @@ def available(self, mod_name=None, extra_args=None):
if extra_args is None:
extra_args = []
# make hidden modules visible (requires Environment Modules 4.6.0)
if StrictVersion(self.version) >= StrictVersion('4.6.0'):
if LooseVersion(self.version) >= LooseVersion('4.6.0'):
extra_args.append(self.SHOW_HIDDEN_OPTION)

return super(EnvironmentModules, self).available(mod_name=mod_name, extra_args=extra_args)
Expand Down Expand Up @@ -1440,11 +1433,11 @@ def __init__(self, *args, **kwargs):
setvar('LMOD_EXTENDED_DEFAULT', 'no', verbose=False)

super(Lmod, self).__init__(*args, **kwargs)
version = StrictVersion(self.version)
version = LooseVersion(self.version)

self.supports_depends_on = True
# See https://lmod.readthedocs.io/en/latest/125_personal_spider_cache.html
if version >= '8.7.12':
if version >= LooseVersion('8.7.12'):
self.USER_CACHE_DIR = os.path.join(os.path.expanduser('~'), '.cache', 'lmod')
else:
self.USER_CACHE_DIR = os.path.join(os.path.expanduser('~'), '.lmod.d', '.cache')
Expand Down
14 changes: 8 additions & 6 deletions test/framework/modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
import easybuild.tools.modules as mod
from easybuild.framework.easyblock import EasyBlock
from easybuild.framework.easyconfig.easyconfig import EasyConfig
from easybuild.tools import StrictVersion
from easybuild.tools import LooseVersion
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.environment import modify_env
from easybuild.tools.filetools import adjust_permissions, copy_file, copy_dir, mkdir
Expand Down Expand Up @@ -226,15 +226,16 @@ def test_avail(self):

# all test modules are accounted for
ms = self.modtool.available()
version = LooseVersion(self.modtool.version)

if isinstance(self.modtool, Lmod) and StrictVersion(self.modtool.version) >= StrictVersion('5.7.5'):
if isinstance(self.modtool, Lmod) and version >= '5.7.5' and not version.is_prerelease('5.7.5', ['rc']):
# with recent versions of Lmod, also the hidden modules are included in the output of 'avail'
self.assertEqual(len(ms), TEST_MODULES_COUNT + 3)
self.assertIn('bzip2/.1.0.6', ms)
self.assertIn('toy/.0.0-deps', ms)
self.assertIn('OpenMPI/.2.1.2-GCC-6.4.0-2.28', ms)
elif (isinstance(self.modtool, EnvironmentModules)
and StrictVersion(self.modtool.version) >= StrictVersion('4.6.0')):
and version >= '4.6.0' and not version.is_prerelease('4.6.0', ['-beta'])):
# bzip2/.1.0.6 is not there, since that's a module file in Lua syntax
self.assertEqual(len(ms), TEST_MODULES_COUNT + 2)
self.assertIn('toy/.0.0-deps', ms)
Expand Down Expand Up @@ -314,7 +315,8 @@ def test_exist(self):

avail_mods = self.modtool.available()
self.assertIn('Java/1.8.0_181', avail_mods)
if isinstance(self.modtool, Lmod) and StrictVersion(self.modtool.version) >= StrictVersion('7.0'):
version = LooseVersion(self.modtool.version)
if isinstance(self.modtool, Lmod) and version >= '7.0' and not version.is_prerelease('7.0', ['rc']):
self.assertIn('Java/1.8', avail_mods)
self.assertIn('Java/site_default', avail_mods)
self.assertIn('JavaAlias', avail_mods)
Expand Down Expand Up @@ -374,7 +376,7 @@ def test_exist(self):
self.assertEqual(self.modtool.exist(['Core/Java/1.8', 'Core/Java/site_default']), [True, True])

# also check with .modulerc.lua for Lmod 7.8 or newer
if isinstance(self.modtool, Lmod) and StrictVersion(self.modtool.version) >= StrictVersion('7.8'):
if isinstance(self.modtool, Lmod) and version >= '7.8' and not version.is_prerelease('7.8', ['rc']):
shutil.move(os.path.join(self.test_prefix, 'Core', 'Java'), java_mod_dir)
reset_module_caches()

Expand Down Expand Up @@ -406,7 +408,7 @@ def test_exist(self):
self.assertEqual(self.modtool.exist(['Core/Java/site_default']), [True])

# Test alias in home directory .modulerc
if isinstance(self.modtool, Lmod) and StrictVersion(self.modtool.version) >= StrictVersion('7.0'):
if isinstance(self.modtool, Lmod) and version >= '7.0' and not version.is_prerelease('7.0', ['rc']):
# Required or temporary HOME would be in MODULEPATH already
self.init_testmods()
# Sanity check: Module aliases don't exist yet
Expand Down
6 changes: 3 additions & 3 deletions test/framework/modulestool.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from unittest import TextTestRunner

from easybuild.base import fancylogger
from easybuild.tools import modules, StrictVersion
from easybuild.tools import modules, LooseVersion
from easybuild.tools.build_log import EasyBuildError
from easybuild.tools.filetools import read_file, which, write_file
from easybuild.tools.modules import EnvironmentModules, Lmod
Expand Down Expand Up @@ -76,7 +76,7 @@ def test_mock(self):
mmt = MockModulesTool(mod_paths=[], testing=True)

# the version of the MMT is the commandline option
self.assertEqual(mmt.version, StrictVersion(MockModulesTool.VERSION_OPTION))
self.assertEqual(mmt.version, LooseVersion(MockModulesTool.VERSION_OPTION))

cmd_abspath = which(MockModulesTool.COMMAND)

Expand All @@ -100,7 +100,7 @@ def test_environment_command(self):
bmmt = BrokenMockModulesTool(mod_paths=[], testing=True)
cmd_abspath = which(MockModulesTool.COMMAND)

self.assertEqual(bmmt.version, StrictVersion(MockModulesTool.VERSION_OPTION))
self.assertEqual(bmmt.version, LooseVersion(MockModulesTool.VERSION_OPTION))
self.assertEqual(bmmt.cmd, cmd_abspath)

# clean it up
Expand Down
4 changes: 4 additions & 0 deletions test/framework/utilities_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ def test_LooseVersion(self):
# Careful here: 1.0 > 1 !!!
self.assertGreater(LooseVersion('1.0'), LooseVersion('1'))
self.assertLess(LooseVersion('1'), LooseVersion('1.0'))
# checking prereleases
self.assertGreater(LooseVersion('4.0.0-beta'), LooseVersion('4.0.0'))
self.assertEqual(LooseVersion('4.0.0-beta').is_prerelease('4.0.0', ['-beta']), True)
self.assertEqual(LooseVersion('4.0.0-beta').is_prerelease('4.0.0', ['rc']), False)

# The following test is taken from Python distutils tests
# licensed under the Python Software Foundation License Version 2
Expand Down

0 comments on commit 6203780

Please sign in to comment.