From f939d37dfe0879ec0ef3e4279dd7cf09ed983a8f Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Wed, 30 Jun 2021 18:43:55 +0200 Subject: [PATCH 01/13] Add required_version argument in __init_subclass__ --- reframe/core/decorators.py | 37 ++++++++++++++++++++++++++----------- reframe/core/pipeline.py | 8 +++++++- 2 files changed, 33 insertions(+), 12 deletions(-) diff --git a/reframe/core/decorators.py b/reframe/core/decorators.py index 9890225615..fe60de2b88 100644 --- a/reframe/core/decorators.py +++ b/reframe/core/decorators.py @@ -91,6 +91,16 @@ def _validate_test(cls): raise ValueError(f'decorated test ({cls.__qualname__!r}) has one or ' f'more undefined parameters') + conditions = [VersionValidator(v) for v in cls._rfm_required_version] + if (cls._rfm_required_version and + not any(c.validate(osext.reframe_version()) for c in conditions)): + + getlogger().info('skipping incompatible test defined' + ' in class: %s' % cls.__name__) + return False + + return True + def simple_test(cls): '''Class decorator for registering tests with ReFrame. @@ -101,10 +111,9 @@ def simple_test(cls): .. versionadded:: 2.13 ''' - _validate_test(cls) - - for _ in cls.param_space: - _register_test(cls) + if _validate_test(cls): + for _ in cls.param_space: + _register_test(cls) return cls @@ -139,14 +148,14 @@ def parameterized_test(*inst): ) def _do_register(cls): - _validate_test(cls) - if not cls.param_space.is_empty(): - raise ValueError( - f'{cls.__qualname__!r} is already a parameterized test' - ) + if _validate_test(cls): + if not cls.param_space.is_empty(): + raise ValueError( + f'{cls.__qualname__!r} is already a parameterized test' + ) - for args in inst: - _register_test(cls, args) + for args in inst: + _register_test(cls, args) return cls @@ -187,6 +196,12 @@ def required_version(*versions): These should be written as ``3.5.0`` and ``3.5.0-dev.0``. ''' + warn.user_deprecation_warning( + 'the @required_version decorator is deprecated; ' + 'please set the required_version in the class arguments instead', + from_version='3.8.0' + ) + if not versions: raise ValueError('no versions specified') diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index aeea2ce53a..e73d075955 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -798,10 +798,16 @@ def __getattribute__(self, name): return super().__getattribute__(name) @classmethod - def __init_subclass__(cls, *, special=False, pin_prefix=False, **kwargs): + def __init_subclass__(cls, *, special=False, pin_prefix=False, + required_version=[], **kwargs): super().__init_subclass__(**kwargs) cls._rfm_special_test = special + if required_version: + cls._rfm_required_version = required_version + elif not hasattr(cls, '_rfm_required_version'): + cls._rfm_required_version = [] + # Insert the prefix to pin the test to if the test lives in a test # library with resources in it. if pin_prefix: From ee6dc5d5580247ee50ef84c2aff15ce7fa35a135 Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Thu, 1 Jul 2021 15:25:32 +0200 Subject: [PATCH 02/13] Fix unittests --- reframe/core/decorators.py | 2 +- unittests/test_loader.py | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/reframe/core/decorators.py b/reframe/core/decorators.py index fe60de2b88..1e921c65c8 100644 --- a/reframe/core/decorators.py +++ b/reframe/core/decorators.py @@ -199,7 +199,7 @@ def required_version(*versions): warn.user_deprecation_warning( 'the @required_version decorator is deprecated; ' 'please set the required_version in the class arguments instead', - from_version='3.8.0' + from_version='3.7.0' ) if not versions: diff --git a/unittests/test_loader.py b/unittests/test_loader.py index ae27adf4cc..73f160eb79 100644 --- a/unittests/test_loader.py +++ b/unittests/test_loader.py @@ -8,6 +8,7 @@ import reframe as rfm from reframe.core.exceptions import NameConflictError, ReframeSyntaxError +from reframe.core.warnings import ReframeDeprecationWarning from reframe.frontend.loader import RegressionCheckLoader @@ -60,9 +61,10 @@ def test_load_error(loader): def test_load_bad_required_version(loader): - with pytest.raises(ValueError): - loader.load_from_file('unittests/resources/checks_unlisted/' - 'no_required_version.py') + with pytest.warns(ReframeDeprecationWarning): + with pytest.raises(ValueError): + loader.load_from_file('unittests/resources/checks_unlisted/' + 'no_required_version.py') def test_load_bad_init(loader): From 8b1fbedc3badcb80b419c8aa9b1211b170c5f26a Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Fri, 2 Jul 2021 09:10:26 +0200 Subject: [PATCH 03/13] Add documentation in tips section --- docs/tutorial_tips_tricks.rst | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/tutorial_tips_tricks.rst b/docs/tutorial_tips_tricks.rst index bc0cffd7b2..e316e294bc 100644 --- a/docs/tutorial_tips_tricks.rst +++ b/docs/tutorial_tips_tricks.rst @@ -583,3 +583,34 @@ The following figure shows one part of the automatically generated pipeline for .. note:: The ReFrame executable must be available in the Gitlab runner that will run the CI jobs. + + +Skip tests based on the version of ReFrame +------------------------------------------ + +.. versionadded:: 3.7.0 + +When writing a test you can specify the versions of ReFrame that are compatible with it like this: + +.. code-block:: python + + class BaseFoo(rfm.RegressionTest, required_version=['x.x.x', '>y.y.y']): + ... + + @rfm.simple_test + class Foo(BaseFoo): + ... + + +``required_version`` is a list of ReFrame version specifications that this test is allowed to run. +A version specification string can have one of the following formats: + +1. ``VERSION``: Specifies a single version. +2. ``{OP}VERSION``, where ``{OP}`` can be any of ``>``, ``>=``, ``<``, ``<=``, ``==`` and ``!=``. For example, the version specification string ``'>=3.5.0'`` will allow the following test to be loaded only by ReFrame 3.5.0 and higher. The ``==VERSION`` specification is the equivalent of ``VERSION``. +3. ``V1..V2``: Specifies a range of versions. + +The test will be selected if *any* of the versions is satisfied, even if the versions specifications are conflicting. + +.. note:: + Even though this feature was available already through a class decorator, now you can set it as a class argument. + This will allow all child classes to inherit the ``required_version``, unless you specify it again. From ad28ab87290989fcf4f4361146f22bea8d1e2cea Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Fri, 2 Jul 2021 09:12:20 +0200 Subject: [PATCH 04/13] Fix indentation --- unittests/test_loader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/unittests/test_loader.py b/unittests/test_loader.py index 73f160eb79..6f8105d893 100644 --- a/unittests/test_loader.py +++ b/unittests/test_loader.py @@ -64,7 +64,7 @@ def test_load_bad_required_version(loader): with pytest.warns(ReframeDeprecationWarning): with pytest.raises(ValueError): loader.load_from_file('unittests/resources/checks_unlisted/' - 'no_required_version.py') + 'no_required_version.py') def test_load_bad_init(loader): From b3b3b710c6878c4e6b49bc6d9eb6bdd90e1126fb Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Fri, 2 Jul 2021 09:23:19 +0200 Subject: [PATCH 05/13] Update log message for required_version --- reframe/core/decorators.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/reframe/core/decorators.py b/reframe/core/decorators.py index 1e921c65c8..36b8b1d55e 100644 --- a/reframe/core/decorators.py +++ b/reframe/core/decorators.py @@ -95,8 +95,9 @@ def _validate_test(cls): if (cls._rfm_required_version and not any(c.validate(osext.reframe_version()) for c in conditions)): - getlogger().info('skipping incompatible test defined' - ' in class: %s' % cls.__name__) + getlogger().info(f"skipping test '{cls.__name__}' because it is " + f"incompatible with the current ReFrame version " + f"({osext.reframe_version().split('-')[0]})") return False return True @@ -213,8 +214,9 @@ def _skip_tests(cls): mod.__rfm_skip_tests = set() if not any(c.validate(osext.reframe_version()) for c in conditions): - getlogger().info('skipping incompatible test defined' - ' in class: %s' % cls.__name__) + getlogger().info(f"skipping test '{cls.__name__}' because it is " + f"incompatible with the current ReFrame version " + f"({osext.reframe_version().split('-')[0]})") mod.__rfm_skip_tests.add(cls) return cls From 5243667236b0dbae5f4a5745b56112f20409446b Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Tue, 13 Jul 2021 07:40:23 +0200 Subject: [PATCH 06/13] change info message to warning --- reframe/core/decorators.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/reframe/core/decorators.py b/reframe/core/decorators.py index 36b8b1d55e..1a25e578c7 100644 --- a/reframe/core/decorators.py +++ b/reframe/core/decorators.py @@ -95,9 +95,9 @@ def _validate_test(cls): if (cls._rfm_required_version and not any(c.validate(osext.reframe_version()) for c in conditions)): - getlogger().info(f"skipping test '{cls.__name__}' because it is " - f"incompatible with the current ReFrame version " - f"({osext.reframe_version().split('-')[0]})") + getlogger().warning(f"skipping test '{cls.__name__}' because it is " + f"incompatible with the current ReFrame version " + f"({osext.reframe_version().split('-')[0]})") return False return True @@ -214,9 +214,10 @@ def _skip_tests(cls): mod.__rfm_skip_tests = set() if not any(c.validate(osext.reframe_version()) for c in conditions): - getlogger().info(f"skipping test '{cls.__name__}' because it is " - f"incompatible with the current ReFrame version " - f"({osext.reframe_version().split('-')[0]})") + getlogger().warning(f"skipping test '{cls.__name__}' because it" + f" is incompatible with the current " + f"ReFrame version " + f"({osext.reframe_version().split('-')[0]})") mod.__rfm_skip_tests.add(cls) return cls From c72b4bf01dde1e5170bf8d83a7225c64682630f1 Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Tue, 13 Jul 2021 07:50:45 +0200 Subject: [PATCH 07/13] Update warning message --- reframe/core/decorators.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/reframe/core/decorators.py b/reframe/core/decorators.py index 1a25e578c7..d541fda648 100644 --- a/reframe/core/decorators.py +++ b/reframe/core/decorators.py @@ -95,9 +95,9 @@ def _validate_test(cls): if (cls._rfm_required_version and not any(c.validate(osext.reframe_version()) for c in conditions)): - getlogger().warning(f"skipping test '{cls.__name__}' because it is " - f"incompatible with the current ReFrame version " - f"({osext.reframe_version().split('-')[0]})") + getlogger().warning(f"skipping incompatible test " + f"'{cls.__qualname__}': not valid for ReFrame " + f"version {osext.reframe_version().split('-')[0]}") return False return True @@ -214,10 +214,10 @@ def _skip_tests(cls): mod.__rfm_skip_tests = set() if not any(c.validate(osext.reframe_version()) for c in conditions): - getlogger().warning(f"skipping test '{cls.__name__}' because it" - f" is incompatible with the current " - f"ReFrame version " - f"({osext.reframe_version().split('-')[0]})") + getlogger().warning( + f"skipping incompatible test '{cls.__qualname__}': not valid " + f"for ReFrame version {osext.reframe_version().split('-')[0]}" + ) mod.__rfm_skip_tests.add(cls) return cls From e3976ba26d6b8b75fb8417bba69fc1c5a274ea1d Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Tue, 13 Jul 2021 07:52:20 +0200 Subject: [PATCH 08/13] set required_version default to None --- 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 e73d075955..97fba46dca 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -799,7 +799,7 @@ def __getattribute__(self, name): @classmethod def __init_subclass__(cls, *, special=False, pin_prefix=False, - required_version=[], **kwargs): + required_version=None, **kwargs): super().__init_subclass__(**kwargs) cls._rfm_special_test = special From 5ae53d489a50e97ae7e3b0f618f1fca35629a8ad Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Tue, 13 Jul 2021 08:23:36 +0200 Subject: [PATCH 09/13] document RegressionTest arguments --- reframe/core/pipeline.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 97fba46dca..da548b46da 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -132,6 +132,25 @@ class RegressionTest(RegressionMixin, jsonext.JSONSerializable): This class provides the implementation of the pipeline phases that the regression test goes through during its lifetime. + :param required_version: is a list of ReFrame version specifications that + this test is allowed to run. + A version specification string can have one of the following formats: + + 1. ``VERSION``: Specifies a single version. + 2. ``{OP}VERSION``, where ``{OP}`` can be any of ``>``, ``>=``, ``<``, + ``<=``, ``==`` and ``!=``. For example, the version specification + string ``'>=3.5.0'`` will allow the following test to be loaded + only by ReFrame 3.5.0 and higher. The ``==VERSION`` specification + is the equivalent of ``VERSION``. + 3. ``V1..V2``: Specifies a range of versions. + + The test will be selected if *any* of the versions is satisfied, even + if the versions specifications are conflicting. + :param pin_prefix: insert the prefix to pin the test to if the test + lives in a test library with resources in it. + :param special: allows to override pipeline stage methods without issuing + a deprecation warning. + .. warning:: .. versionchanged:: 3.4.2 Multiple inheritance with a shared common ancestor is not allowed. From bf9333da5e4eb6129f3c07151e308887be1b2d2e Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Tue, 13 Jul 2021 08:27:01 +0200 Subject: [PATCH 10/13] Remove required_version doc from tips_and_tricks --- docs/tutorial_tips_tricks.rst | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/docs/tutorial_tips_tricks.rst b/docs/tutorial_tips_tricks.rst index e316e294bc..bc0cffd7b2 100644 --- a/docs/tutorial_tips_tricks.rst +++ b/docs/tutorial_tips_tricks.rst @@ -583,34 +583,3 @@ The following figure shows one part of the automatically generated pipeline for .. note:: The ReFrame executable must be available in the Gitlab runner that will run the CI jobs. - - -Skip tests based on the version of ReFrame ------------------------------------------- - -.. versionadded:: 3.7.0 - -When writing a test you can specify the versions of ReFrame that are compatible with it like this: - -.. code-block:: python - - class BaseFoo(rfm.RegressionTest, required_version=['x.x.x', '>y.y.y']): - ... - - @rfm.simple_test - class Foo(BaseFoo): - ... - - -``required_version`` is a list of ReFrame version specifications that this test is allowed to run. -A version specification string can have one of the following formats: - -1. ``VERSION``: Specifies a single version. -2. ``{OP}VERSION``, where ``{OP}`` can be any of ``>``, ``>=``, ``<``, ``<=``, ``==`` and ``!=``. For example, the version specification string ``'>=3.5.0'`` will allow the following test to be loaded only by ReFrame 3.5.0 and higher. The ``==VERSION`` specification is the equivalent of ``VERSION``. -3. ``V1..V2``: Specifies a range of versions. - -The test will be selected if *any* of the versions is satisfied, even if the versions specifications are conflicting. - -.. note:: - Even though this feature was available already through a class decorator, now you can set it as a class argument. - This will allow all child classes to inherit the ``required_version``, unless you specify it again. From a15eb7b49e92afe2456d048633c1c024678ae530 Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Tue, 13 Jul 2021 08:31:03 +0200 Subject: [PATCH 11/13] change required_version to require_version --- reframe/core/decorators.py | 2 +- reframe/core/pipeline.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/reframe/core/decorators.py b/reframe/core/decorators.py index d541fda648..57a4061875 100644 --- a/reframe/core/decorators.py +++ b/reframe/core/decorators.py @@ -199,7 +199,7 @@ def required_version(*versions): ''' warn.user_deprecation_warning( 'the @required_version decorator is deprecated; ' - 'please set the required_version in the class arguments instead', + 'please set the require_version in the class arguments instead', from_version='3.7.0' ) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index da548b46da..a60f49ad67 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -132,7 +132,7 @@ class RegressionTest(RegressionMixin, jsonext.JSONSerializable): This class provides the implementation of the pipeline phases that the regression test goes through during its lifetime. - :param required_version: is a list of ReFrame version specifications that + :param require_version: is a list of ReFrame version specifications that this test is allowed to run. A version specification string can have one of the following formats: @@ -818,12 +818,12 @@ def __getattribute__(self, name): @classmethod def __init_subclass__(cls, *, special=False, pin_prefix=False, - required_version=None, **kwargs): + require_version=None, **kwargs): super().__init_subclass__(**kwargs) cls._rfm_special_test = special - if required_version: - cls._rfm_required_version = required_version + if require_version: + cls._rfm_required_version = require_version elif not hasattr(cls, '_rfm_required_version'): cls._rfm_required_version = [] From c13d9ac1958b09a81c47a31134f7708d88204e0e Mon Sep 17 00:00:00 2001 From: Vasileios Karakasis Date: Tue, 13 Jul 2021 22:55:45 +0200 Subject: [PATCH 12/13] Address PR comments --- reframe/core/decorators.py | 4 +-- reframe/core/pipeline.py | 52 +++++++++++++++++++++++++------------- 2 files changed, 36 insertions(+), 20 deletions(-) diff --git a/reframe/core/decorators.py b/reframe/core/decorators.py index 57a4061875..c0c03e05f1 100644 --- a/reframe/core/decorators.py +++ b/reframe/core/decorators.py @@ -198,8 +198,8 @@ def required_version(*versions): ''' warn.user_deprecation_warning( - 'the @required_version decorator is deprecated; ' - 'please set the require_version in the class arguments instead', + "the '@required_version' decorator is deprecated; please set " + "the 'require_version' parameter in the class definition instead", from_version='3.7.0' ) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index a60f49ad67..2aafe2ef71 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -132,33 +132,49 @@ class RegressionTest(RegressionMixin, jsonext.JSONSerializable): This class provides the implementation of the pipeline phases that the regression test goes through during its lifetime. - :param require_version: is a list of ReFrame version specifications that - this test is allowed to run. - A version specification string can have one of the following formats: - - 1. ``VERSION``: Specifies a single version. - 2. ``{OP}VERSION``, where ``{OP}`` can be any of ``>``, ``>=``, ``<``, - ``<=``, ``==`` and ``!=``. For example, the version specification - string ``'>=3.5.0'`` will allow the following test to be loaded - only by ReFrame 3.5.0 and higher. The ``==VERSION`` specification - is the equivalent of ``VERSION``. - 3. ``V1..V2``: Specifies a range of versions. + This class accepts parameters at the *class definition*, i.e., the test + class can be defined as follows: + + .. code-block:: python + + class MyTest(RegressionTest, param='foo', ...): + + where ``param`` is one of the following: + + :param pin_prefix: the prefix to pin the test to if the test lives in a + test library with resources in it. + + :param require_version: a list of ReFrame version specifications that this + test is allowed to run. A version specification string can have one of + the following formats: + + - ``VERSION``: Specifies a single version. + - ``{OP}VERSION``, where ``{OP}`` can be any of ``>``, ``>=``, ``<``, + ``<=``, ``==`` and ``!=``. For example, the version specification + string ``'>=3.5.0'`` will allow the following test to be loaded + only by ReFrame 3.5.0 and higher. The ``==VERSION`` specification + is the equivalent of ``VERSION``. + - ``V1..V2``: Specifies a range of versions. The test will be selected if *any* of the versions is satisfied, even if the versions specifications are conflicting. - :param pin_prefix: insert the prefix to pin the test to if the test - lives in a test library with resources in it. - :param special: allows to override pipeline stage methods without issuing - a deprecation warning. - .. warning:: - .. versionchanged:: 3.4.2 - Multiple inheritance with a shared common ancestor is not allowed. + :param special: allow pipeline stage methods to be overriden in this class. .. note:: .. versionchanged:: 2.19 Base constructor takes no arguments. + .. versionadded:: 3.3 + The ``pin_prefix`` class definition parameter is added. + + .. versionadded:: 3.7.0 + The ``require_verion`` class definition parameter is added. + + .. warning:: + .. versionchanged:: 3.4.2 + Multiple inheritance with a shared common ancestor is not allowed. + ''' def disable_hook(self, hook_name): From b3c4697b5fe213edeadbdce72526b75f0cfcd0d4 Mon Sep 17 00:00:00 2001 From: Eirini Koutsaniti Date: Wed, 14 Jul 2021 13:32:31 +0200 Subject: [PATCH 13/13] address comment --- reframe/core/pipeline.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/reframe/core/pipeline.py b/reframe/core/pipeline.py index 34f29f6e6e..09e14e90e4 100644 --- a/reframe/core/pipeline.py +++ b/reframe/core/pipeline.py @@ -147,8 +147,8 @@ class MyTest(RegressionTest, param='foo', ...): where ``param`` is one of the following: - :param pin_prefix: the prefix to pin the test to if the test lives in a - test library with resources in it. + :param pin_prefix: lock the test prefix to the directory where the current + class lives. :param require_version: a list of ReFrame version specifications that this test is allowed to run. A version specification string can have one of