Skip to content

Commit 04dc49b

Browse files
committed
Merge branch 'develop' into depr_py26
2 parents b035a04 + d6fd2ec commit 04dc49b

File tree

9 files changed

+156
-72
lines changed

9 files changed

+156
-72
lines changed

.github/workflows/unit_tests.yml

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ jobs:
66
runs-on: ubuntu-18.04
77
strategy:
88
matrix:
9-
python: [2.7, 3.5, 3.6, 3.7]
10-
modules_tool: [Lmod-6.6.3, Lmod-7.8.22, Lmod-8.1.14, modules-tcl-1.147, modules-3.2.10, modules-4.1.4]
9+
python: [2.7, 3.5, 3.6, 3.7, 3.8]
10+
modules_tool: [Lmod-7.8.22, Lmod-8.2.3, modules-tcl-1.147, modules-3.2.10, modules-4.1.4]
1111
module_syntax: [Lua, Tcl]
1212
# exclude some configuration for non-Lmod modules tool:
1313
# - don't test with Lua module syntax (only supported in Lmod)
14-
# - don't test with Python 3.5 and 3.7 (only with 2.7 and 3.6), to limit test configurations
14+
# - exclude Python 3.x versions other than 3.6, to limit test configurations
1515
exclude:
1616
- modules_tool: modules-tcl-1.147
1717
module_syntax: Lua
@@ -23,14 +23,20 @@ jobs:
2323
python: 3.5
2424
- modules_tool: modules-tcl-1.147
2525
python: 3.7
26+
- modules_tool: modules-tcl-1.147
27+
python: 3.8
2628
- modules_tool: modules-3.2.10
2729
python: 3.5
2830
- modules_tool: modules-3.2.10
2931
python: 3.7
32+
- modules_tool: modules-3.2.10
33+
python: 3.8
3034
- modules_tool: modules-4.1.4
3135
python: 3.5
3236
- modules_tool: modules-4.1.4
3337
python: 3.7
38+
- modules_tool: modules-4.1.4
39+
python: 3.8
3440
fail-fast: false
3541
steps:
3642
- uses: actions/checkout@v1

.travis.yml

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
language: python
2-
python: 2.6
3-
dist: trusty
2+
python: 2.7
3+
dist: xenial
44
env:
55
matrix:
66
# purposely specifying slowest builds first, to gain time overall
7-
- LMOD_VERSION=6.5.1
8-
- LMOD_VERSION=6.5.1 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
97
- LMOD_VERSION=7.8.22
108
- LMOD_VERSION=7.8.22 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
11-
- LMOD_VERSION=8.0.9
12-
- LMOD_VERSION=8.0.9 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
9+
- LMOD_VERSION=8.2.3
10+
- LMOD_VERSION=8.2.3 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
1311
- ENV_MOD_VERSION=3.2.10 TEST_EASYBUILD_MODULES_TOOL=EnvironmentModulesC TEST_EASYBUILD_MODULE_SYNTAX=Tcl
1412
- ENV_MOD_TCL_VERSION=1.147 TEST_EASYBUILD_MODULES_TOOL=EnvironmentModulesTcl TEST_EASYBUILD_MODULE_SYNTAX=Tcl
1513
- ENV_MOD_VERSION=4.1.4 TEST_EASYBUILD_MODULE_SYNTAX=Tcl TEST_EASYBUILD_MODULES_TOOL=EnvironmentModules # Tmod 4.1.4 is used in RHEL8
@@ -19,43 +17,34 @@ matrix:
1917
# mark build as finished as soon as job has failed
2018
fast_finish: true
2119
include:
22-
# also test default configuration with Python 2.7
20+
# also test default configuration with Python 2.6
21+
- python: 2.6
22+
env: LMOD_VERSION=7.8.22
23+
dist: trusty
24+
# single configuration to test with Lmod 6 and Python 2.7
2325
- python: 2.7
24-
env: LMOD_VERSION=6.6.3
25-
dist: xenial
26+
env: LMOD_VERSION=6.5.1 TEST_EASYBUILD_SILENCE_DEPRECATION_WARNINGS=Lmod6
2627
# also test with Python 3.6
27-
- python: 3.6
28-
env: LMOD_VERSION=6.6.3
29-
dist: xenial
30-
- python: 3.6
31-
env: LMOD_VERSION=6.5.1 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
32-
dist: xenial
3328
- python: 3.6
3429
env: LMOD_VERSION=7.8.22
35-
dist: xenial
3630
- python: 3.6
3731
env: LMOD_VERSION=7.8.22 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
38-
dist: xenial
3932
- python: 3.6
40-
env: LMOD_VERSION=8.0.9
41-
dist: xenial
33+
env: LMOD_VERSION=8.2.3
4234
- python: 3.6
43-
env: LMOD_VERSION=8.0.9 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
44-
dist: xenial
35+
env: LMOD_VERSION=8.2.3 TEST_EASYBUILD_MODULE_SYNTAX=Tcl
4536
- python: 3.6
4637
env: ENV_MOD_VERSION=3.2.10 TEST_EASYBUILD_MODULES_TOOL=EnvironmentModulesC TEST_EASYBUILD_MODULE_SYNTAX=Tcl
47-
dist: xenial
4838
- python: 3.6
4939
env: ENV_MOD_TCL_VERSION=1.147 TEST_EASYBUILD_MODULES_TOOL=EnvironmentModulesTcl TEST_EASYBUILD_MODULE_SYNTAX=Tcl
50-
dist: xenial
5140
- python: 3.6
5241
env: ENV_MOD_VERSION=4.1.4 TEST_EASYBUILD_MODULE_SYNTAX=Tcl TEST_EASYBUILD_MODULES_TOOL=EnvironmentModules # Tmod 4.1.4 is used in RHEL8
53-
dist: xenial
5442
# also test most common configuration with Python 3.5 and 3.7
5543
- python: 3.5
5644
env: LMOD_VERSION=7.8.22
57-
dist: xenial
5845
- python: 3.7
46+
env: LMOD_VERSION=7.8.22
47+
- python: 3.8
5948
dist: xenial
6049
env: LMOD_VERSION=7.8.22
6150
addons:

easybuild/tools/config.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ def mk_full_default_path(name, prefix=DEFAULT_PREFIX):
196196
'pr_target_repo',
197197
'rpath_filter',
198198
'regtest_output_dir',
199+
'silence_deprecation_warnings',
199200
'skip',
200201
'stop',
201202
'subdir_user_modules',

easybuild/tools/modules.py

Lines changed: 48 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,8 @@
130130

131131
class ModulesTool(object):
132132
"""An abstract interface to a tool that deals with modules."""
133+
# name of this modules tool (used in log/warning/error messages)
134+
NAME = None
133135
# position and optionname
134136
TERSE_OPTION = (0, '--terse')
135137
# module command to use
@@ -143,6 +145,8 @@ class ModulesTool(object):
143145
VERSION_OPTION = '--version'
144146
# minimal required version (StrictVersion; suffix rc replaced with b (and treated as beta by StrictVersion))
145147
REQ_VERSION = None
148+
# deprecated version limit (support for versions below this version is deprecated)
149+
DEPR_VERSION = None
146150
# maximum version allowed (StrictVersion; suffix rc replaced with b (and treated as beta by StrictVersion))
147151
MAX_VERSION = None
148152
# the regexp, should have a "version" group (multiline search)
@@ -159,7 +163,7 @@ def __init__(self, mod_paths=None, testing=False):
159163
# this can/should be set to True during testing
160164
self.testing = testing
161165

162-
self.log = fancylogger.getLogger(self.__class__.__name__, fname=False)
166+
self.log = fancylogger.getLogger(self.NAME, fname=False)
163167

164168
# DEPRECATED!
165169
self._modules = []
@@ -178,19 +182,20 @@ def __init__(self, mod_paths=None, testing=False):
178182

179183
# only use command path in environment variable if command in not available in $PATH
180184
if which(self.cmd) is None and env_cmd_path is not None:
181-
self.log.debug('Set command via environment variable %s: %s', self.COMMAND_ENVIRONMENT, self.cmd)
185+
self.log.debug("Set %s command via environment variable %s: %s",
186+
self.NAME, self.COMMAND_ENVIRONMENT, self.cmd)
182187
self.cmd = env_cmd_path
183188

184189
# check whether paths obtained via $PATH and $LMOD_CMD are different
185190
elif which(self.cmd) != env_cmd_path:
186-
self.log.debug("Different paths found for module command '%s' via which/$PATH and $%s: %s vs %s",
187-
self.COMMAND, self.COMMAND_ENVIRONMENT, self.cmd, env_cmd_path)
191+
self.log.debug("Different paths found for %s command '%s' via which/$PATH and $%s: %s vs %s",
192+
self.NAME, self.COMMAND, self.COMMAND_ENVIRONMENT, self.cmd, env_cmd_path)
188193

189194
# make sure the module command was found
190195
if self.cmd is None:
191-
raise EasyBuildError("No command set.")
196+
raise EasyBuildError("No command set for %s", self.NAME)
192197
else:
193-
self.log.debug('Using command %s' % self.cmd)
198+
self.log.debug('Using %s command %s', self.NAME, self.cmd)
194199

195200
# version of modules tool
196201
self.version = None
@@ -204,13 +209,13 @@ def __init__(self, mod_paths=None, testing=False):
204209

205210
def buildstats(self):
206211
"""Return tuple with data to be included in buildstats"""
207-
return (self.__class__.__name__, self.cmd, self.version)
212+
return (self.NAME, self.cmd, self.version)
208213

209214
def set_and_check_version(self):
210215
"""Get the module version, and check any requirements"""
211-
if self.COMMAND in MODULE_VERSION_CACHE:
212-
self.version = MODULE_VERSION_CACHE[self.COMMAND]
213-
self.log.debug("Found cached version for %s: %s", self.COMMAND, self.version)
216+
if self.cmd in MODULE_VERSION_CACHE:
217+
self.version = MODULE_VERSION_CACHE[self.cmd]
218+
self.log.debug("Found cached version for %s command %s: %s", self.NAME, self.COMMAND, self.version)
214219
return
215220

216221
if self.VERSION_REGEXP is None:
@@ -223,7 +228,7 @@ def set_and_check_version(self):
223228
res = ver_re.search(txt)
224229
if res:
225230
self.version = res.group('version')
226-
self.log.info("Found version %s" % self.version)
231+
self.log.info("Found %s version %s", self.NAME, self.version)
227232

228233
# make sure version is a valid StrictVersion (e.g., 5.7.3.1 is invalid),
229234
# and replace 'rc' by 'b', to make StrictVersion treat it as a beta-release
@@ -233,47 +238,59 @@ def set_and_check_version(self):
233238

234239
self.log.info("Converted actual version to '%s'" % self.version)
235240
else:
236-
raise EasyBuildError("Failed to determine version from option '%s' output: %s",
237-
self.VERSION_OPTION, txt)
241+
raise EasyBuildError("Failed to determine %s version from option '%s' output: %s",
242+
self.NAME, self.VERSION_OPTION, txt)
238243
except (OSError) as err:
239-
raise EasyBuildError("Failed to check version: %s", err)
244+
raise EasyBuildError("Failed to check %s version: %s", self.NAME, err)
240245

241246
if self.REQ_VERSION is None and self.MAX_VERSION is None:
242247
self.log.debug("No version requirement defined.")
243248

244249
elif build_option('modules_tool_version_check'):
245-
self.log.debug("Checking whether modules tool version '%s' meets requirements", self.version)
250+
self.log.debug("Checking whether %s version %s meets requirements", self.NAME, self.version)
246251

247252
if self.REQ_VERSION is not None:
248-
self.log.debug("Required minimum version defined.")
253+
self.log.debug("Required minimum %s version defined: %s", self.NAME, self.REQ_VERSION)
249254
if StrictVersion(self.version) < StrictVersion(self.REQ_VERSION):
250255
raise EasyBuildError("EasyBuild requires %s >= v%s, found v%s",
251-
self.__class__.__name__, self.REQ_VERSION, self.version)
256+
self.NAME, self.REQ_VERSION, self.version)
252257
else:
253-
self.log.debug('Version %s matches requirement >= %s', self.version, self.REQ_VERSION)
258+
self.log.debug('%s version %s matches requirement >= %s', self.NAME, self.version, self.REQ_VERSION)
259+
260+
if self.DEPR_VERSION is not None:
261+
self.log.debug("Deprecated %s version limit defined: %s", self.NAME, self.DEPR_VERSION)
262+
if StrictVersion(self.version) < StrictVersion(self.DEPR_VERSION):
263+
depr_msg = "Support for %s version < %s is deprecated, " % (self.NAME, self.DEPR_VERSION)
264+
depr_msg += "found version %s" % self.version
265+
266+
silence_deprecation_warnings = build_option('silence_deprecation_warnings') or []
267+
268+
if self.version.startswith('6') and 'Lmod6' in silence_deprecation_warnings:
269+
self.log.warning(depr_msg)
270+
else:
271+
self.log.deprecated(depr_msg, '5.0')
254272

255273
if self.MAX_VERSION is not None:
256-
self.log.debug("Maximum allowed version defined.")
274+
self.log.debug("Maximum allowed %s version defined: %s", self.NAME, self.MAX_VERSION)
257275
if StrictVersion(self.version) > StrictVersion(self.MAX_VERSION):
258276
raise EasyBuildError("EasyBuild requires %s <= v%s, found v%s",
259-
self.__class__.__name__, self.MAX_VERSION, self.version)
277+
self.NAME, self.MAX_VERSION, self.version)
260278
else:
261279
self.log.debug('Version %s matches requirement <= %s', self.version, self.MAX_VERSION)
262280
else:
263281
self.log.debug("Skipping modules tool version '%s' requirements check", self.version)
264282

265-
MODULE_VERSION_CACHE[self.COMMAND] = self.version
283+
MODULE_VERSION_CACHE[self.cmd] = self.version
266284

267285
def check_cmd_avail(self):
268286
"""Check whether modules tool command is available."""
269287
cmd_path = which(self.cmd)
270288
if cmd_path is not None:
271289
self.cmd = cmd_path
272-
self.log.info("Full path for module command is %s, so using it" % self.cmd)
290+
self.log.info("Full path for %s command is %s, so using it", self.NAME, self.cmd)
273291
else:
274-
mod_tool = self.__class__.__name__
275292
mod_tools = avail_modules_tools().keys()
276-
error_msg = "%s modules tool can not be used, '%s' command is not available" % (mod_tool, self.cmd)
293+
error_msg = "%s modules tool can not be used, '%s' command is not available" % (self.NAME, self.cmd)
277294
error_msg += "; use --modules-tool to specify a different modules tool to use (%s)" % ', '.join(mod_tools)
278295
raise EasyBuildError(error_msg)
279296

@@ -292,7 +309,7 @@ def check_module_function(self, allow_mismatch=False, regex=None):
292309
if regex is None:
293310
regex = r".*%s" % os.path.basename(self.cmd)
294311
mod_cmd_re = re.compile(regex, re.M)
295-
mod_details = "pattern '%s' (%s)" % (mod_cmd_re.pattern, self.__class__.__name__)
312+
mod_details = "pattern '%s' (%s)" % (mod_cmd_re.pattern, self.NAME)
296313

297314
if ec == 0:
298315
if mod_cmd_re.search(out):
@@ -671,7 +688,7 @@ def set_path_env_var(self, key, paths):
671688

672689
def check_module_output(self, cmd, stdout, stderr):
673690
"""Check output of 'module' command, see if if is potentially invalid."""
674-
self.log.debug("No checking of module output implemented for %s", self.__class__.__name__)
691+
self.log.debug("No checking of module output implemented for %s", self.NAME)
675692

676693
def compose_cmd_list(self, args, opts=None):
677694
"""
@@ -1075,6 +1092,7 @@ def update(self):
10751092

10761093
class EnvironmentModulesC(ModulesTool):
10771094
"""Interface to (C) environment modules (modulecmd)."""
1095+
NAME = "Environment Modules v3"
10781096
COMMAND = "modulecmd"
10791097
REQ_VERSION = '3.2.10'
10801098
MAX_VERSION = '3.99'
@@ -1110,6 +1128,7 @@ def update(self):
11101128

11111129
class EnvironmentModulesTcl(EnvironmentModulesC):
11121130
"""Interface to (Tcl) environment modules (modulecmd.tcl)."""
1131+
NAME = "ancient Tcl-only Environment Modules"
11131132
# Tcl environment modules have no --terse (yet),
11141133
# -t must be added after the command ('avail', 'list', etc.)
11151134
TERSE_OPTION = (1, '-t')
@@ -1182,6 +1201,7 @@ def remove_module_path(self, path, set_mod_paths=True):
11821201

11831202
class EnvironmentModules(EnvironmentModulesTcl):
11841203
"""Interface to environment modules 4.0+"""
1204+
NAME = "Environment Modules v4"
11851205
COMMAND = os.path.join(os.getenv('MODULESHOME', 'MODULESHOME_NOT_DEFINED'), 'libexec', 'modulecmd.tcl')
11861206
REQ_VERSION = '4.0.0'
11871207
MAX_VERSION = None
@@ -1197,9 +1217,11 @@ def check_module_output(self, cmd, stdout, stderr):
11971217

11981218
class Lmod(ModulesTool):
11991219
"""Interface to Lmod."""
1220+
NAME = "Lmod"
12001221
COMMAND = 'lmod'
12011222
COMMAND_ENVIRONMENT = 'LMOD_CMD'
12021223
REQ_VERSION = '6.5.1'
1224+
DEPR_VERSION = '7.0.0'
12031225
REQ_VERSION_DEPENDS_ON = '7.6.1'
12041226
VERSION_REGEXP = r"^Modules\s+based\s+on\s+Lua:\s+Version\s+(?P<version>\d\S*)\s"
12051227
USER_CACHE_DIR = os.path.join(os.path.expanduser('~'), '.lmod.d', '.cache')

easybuild/tools/options.py

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,7 @@ def override_options(self):
413413
'rpath-filter': ("List of regex patterns to use for filtering out RPATH paths", 'strlist', 'store', None),
414414
'set-default-module': ("Set the generated module as default", None, 'store_true', False),
415415
'set-gid-bit': ("Set group ID bit on newly created directories", None, 'store_true', False),
416+
'silence-deprecation-warnings': ("Silence specified deprecation warnings", 'strlist', 'extend', None),
416417
'sticky-bit': ("Set sticky bit on newly created directories", None, 'store_true', False),
417418
'skip-test-cases': ("Skip running test cases", None, 'store_true', False, 't'),
418419
'trace': ("Provide more information in output to stdout on progress", None, 'store_true', False, 'T'),
@@ -450,8 +451,8 @@ def config_options(self):
450451
'buildpath': ("Temporary build path", None, 'store', mk_full_default_path('buildpath')),
451452
'containerpath': ("Location where container recipe & image will be stored", None, 'store',
452453
mk_full_default_path('containerpath')),
453-
'external-modules-metadata': ("List of files specifying metadata for external modules (INI format)",
454-
'strlist', 'store', None),
454+
'external-modules-metadata': ("List of (glob patterns for) paths to files specifying metadata "
455+
"for external modules (INI format)", 'strlist', 'store', None),
455456
'hooks': ("Location of Python module with hook implementations", 'str', 'store', None),
456457
'ignore-dirs': ("Directory names to ignore when searching for files/dirs",
457458
'strlist', 'store', ['.git', '.svn']),
@@ -1473,14 +1474,25 @@ def parse_external_modules_metadata(cfgs):
14731474
"""
14741475
Parse metadata for external modules.
14751476
1476-
:param cfgs: list of config files providing metadata for external modules
1477+
:param cfgs: list of (glob patterns for) paths to config files providing metadata for external modules
14771478
:return: parsed metadata for external modules
14781479
"""
1480+
if cfgs is None:
1481+
cfgs = []
1482+
1483+
# expand glob patterns, and report error for faulty paths
1484+
paths = []
1485+
for cfg in cfgs:
1486+
res = glob.glob(cfg)
1487+
if res:
1488+
paths.extend(res)
1489+
else:
1490+
# if there are no matches, we report an error to avoid silently ignores faulty paths
1491+
raise EasyBuildError("Specified path for file with external modules metadata does not exist: %s", cfg)
1492+
cfgs = paths
14791493

14801494
# use external modules metadata configuration files that are available by default, unless others are specified
14811495
if not cfgs:
1482-
cfgs = []
1483-
14841496
# we expect to find *external_modules_metadata.cfg files in etc/ on same level as easybuild/framework
14851497
topdirs = [os.path.dirname(os.path.dirname(os.path.dirname(__file__)))]
14861498

@@ -1504,14 +1516,11 @@ def parse_external_modules_metadata(cfgs):
15041516

15051517
parsed_metadata = ConfigObj()
15061518
for cfg in cfgs:
1507-
if os.path.isfile(cfg):
1508-
_log.debug("Parsing %s with external modules metadata", cfg)
1509-
try:
1510-
parsed_metadata.merge(ConfigObj(cfg))
1511-
except ConfigObjError as err:
1512-
raise EasyBuildError("Failed to parse %s with external modules metadata: %s", cfg, err)
1513-
else:
1514-
raise EasyBuildError("Specified path for file with external modules metadata does not exist: %s", cfg)
1519+
_log.debug("Parsing %s with external modules metadata", cfg)
1520+
try:
1521+
parsed_metadata.merge(ConfigObj(cfg))
1522+
except ConfigObjError as err:
1523+
raise EasyBuildError("Failed to parse %s with external modules metadata: %s", cfg, err)
15151524

15161525
# make sure name/version values are always lists, make sure they're equal length
15171526
for mod, entry in parsed_metadata.items():

0 commit comments

Comments
 (0)