diff --git a/reframe/core/logging.py b/reframe/core/logging.py index ec1273602a..895293c8c9 100644 --- a/reframe/core/logging.py +++ b/reframe/core/logging.py @@ -408,10 +408,10 @@ def error(self, msg, *args, **kwargs): return self.log(ERROR, msg, *args, **kwargs) def warning(self, msg, *args, **kwargs): - return self.log(WARNING, msg, *args, **kwargs) + self.log(WARNING, msg, *args, **kwargs) def info(self, msg, *args, **kwargs): - return self.log(INFO, msg, *args, **kwargs) + self.log(INFO, msg, *args, **kwargs) def verbose(self, message, *args, **kwargs): self.log(VERBOSE, message, *args, **kwargs) @@ -423,6 +423,10 @@ def debug2(self, message, *args, **kwargs): self.log(DEBUG2, message, *args, **kwargs) +# This is a cache for warnings that we don't want to repeat +_WARN_ONCE = set() + + class LoggerAdapter(logging.LoggerAdapter): def __init__(self, logger=None, check=None): super().__init__( @@ -550,7 +554,13 @@ def debug2(self, message, *args, **kwargs): def verbose(self, message, *args, **kwargs): self.log(VERBOSE, message, *args, **kwargs) - def warning(self, message, *args, **kwargs): + def warning(self, message, *args, cache=False, **kwargs): + if cache: + if message in _WARN_ONCE: + return + + _WARN_ONCE.add(message) + message = '%s: %s' % (sys.argv[0], message) if self.colorize: message = color.colorize(message, color.YELLOW) @@ -594,6 +604,7 @@ def __init__(self, check=None, level=DEBUG): self._orig_logger = _context_logger if check is not None: _context_logger = LoggerAdapter(_logger, check) + _context_logger.colorize = self._orig_logger.colorize def __enter__(self): return _context_logger diff --git a/reframe/core/modules.py b/reframe/core/modules.py index 35e019ec8d..e4cc552310 100644 --- a/reframe/core/modules.py +++ b/reframe/core/modules.py @@ -1008,6 +1008,14 @@ def unload_all(self): class NoModImpl(ModulesSystemImpl): '''A convenience class that implements a no-op a modules system.''' + def _warn(self, msg): + getlogger().warning( + f"no modules system is set: {msg}: " + f"check the 'modules_system' configuration " + f"parameter for your system", + cache=True + ) + def available_modules(self, substr): return [] @@ -1021,10 +1029,10 @@ def _execute(self, cmd, *args): return '' def load_module(self, module): - pass + self._warn(f'module {module.name!r} will not be loaded') def unload_module(self, module): - pass + self._warn(f'module {module.name!r} will not be unloaded') def is_module_loaded(self, module): # @@ -1043,21 +1051,23 @@ def modulecmd(self, *args): return '' def unload_all(self): - pass + self._warn('no module will be purged') def searchpath(self): return [] def searchpath_add(self, *dirs): - pass + self._warn('MODULEPATH will not be modified') def searchpath_remove(self, *dirs): - pass + self._warn('MODULEPATH will not be modified') def emit_load_instr(self, module): + self._warn(f'module {module.name!r} will not be loaded') return [] def emit_unload_instr(self, module): + self._warn(f'module {module.name!r} will not be unloaded') return [] diff --git a/unittests/test_logging.py b/unittests/test_logging.py index 0b91e2d781..caa374fb05 100644 --- a/unittests/test_logging.py +++ b/unittests/test_logging.py @@ -328,6 +328,18 @@ def test_handler_noappend(make_exec_ctx, config_file, logfile): assert _found_in_logfile('bar', logfile) +def test_warn_once(default_exec_ctx, logfile): + rlog.configure_logging(rt.runtime().site_config) + rlog.getlogger().warning('foo', cache=True) + rlog.getlogger().warning('foo', cache=True) + rlog.getlogger().warning('foo', cache=True) + _flush_handlers() + _close_handlers() + + with open(logfile, 'rt') as fp: + assert len(re.findall('foo', fp.read())) == 1 + + def test_date_format(default_exec_ctx, logfile): rlog.configure_logging(rt.runtime().site_config) rlog.getlogger().warning('foo')