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
Enable unittest.suite to accept partial calls of test rules #276
Conversation
Looks like the buildifier issues in CI are pre-existing problems: https://buildkite.com/bazel/bazel-skylib/builds/1240#annotation-buildifier |
#278 should address the CI issues. |
3cf001b
to
333c80d
Compare
CI is now green! |
Note: I did not make any changes under |
It occurs to me that the new |
333c80d
to
c4f81ca
Compare
@c-parsons @jin @meisterT Any thoughts on this? |
lib/types.bzl
Outdated
Returns: | ||
True if v was created by partial.make(), False otherwise. | ||
""" | ||
return type(v) == _a_struct_type \ |
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.
This is only a heuristic. I wonder if making the partial struct an instance of a distinct provider might allow a more robust check.
FWIW, partial is hack to work around the fact that nested def statements don't currently work, but I would like to fix that, at which point we can get start to rid of partial.
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 was following the precedent and model of _is_set()
just above, since it, too, pertains to a skylib-specific type.
Note that _is_partial()
is a macro and gets used by other macros in skylib, in particular, in _suite()
in lib/unittest.bzl
. My understanding is that providers are available only within rules, so they would not be available for use by macros.
Is there an example that you could point me to that would illustrate the alternative implementation you're suggesting?
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 was following the precedent and model of _is_set() just above, since it, too, pertains to a skylib-specific type.
Yes, your inclination to follow the local style is good; it's the local style that is the problem. The is_set function and the underlying set abstraction really shouldn't exist: it's wasteful to create a struct to wrap a hash table: it adds two additional indirections and several more allocations.
The whole types.bzl file should ideally cease to exist. Any time a function's doc string is longer than its body, and it holds no special knowledge or decisions, it's a bad abstraction. Every one of these functions could be implemented just as well by the caller and they would safe themselves a dependency. (This module is Starlark's answer to JavaScript's "leftpad".)
Regarding my providers suggestion, you can construct a distinct provider and use its instances for partial values, but apparently we provide no way to query the provider of a struct value, which is what you'd need for the is_partial function, so it won't work. Please ignore it.
I suggest you move the is_partial function into the partial module.
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.
Will do. Would you like the name to remain is_partial
? That way the usage sites would look like partial.is_partial()
, which strikes me as a bit redundant. Another possibility that occurs to me is to call it made
, since it checks whether something came from make
. That way the usage sites would look like partial.made()
, to check if something came from partial.make()
.
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.
partial.is_instance(x)
? made
seems a little too cryptic.
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.
Sounds good, I'll use is_instance()
.
Now, is it okay to make partial.is_instance()
use the predicates defined in types.bzl
? Or should I code it so that partial.bzl
does not do any additional load()
s?
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 will err on the side of being self-contained. If you'd prefer to keep things DRY at the cost of making partial.bzl
depend on types.bzl
, please just let me know.
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.
Okay, I believe I've addressed all your comments so far.
lib/types.bzl
Outdated
@@ -149,4 +163,5 @@ types = struct( | |||
is_function = _is_function, | |||
is_depset = _is_depset, | |||
is_set = _is_set, | |||
is_partial = _is_partial, |
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.
Can we move this function to the partial file so that types doesn't depend on it?
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'm not sure what you mean by "so that types doesn't depend on it". In my current changes, is_partial()
in types.bzl
does not depend on partial.bzl
, just as is_set()
does not depend on any of the skylib set implementations. Both of these predicates do duplicate knowledge of how their respective data structures are implemented elsewhere.
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.
Exactly: the knowledge belongs in sets or partial, not in types. Even though there may be no physical dependency, there is a logical one, and it is unnecessary.
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.
My goal here is to enable constructing a unittest.suite with rules that have nondefault attributes. Since skylib itself provides for partial instantiation of rules, it seemed reasonable to try to solve the problem using tools that skylib already provides. But, if there's a different approach that you'd prefer to see, I'll be happy to explore it.
lib/types.bzl
Outdated
Returns: | ||
True if v was created by partial.make(), False otherwise. | ||
""" | ||
return type(v) == _a_struct_type \ |
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 was following the precedent and model of _is_set()
just above, since it, too, pertains to a skylib-specific type.
Note that _is_partial()
is a macro and gets used by other macros in skylib, in particular, in _suite()
in lib/unittest.bzl
. My understanding is that providers are available only within rules, so they would not be available for use by macros.
Is there an example that you could point me to that would illustrate the alternative implementation you're suggesting?
lib/types.bzl
Outdated
@@ -149,4 +163,5 @@ types = struct( | |||
is_function = _is_function, | |||
is_depset = _is_depset, | |||
is_set = _is_set, | |||
is_partial = _is_partial, |
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'm not sure what you mean by "so that types doesn't depend on it". In my current changes, is_partial()
in types.bzl
does not depend on partial.bzl
, just as is_set()
does not depend on any of the skylib set implementations. Both of these predicates do duplicate knowledge of how their respective data structures are implemented elsewhere.
062c9bf
to
cd7a3e1
Compare
This permits using `unittest.suite` with test rules that have nondefault attributes, while retaining compatibility with current usage. For instance, this permits setting a `timeout` on each test in a `unittest.suite`. Previously, all tests in a `unittest.suite` would have the default timeout, with no good way to alter this. This made it hard to eliminate all the warnings produced from using the `--test_verbose_timeout_warnings` bazel option. While timeouts were the motivation, the solution here is not specific to timeouts. It will permit arbitrary additional arguments to the test rules in a `unittest.suite`. Fixes bazelbuild#98
cd7a3e1
to
8909370
Compare
# | ||
# Since this check is heuristic anyway, we simply check for the | ||
# presence of a "function" attribute without checking its type. | ||
return type(v) == _a_struct_type \ |
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.
It's frustrating that Starlark doesn't offer better features for encapsulation. Like I mentioned, providers act as distinct constructors but don't have an 'instanceof' operation. Another approach would be to add a field whose value is a distinguished value, and check that it matches in is_instance, but there's no way to make the field private to stop external clients from accessing it.
Fortunately in this instance, lambdas should render the whole module unnecessary.
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 will look forward to better mechanisms in the future. In the meantime, thank you for helping to get this landed!
This permits using
unittest.suite
with test rules that have nondefaultattributes, while retaining compatibility with current usage.
For instance, this permits setting a
timeout
on each test in aunittest.suite
. Previously, all tests in aunittest.suite
wouldhave the default timeout, with no good way to alter this. This
made it hard to eliminate all the warnings produced from using the
--test_verbose_timeout_warnings
bazel option.While timeouts were the motivation, the solution here is not specific
to timeouts. It will permit arbitrary additional arguments to the test
rules in a
unittest.suite
.Fixes #98