Testing keyword argument defaults #22
Description
We need to make sure all the function tests test the default values for keyword arguments, that is, calling the function without passing the keyword argument. This explicitly isn't tested in the signature tests because those tests aren't sophisticated enough to check that the correct default value is used.
I think we can abstract this somewhat in the hypothesis helpers. Something like
class DefaultValue:
"""
Stand-in value for a keyword argument not being passed
"""
def kwarg(default, kwarg_values):
return one_of(DefaultValue, just(default), kwarg_values)with a strategy helper kwarg that would work like
@given(x=arrays(), offset=kwarg(0, integers()))
def test_diagonal(x, offset):
if offset is DefaultValue:
res = diagonal(x)
offset = 0
else:
res = diagonal(x, offset=offset)
# Test behavior hereMaybe we could even abstract this further by generating using the function stubs. I'm not sure. The stubs won't contain enough information to infer what strategy values should be drawn from (even with type hints, it won't contain things like bounds or only specific sets of dtypes), but it does have the default values.
Activity
honno commentedon Sep 29, 2021
Ah this is interesting. How about a pattern where we always generate kwargs for the function tests?
If I'm correct in saying, Python interprets unpacking of an empty dict like
foo(..., **{})exactly the same asfoo(...).A helper method therefore could be:
So for example:
Personally I prefer using
<keyword> not in kw.keys()to indicate default values, as opposed to the cognitive load of introducing a custom class likeDefaultValue. I also like that Hypothesis errors will print exactly what is passed to the functions being tested. I'm not sure if I'm missing something with this solution though.asmeurer commentedon Sep 29, 2021
Yes, your way is much better.
We would also want some way to normalize the keyword arguments with the actual default values. So for instance in my diagonal example, we can just check that
offsetdoes the right thing and when offset is omitted it is tested as the default value of 0. Something likeOr maybe we could make a pseudo-strategy that uses
sharedto pass in the default values as arguments to the test function. I'm not sure what the best way is.honno commentedon Sep 29, 2021
Oh I see. I think this pattern should be sufficient for anytime we want to test defaulting assumptions.
asmeurer commentedon Sep 29, 2021
Let's just do that for now. If it gets too repetitive, we can see if something more streamlined makes sense, perhaps by extracting the default automatically from the stubs.
honno commentedon Oct 5, 2021
@asmeurer What do you think about the current use of
kwintest_creation_functions.py? I didn't quite appreciate your defaulting idea, as I'm not super happy goingkw.get("dtype", None) is Noneall the time... I think I still need to try the pattern out more, but we might want to implement youradd_defaults()idea in the future.asmeurer commentedon Oct 5, 2021
Yeah, working through the linalg tests, which have a lot of keyword arguments, I'm also thinking it would be a good idea, especially if we can extract the defaults automatically from the stubs.
honno commentedon Jan 18, 2022
All the primary tests do this well now I think.