-
Notifications
You must be signed in to change notification settings - Fork 117
[feat] Allow setting test variables from the command line #2117
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feat] Allow setting test variables from the command line #2117
Conversation
|
Hello @vkarak, Thank you for updating! Cheers! There are no PEP8 issues in this Pull Request!Do see the ReFrame Coding Style Guide Comment last updated at 2021-08-24 11:54:42 UTC |
Codecov Report
@@ Coverage Diff @@
## master #2117 +/- ##
==========================================
+ Coverage 86.15% 86.29% +0.13%
==========================================
Files 53 53
Lines 9465 9557 +92
==========================================
+ Hits 8155 8247 +92
Misses 1310 1310
Continue to review full report at Codecov.
|
|
@vkarak, I (think that I can) understand the reasoning behind the use of So, I wonder if the use of I am particularly concerned with the functions def _instantiate(cls, inst_args, *args, **kwargs):
kwargs['_rfm_use_params'] = True
...Can't we name the The interface of these functions is a bit more difficult to understand. What is expected to be passed in the def load_from_module(self, module, *args, **kwargs):
def load_from_file(self, filename, force=False, *args, **kwargs):
def load_from_dir(self, dirname, recurse=False, force=False, *args, **kwargs):
def load_all(self, force=False, *args, **kwargs):An example of where it became difficult to reason about the code is def load_from_file(self, filename, force=False, *args, **kwargs):
if not self._validate_source(filename):
return []
try:
return self.load_from_module(
util.import_module_from_file(filename, force), *args, **kwargs)
...As you can see, in order to know what is being passed to What do you think? |
jjotero
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've got a bit of a similar concern as @victorusu with respect to that very long argument forwarding. If we ignore for a second the type conversion issue, the metaclass already provides the mechanism to set the default values of a variable by simply doing cls.var = 5. The only reason why we're not able to do so right now, is because the test registry returns the tests already instantiated, which means it would be too late for the loader to change a variable's default value. Therefore, I think this test registry is the main "bottleneck", and I think this should be made a proper class that the loader can interact with. This would avoid that very long argument forwarding, which is very difficult to keep track of.
Regarding the type conversion, I don't think it should be handled at the variable level. The variables are type-agnostic and their sole function is to hold a default value until the class is instantiated and the value gets passed to the data descriptor, whatever that descriptor is. The descriptor is the one responsible for handling the types (if it cares about types at all), and I think that this should be the pace where the type conversion is addressed, and not in the variables.
Below I described an alternative approach, which I think would simplify things.
|
I have a minor comment regarding which entity should be the one that actually updates the variables with the values from the command line (this is just me thinking out loud). It feels a bit strange that the registry's registry.instantiate_all(extern_vars)
...
registry.instantiate_all()the second call would also return the test instances with the variables set to the values in |
Yes, I agree. The fact that the argument passed causes a side effects works against the idea of actually passing this argument. I will fix that.
I bet it won't bother @victorusu's crazy parameter exploration experiments. :-) |
jjotero
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple of comments on the error checking side of things.
Also, I'm a big fan of this new _set_defaults function in the loader, because it now gives us the room to make this loader account for any corner case we might encounter, such as the conversion of None :)
I know that is now dealt with by the Null type in the typecheck utility, but this _set_defaults now allows us to interpret certain values specially. For example, we could say that in order to set a variable to None, users would have to set it as -S x=<None> (or something like that) and this _set_defaults would match that string and then just do a test.setvar('x', None) (and not a test.setvar('x', fields.make_convertible(value))). This would allow users to set to None from the command line without having to use the custom Null type.
jjotero
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a minor comment on the __setattr__ in the metaclass. Other than that, all this looks ready for the docs to me!
|
Looks good! |
Also: - Fix crash when test registry is empty
teojgo
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lgtm, I have some minor comments that you can take into account if you think they make sense.
jjotero
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm
This PR adds the possibility to set test variables from the command line. More specifically, users can now pass
-S foo=Xfrom the command line to set the variablefootoXin all loaded tests. To set multiple variables, users should pass the-Soption multiple times.Behind the scenes the framework sets the default value of a variable to whatever was passed from the command line. This happen at the variable injection time, just before the final test is constructed and returned to the frontend. This has some implications:
num_taskswill be1regardless of the value passed from the command-line.--skip-systme-checkand--skip-prgenv-checkoption could be simply implemented by overriding thevalid_systemsandvalid_prog_environsusing this mechanism. We could even support dynamic tags that would be set from the command line and the test could act upon them.The downside is that the variable change will happen to all the loaded tests; there is no way to limit that, unless we support a syntax like this:-S my_test.foo=3, which would setfooonly for testmy_test.-S my_test.foo=3, in which casefoowill only be set formy_test. Notice that wildcards are not accepted in such a syntax.initphase. This is, however, consistent with the default values behaviour, which is exactly what this mechanism leverages.If a variable that is asked to be set from the command line is not declared in a test, it will be silently ignored.
One key aspect of this PR is to handle transparently the conversion from the string passed to the command line to the type of the variable.
For the moment, the default conversion function is pretty simple. If the field is aTypedFieldit tries to convert to each one of the supported types doingtyp(s)until it succeeds.Noneare handled specially and if the string'None'is passed and the accepted type isNoneTypeit will be converted toNone. Users when declaring variables are given the option to pass a custom conversion function, in which case it will be used instead of the default. If a conversion is not possible aTypeErrorwill be raised and the test will be skipped.The type conversion happens in the
TypedFieldby passing the command-line value wrapped in a special object. When aTypedFieldis attempted to be set by such an object, theTypedFieldwill try to implicitly convert the value to any of its supported types. If no conversion is successful, only then aTypeErrorwill be thrown. The implicit type conversions happen by callingtyp(s), wheretypis the type we want to convert to andsis the source string. All built-in types accept this type of conversion, but we have extended the type system used by theTypedFields to allow even conversion of iterables, as well as any user type using the same convention. If the field type isList[int]and the user passes the string1,2from the command line, this will be converted to the list[1, 2]automatically. Behind the scenes, this works by inspecting the call to the type constructor and delegating that to a user-defined cast method for the source type. In the case of strings, this method is__rfm_cast_str__(), butstrcan be replaced with any other type name. Users that want to use this type of conversion with their types inTypedFields, will have to use theConvertibleTypemetaclass for their classes and define the__rfm_cast_str(s)method as a class method. For the container types, the type metaclasses that implement the type system define this method to take care of the conversion. Here is an example of a custom type that supports implicit conversions from string:Note: the
__rfm_cast_<type>__methods can only take a single argument and do not support keyword arguments.This mechanism allows us to support conversions also for built-in types that they normally do not, such as theNoneType. We define theNulltype that supports conversions from'null'toNoneand we register to this type theNoneType, so thatisinstance(None, Null)is true.Conversion to
Noneare treated at the loader. The loader simply checks if the passed value is@noneand in that case sets the variable toNone, otherwise the conversion is delegated to theTypedField.LimitationsConversion of iterables is not supported, but we could add it.The implicit conversion of'null'toNonewill fail to set a string-or-None field toNone, since the types are tried in the order they are declared. If the field was defined asNone-or-string, the conversion would be successful. To mitigate that, all relevant variables have been changed toNone-or-string.Currently all the fields that allowNonevalues are of typetype(None). If we want to allow implicit conversions toNonefrom the command line, we should update all these fields totyp.Null.Todos
typecheckmoduleNoneproperlymytest.var = valsyntaxFixes #563.