From 7f413707a1d723e55cb9d69b8cf994a5df1c6a2f Mon Sep 17 00:00:00 2001 From: Javier Otero Date: Tue, 24 Aug 2021 12:09:53 +0200 Subject: [PATCH 1/3] Fix bind directive --- reframe/core/meta.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/reframe/core/meta.py b/reframe/core/meta.py index d6815f14a0..5b3bc2eaf1 100644 --- a/reframe/core/meta.py +++ b/reframe/core/meta.py @@ -136,6 +136,10 @@ def __getitem__(self, key): # raise the exception from the base __getitem__. raise err from None + def reset(self, key): + '''Reset an item to rerun it through the __setitem__ logic.''' + self[key] = self[key] + class WrappedFunction: '''Descriptor to wrap a free function as a bound-method. @@ -212,14 +216,32 @@ def __prepare__(metacls, name, bases, **kwargs): namespace['required'] = variables.Undefined # Utility decorators + namespace['_rfm_ext_bound'] = set() + def bind(fn, name=None): '''Directive to bind a free function to a class. See online docs for more information. + + .. note:: + Functions bound using this directive must be re-inspected after + the class body execution has completed. This directive attaches + the external method into the class namespace and returns the + associated instance of the :class:`WrappedFunction`. However, + this instance may be further modified by other ReFrame builtins + such as :func:`run_before`, :func:`run_after`, :func:`final` and + so on after it was added to the namespace, which would bypass + the logic implemented in the :func:`__setitem__` method from the + :class:`MetaNamespace` class. Hence, we track the items set by + this directive in the ``_rfm_ext_bound`` set, so they can be + later re-inspected. ''' inst = metacls.WrappedFunction(fn, name) namespace[inst.__name__] = inst + + # Track the imported external functions + namespace['_rfm_ext_bound'].add(inst.__name__) return inst def final(fn): @@ -324,6 +346,10 @@ class was created or even at the instance level (e.g. doing for b in directives: namespace.pop(b, None) + # Reset the external functions imported through the bind directive. + for item in namespace.pop('_rfm_ext_bound'): + namespace.reset(item) + return super().__new__(metacls, name, bases, dict(namespace), **kwargs) def __init__(cls, name, bases, namespace, **kwargs): From abbeee093a055f406d7eaa95c88331c63ee831b1 Mon Sep 17 00:00:00 2001 From: Javier Otero Date: Tue, 24 Aug 2021 12:23:47 +0200 Subject: [PATCH 2/3] Add unit test --- unittests/test_meta.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/unittests/test_meta.py b/unittests/test_meta.py index 7b9f12106c..8781e42b2c 100644 --- a/unittests/test_meta.py +++ b/unittests/test_meta.py @@ -81,6 +81,9 @@ class MyTest(MyMeta): bind(ext_fn) bind(ext_fn, name='ext') + # Catch bug #2146 + final(bind(ext_fn, name='my_final')) + # Bound as different objects assert ext_fn is not ext @@ -100,6 +103,9 @@ def __init__(self): assert self.ext_fn() is self assert self.ext() is self + # Catch bug #2146 + assert 'my_final' in MyTest._rfm_final_methods + # Test __get__ MyTest() From 6e4dd10536f011fae547883607fbd48d547013c7 Mon Sep 17 00:00:00 2001 From: Javier Otero <71280927+jjotero@users.noreply.github.com> Date: Tue, 24 Aug 2021 14:17:32 +0200 Subject: [PATCH 3/3] Fix typo --- reframe/core/pipeline.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index e87659f4a3..f38bea37f8 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -1711,7 +1711,7 @@ def check_performance(self): if self.perf_variables or self._rfm_perf_fns: if hasattr(self, 'perf_patterns'): raise ReframeSyntaxError( - f"assigning a value to 'perf_pattenrs' conflicts ", + f"assigning a value to 'perf_patterns' conflicts ", f"with using the 'performance_function' decorator ", f"or setting a value to 'perf_variables'" )