Skip to content

Commit

Permalink
Merge branch 'master' into mg/pbs-job-status
Browse files Browse the repository at this point in the history
  • Loading branch information
vkarak committed Sep 29, 2023
2 parents 722bca5 + 6cdf0bb commit 519b70f
Show file tree
Hide file tree
Showing 9 changed files with 82 additions and 29 deletions.
10 changes: 8 additions & 2 deletions docs/config_reference.rst
Expand Up @@ -1219,9 +1219,9 @@ The additional properties for the ``filelog`` handler are the following:
{basedir}/
system1/
partition1/
test_short_name.log
<test_class_name>.log
partition2/
test_short_name.log
<test_class_name>.log
...
system2/
...
Expand All @@ -1241,6 +1241,12 @@ The additional properties for the ``filelog`` handler are the following:
Examples of changes in the logged information are when the log record format changes or a new performance metric is added, deleted or has its name changed.
This behavior guarantees that each log file is consistent and it will not break existing parsers.

.. versionchanged:: 4.3

In the generated log file, the name of the test class name is used instead of the test's short name (which included the test's hash).
This allows the results of different variants of a parameterized test to be stored in the same log file facilitating post-processing.


The ``graylog`` log handler
---------------------------

Expand Down
2 changes: 1 addition & 1 deletion reframe/__init__.py
Expand Up @@ -6,7 +6,7 @@
import os
import sys

VERSION = '4.3.2'
VERSION = '4.3.3'
INSTALL_PREFIX = os.path.normpath(
os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
)
Expand Down
54 changes: 33 additions & 21 deletions reframe/core/hooks.py
Expand Up @@ -6,7 +6,13 @@
import functools
import inspect

import reframe.utility as util

def is_hook(func):
return hasattr(func, '_rfm_attach')


def is_dep_hook(func):
return hasattr(func, '_rfm_resolve_deps')


def attach_to(phase):
Expand All @@ -15,7 +21,7 @@ def attach_to(phase):
:meta private:
'''
def deco(func):
if hasattr(func, '_rfm_attach'):
if is_hook(func):
func._rfm_attach.append(phase)
else:
func._rfm_attach = [phase]
Expand Down Expand Up @@ -118,7 +124,7 @@ class Hook:

def __init__(self, fn):
self.__fn = fn
if not hasattr(fn, '_rfm_attach'):
if not is_hook(fn):
raise ValueError(f'{fn.__name__} is not a hook')

@property
Expand Down Expand Up @@ -152,7 +158,7 @@ class HookRegistry:
'''Global hook registry.'''

def __init__(self, hooks=None):
self.__hooks = util.OrderedSet()
self.__hooks = []
if hooks is not None:
self.update(hooks)

Expand All @@ -172,30 +178,36 @@ def add(self, v):
of the pipeline where they must be attached. Dependencies will be
resolved first in the post-setup phase if not assigned elsewhere.
'''

if hasattr(v, '_rfm_attach'):
if is_hook(v):
# Always override hooks with the same name
h = Hook(v)
self.__hooks.discard(h)
self.__hooks.add(h)
elif hasattr(v, '_rfm_resolve_deps'):
try:
pos = self.__hooks.index(h)
except ValueError:
self.__hooks.append(h)
else:
self.__hooks[pos] = h
elif is_dep_hook(v):
v._rfm_attach = ['post_setup']
self.__hooks.add(Hook(v))

def update(self, other, *, denied_hooks=None):
'''Update the hook registry with the hooks from another hook registry.
self.__hooks.append(Hook(v))

The optional ``denied_hooks`` argument takes a set of disallowed
hook names, preventing their inclusion into the current hook registry.
'''
def update(self, other, *, forbidden_names=None):
'''Update the hook registry with the hooks from another hook
registry.'''

assert isinstance(other, HookRegistry)
denied_hooks = denied_hooks or set()
forbidden_names = forbidden_names or {}
for h in other:
if h.__name__ not in denied_hooks:
# Hooks in `other` override ours
self.__hooks.discard(h)
self.__hooks.add(h)
if (h.__name__ in forbidden_names and
not is_hook(forbidden_names[h.__name__])):
continue

try:
pos = self.__hooks.index(h)
except ValueError:
self.__hooks.append(h)
else:
self.__hooks[pos] = h

def __repr__(self):
return repr(self.__hooks)
5 changes: 2 additions & 3 deletions reframe/core/meta.py
Expand Up @@ -348,9 +348,8 @@ def __init__(cls, name, bases, namespace, **kwargs):
# parent classes in reverse MRO order
for c in list(reversed(cls.mro()))[:-1]:
if hasattr(c, '_rfm_local_hook_registry'):
cls._rfm_hook_registry.update(
c._rfm_local_hook_registry, denied_hooks=namespace
)
cls._rfm_hook_registry.update(c._rfm_local_hook_registry,
forbidden_names=namespace)

cls._rfm_hook_registry.update(cls._rfm_local_hook_registry)

Expand Down
3 changes: 3 additions & 0 deletions reframe/frontend/autodetect.py
Expand Up @@ -59,6 +59,9 @@ def __enter__(self):
shutil.copy2(src, self._workdir)
except FileNotFoundError:
use_pip = True
except Exception as err:
osext.rmtree(self._workdir)
raise err

return self._workdir, use_pip

Expand Down
3 changes: 3 additions & 0 deletions reframe/utility/__init__.py
Expand Up @@ -1275,6 +1275,9 @@ def __delitem__(self, key):
def __missing__(self, key):
raise KeyError(str(key))

def __rfm_json_encode__(self):
return self.data


@functools.total_ordering
class OrderedSet(collections.abc.MutableSet):
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Expand Up @@ -34,7 +34,7 @@ install_requires =
semver

[options.packages.find]
include = reframe,reframe.*
include = reframe,reframe.*,hpctestlib.*

[options.package_data]
reframe = schemas/*
Expand Down
20 changes: 20 additions & 0 deletions unittests/test_pipeline.py
Expand Up @@ -1162,6 +1162,26 @@ def foo(self):
assert test.pipeline_hooks() == {'post_setup': [MyTest.foo]}


def test_overriden_hook_exec_order():
@test_util.custom_prefix('unittests/resources/checks')
class X(rfm.RunOnlyRegressionTest):
@run_before('run')
def foo(self):
pass

@run_before('run')
def bar(self):
pass

class Y(X):
@run_before('run')
def foo(self):
pass

test = Y()
assert test.pipeline_hooks() == {'pre_run': [Y.foo, X.bar]}


def test_disabled_hooks(HelloTest, local_exec_ctx):
@test_util.custom_prefix('unittests/resources/checks')
class BaseTest(HelloTest):
Expand Down
12 changes: 11 additions & 1 deletion unittests/test_utility.py
Expand Up @@ -799,7 +799,6 @@ def test_scoped_dict_construction():
'a': {'k1': 3, 'k2': 4},
'b': {'k3': 5}
}
namespace_dict = reframe.utility.ScopedDict()
namespace_dict = reframe.utility.ScopedDict(d)

# Change local dict and verify that the stored values are not affected
Expand Down Expand Up @@ -1088,6 +1087,17 @@ def test_scoped_dict_update():
assert scoped_dict == scoped_dict_alt


def test_scoped_dict_json_enc():
import json

d = {
'a': {'k1': 3, 'k2': 4},
'b': {'k3': 5}
}
ns_dict = reframe.utility.ScopedDict(d)
assert d == json.loads(jsonext.dumps(ns_dict))


def test_sequence_view():
l = util.SequenceView([1, 2, 2])
assert 1 == l[0]
Expand Down

0 comments on commit 519b70f

Please sign in to comment.