Skip to content

Commit d093821

Browse files
author
Vasileios Karakasis
committed
Reframe 2.4 public release
1 parent f9b7b1c commit d093821

31 files changed

+2124
-517
lines changed

reframe.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#!/usr/bin/env python3
2+
import sys
23
import reframe.frontend.cli as cli
34

45
if __name__ == '__main__':

reframe/core/environments.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@
33
import subprocess
44
import reframe.utility.os as os_ext
55

6-
from reframe.core.exceptions import ReframeError, CommandError, \
7-
CompilationError
6+
from reframe.core.exceptions import ReframeError, CommandError, CompilationError
87
from reframe.core.fields import *
98
from reframe.core.modules import *
109

@@ -40,18 +39,20 @@ def set_variable(self, name, value):
4039

4140
def load(self):
4241
"""Load environment."""
43-
for k, v in self.variables.items():
44-
if k in os.environ:
45-
self._saved_variables[k] = os.environ[k]
46-
os.environ[k] = v
4742

48-
# conlicted module list must be filled at the time of load
43+
# conflicted module list must be filled at the time of load
4944
for m in self.modules:
5045
if module_present(m):
5146
self._preloaded.add(m)
5247

5348
self._conflicted += module_force_load(m)
5449

50+
for k, v in self.variables.items():
51+
if k in os.environ:
52+
self._saved_variables[k] = os.environ[k]
53+
54+
os.environ[k] = os.path.expandvars(v)
55+
5556
self.loaded = True
5657

5758

@@ -94,15 +95,15 @@ def emit_load_instructions(self, builder):
9495
# FIXME: Does not correspond to the actual process in unload()
9596
def emit_unload_instructions(self, builder):
9697
"""Emit shell instructions for loading this environment."""
98+
for k, v in self.variables.items():
99+
builder.unset_variable(k)
100+
97101
for m in self.modules:
98102
builder.verbatim('module unload %s' % m)
99103

100104
for m in self._conflicted:
101105
builder.verbatim('module load %s' % m)
102106

103-
for k, v in self.variables.items():
104-
builder.unset_variable(k)
105-
106107

107108
def __eq__(self, other):
108109
return \

reframe/core/exceptions.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def __str__(self):
1313
return self.message
1414

1515

16-
class RegressionFatalError(ReframeError):
16+
class ReframeFatalError(ReframeError):
1717
pass
1818

1919

@@ -31,18 +31,19 @@ class ConfigurationError(ReframeError):
3131

3232
class CommandError(ReframeError):
3333
def __init__(self, command, stdout, stderr, exitcode, timeout=0):
34+
if not isinstance(command, str):
35+
self.command = ' '.join(command)
36+
else:
37+
self.command = command
38+
3439
if timeout:
3540
super().__init__(
36-
"Command '%s' timed out after %d s" % (command, timeout))
41+
"Command `%s' timed out after %d s" % (self.command, timeout))
3742

3843
else:
3944
super().__init__(
40-
"Command '%s' failed with exit code: %d" % (command, exitcode))
41-
42-
if not isinstance(command, str):
43-
self.command = ' '.join(command)
44-
else:
45-
self.command = command
45+
"Command `%s' failed with exit code: %d" % \
46+
(self.command, exitcode))
4647

4748
self.stdout = stdout
4849
self.stderr = stderr
@@ -51,10 +52,12 @@ def __init__(self, command, stdout, stderr, exitcode, timeout=0):
5152

5253

5354
def __str__(self):
54-
return '{command : "%s", stdout : "%s", stderr : "%s", ' \
55-
'exitcode : %s, timeout : %d }' % \
56-
(self.command, self.stdout, self.stderr,
57-
self.exitcode, self.timeout)
55+
ret = '\n' + super().__str__() + \
56+
"\n=== STDOUT ===\n" + \
57+
self.stdout + \
58+
"\n=== STDERR ===\n" + \
59+
self.stderr
60+
return ret
5861

5962

6063
class CompilationError(CommandError):

reframe/core/logging.py

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
import logging
2+
import os
3+
import logging.handlers
4+
import sys
5+
import shutil
6+
7+
from datetime import datetime
8+
9+
from reframe.settings import settings
10+
from reframe.core.exceptions import ConfigurationError, ReframeError
11+
12+
# Reframe's log levels
13+
CRITICAL = 50
14+
ERROR = 40
15+
WARNING = 30
16+
INFO = 20
17+
VERBOSE = 19
18+
DEBUG = 10
19+
NOTSET = 0
20+
21+
22+
_log_level_names = {
23+
CRITICAL : 'critical',
24+
ERROR : 'error',
25+
WARNING : 'warning',
26+
INFO : 'info',
27+
VERBOSE : 'verbose',
28+
DEBUG : 'debug',
29+
NOTSET : 'undefined'
30+
}
31+
32+
_log_level_values = {
33+
'critical' : CRITICAL,
34+
'error' : ERROR,
35+
'warning' : WARNING,
36+
'info' : INFO,
37+
'verbose' : VERBOSE,
38+
'debug' : DEBUG,
39+
'undefined' : NOTSET,
40+
'notset' : NOTSET
41+
}
42+
43+
def _check_level(level):
44+
if isinstance(level, int):
45+
ret = level
46+
elif isinstance(level, str):
47+
norm_level = level.lower()
48+
if norm_level not in _log_level_values:
49+
raise ReframeError('logger level %s not available' % level)
50+
else:
51+
ret = _log_level_values[norm_level]
52+
else:
53+
raise TypeError('logger level %s not an int or a valid string' % level)
54+
55+
return ret
56+
57+
58+
# Redefine handlers so as to use our levels
59+
60+
class Handler(logging.Handler):
61+
def setLevel(self, level):
62+
self.level = _check_level(level)
63+
64+
65+
class StreamHandler(Handler, logging.StreamHandler):
66+
pass
67+
68+
69+
class RotatingFileHandler(Handler, logging.handlers.RotatingFileHandler):
70+
pass
71+
72+
73+
class FileHandler(Handler, logging.FileHandler):
74+
pass
75+
76+
77+
class NullHandler(Handler, logging.NullHandler):
78+
pass
79+
80+
81+
def load_from_dict(logging_config):
82+
if not isinstance(logging_config, dict):
83+
raise ConfigurationError('logging configuration is not a dict')
84+
85+
level = logging_config.get('level', 'info').lower()
86+
handlers_dict = logging_config.get('handlers', None)
87+
88+
# if not handlers_dict:
89+
# raise ConfigurationError('no entry for handlers was found')
90+
91+
logger = Logger('reframe')
92+
logger.setLevel(_log_level_values[level])
93+
94+
for handler in _extract_handlers(handlers_dict):
95+
logger.addHandler(handler)
96+
97+
return logger
98+
99+
100+
def _extract_handlers(handlers_dict):
101+
handlers = []
102+
if not handlers_dict:
103+
raise ConfigurationError('no handlers are defined for logger')
104+
105+
for filename, handler_config in handlers_dict.items():
106+
if not isinstance(handler_config, dict):
107+
raise ConfigurationError(
108+
'handler %s is not a dictionary' % filename
109+
)
110+
111+
level = handler_config.get('level', 'debug').lower()
112+
fmt = handler_config.get('format', '%(message)s')
113+
datefmt = handler_config.get('datefmt', '%FT%T')
114+
append = handler_config.get('append', False)
115+
timestamp = handler_config.get('timestamp', None)
116+
117+
if filename == '&1':
118+
hdlr = StreamHandler(stream=sys.stdout)
119+
elif filename == '&2':
120+
hdlr = StreamHandler(stream=sys.stderr)
121+
else:
122+
if timestamp:
123+
basename, ext = os.path.splitext(filename)
124+
filename = '%s_%s%s' % (
125+
basename, datetime.now().strftime(timestamp), ext
126+
)
127+
128+
hdlr = RotatingFileHandler(
129+
filename, mode='a+' if append else 'w+'
130+
)
131+
132+
hdlr.setFormatter(logging.Formatter(fmt=fmt, datefmt=datefmt))
133+
hdlr.setLevel(level)
134+
handlers.append(hdlr)
135+
136+
return handlers
137+
138+
139+
class Logger(logging.Logger):
140+
def __init__(self, name, level=logging.NOTSET):
141+
# We will set the logger level ourselves so as to bypass the base class'
142+
# check
143+
super().__init__(name, logging.NOTSET)
144+
self.level = _check_level(level)
145+
self.check = None
146+
147+
148+
def setLevel(self, level):
149+
self.level = _check_level(level)
150+
151+
152+
def makeRecord(self, name, level, fn, lno, msg, args, exc_info,
153+
func=None, extra=None, sinfo=None):
154+
# Setup dynamic fields of the check
155+
if self.check and self.check.job:
156+
extra['check_jobid'] = self.check.job.jobid
157+
158+
record = super().makeRecord(name, level, fn, lno, msg, args, exc_info,
159+
func, extra, sinfo)
160+
try:
161+
# Fill in our name for the record
162+
record.levelname = _log_level_names[level]
163+
except KeyError:
164+
# Go with the default level name of Python logging
165+
pass
166+
167+
return record
168+
169+
170+
# Override all the convenience logging functions, because we want to make
171+
# sure that they map to our level definitions
172+
173+
def critical(self, msg, *args, **kwargs):
174+
return self.log(CRITICAL, msg, *args, **kwargs)
175+
176+
177+
def error(self, msg, *args, **kwargs):
178+
return self.log(ERROR, msg, *args, **kwargs)
179+
180+
181+
def warning(self, msg, *args, **kwargs):
182+
return self.log(WARNING, msg, *args, **kwargs)
183+
184+
185+
def info(self, msg, *args, **kwargs):
186+
return self.log(INFO, msg, *args, **kwargs)
187+
188+
189+
def verbose(self, message, *args, **kwargs):
190+
self.log(VERBOSE, message, *args, **kwargs)
191+
192+
193+
def debug(self, message, *args, **kwargs):
194+
self.log(DEBUG, message, *args, **kwargs)
195+
196+
197+
class LoggerAdapter(logging.LoggerAdapter):
198+
def __init__(self, logger = None, check = None):
199+
super().__init__(
200+
logger,
201+
{
202+
'check_name' : check.name if check else 'reframe',
203+
'check_jobid' : '-1'
204+
}
205+
)
206+
if self.logger:
207+
self.logger.check = check
208+
209+
210+
def setLevel(self, level):
211+
if self.logger:
212+
super().setLevel(level)
213+
214+
215+
# Override log() function to treat `None` loggers
216+
def log(self, level, msg, *args, **kwargs):
217+
if self.logger:
218+
super().log(level, msg, *args, **kwargs)
219+
220+
221+
def verbose(self, message, *args, **kwargs):
222+
self.log(VERBOSE, message, *args, **kwargs)
223+
224+
225+
# A logger that doesn't log anything
226+
null_logger = LoggerAdapter()
227+
228+
_logger = None
229+
_frontend_logger = null_logger
230+
231+
def configure_logging(config):
232+
global _logger
233+
global _frontend_logger
234+
235+
if config == None:
236+
_logger = None
237+
_frontend_logger = null_logger
238+
return
239+
240+
_logger = load_from_dict(config)
241+
_frontend_logger = LoggerAdapter(_logger)
242+
243+
244+
def save_log_files(dest):
245+
os.makedirs(dest, exist_ok=True)
246+
for hdlr in _logger.handlers:
247+
if isinstance(hdlr, logging.FileHandler):
248+
shutil.copy(hdlr.baseFilename, dest, follow_symlinks=True)
249+
250+
def getlogger(logger_kind, *args, **kwargs):
251+
if logger_kind == 'frontend':
252+
return _frontend_logger
253+
elif logger_kind == 'check':
254+
return LoggerAdapter(_logger, *args, **kwargs)
255+
else:
256+
raise ReframeError('unknown kind of logger: %s' % logger_kind)

0 commit comments

Comments
 (0)