-
-
Notifications
You must be signed in to change notification settings - Fork 636
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
Explicitly specified protocols for plugins #12934
Comments
I'm not following what this means. Is the protocol rule something declared by the "union base" and used by all implementers? --
I'm much less worried about boilerplate when defining a new union base. That doesn't happen frequently, especially for plugin authors, and is marked as an advanced section in our docs. Agreed w/ trying to keep boilerplate low for union members. Although, also I encourage figuring out what the ideal API looks like without considering boilerplate. Then we can try to make it more elegant. |
Correct.
This shouldn't affect the cost of defining members at all. |
Ah so you're making more explicit in the rule graph the association of What's the |
It's guaranteeing that |
Got it. So union member rules can still request whichever arbitrary types they want, like subsystems and TransitiveTargets, it sounds like. Cool |
For the API, this speaks to an idea I've been floating since #12577. Stop using the def rules():
return [UnionBase(ConcreteOutputType, GenericInputType, additional_inputs=[Platform]) The class-based decorator doesn't work well because we want exact type ID matching for union bases, not subclassing. This interface solves that, and seems to fit with your proposal? (Or this to reduce confusion: def rules():
return [UnionBase(output=ConcreteOutputType, input=GenericInputType, additional_inputs=[Platform]) |
Yea, that sounds reasonable. It would probably be better to avoid trying to choose a new name while fixing the immediate issue, so I'll stick with that. |
Cool. Btw, I think this modeling would make #12577 superfluous. So, no need to enforce new constraints like that you must implement |
I think these names could be worth the clarity: def rules():
return [
UnionBase(output=GeneratedSources, base=GenerateSourcesRequest),
UnionMember(base=GenerateSourcesRequest, member=GenerateProtobufSourcesRequest),
] Maybeeee require kwargs? Iirc a common confusion is switching the args of |
…es. (#12965) `generate_coverage_reports` requested `Addresses` as an argument, and in common usage, these were computed from the `Specs`. But that will represent an interface leak under #12934 (the only one in the pantsbuild/pants codebase, apparently): it is not expected that the `await Get(CoverageReports, CoverageDataCollection, ..)` union usage would consume the `Specs`; rather: it should operate for its inputs, which might not precisely match the `Specs`. To fix this, consume the `Addresses` associated with the coverage data instead. [ci skip-build-wheels] [ci skip-rust]
As described in #12934, we would like plugins to have better defined interfaces, and to further clarify what is available while satisfying a `@union`. Without making any API changes for `@rule` authors, we can take a first step in this direction by implementing the `Get`s for `@union` types using `Query`s instead. The effect of this is that a `@union` usage may _only_ use the explicitly provided `Param`, and no others. When compared to the usual usage of `Get` (which can consume any `Param`s which are in scope at the "call site"), this makes for a much better defined API boundary. In order to complete #12934, we will likely want to make interface changes to allow more than the single `@union`-member-`Param` specified to the `Get` to be consumed by a plugin (see the examples in that issue's description). But that is not necessary today, and this change also has the benefit of fixing the blocking issue behind #12889 and #12929 by significantly simplifying the rule graph computation (since plugin boundaries are now "hardcoded", and don't need the `Param` detection executed for `Get`s). [ci skip-build-wheels]
I think it could be made clearer, if union bases have classmethods to get the required union rules, so you register them in the same fashion as for target plugin fields. def rules():
return [
GenerateSourcesRequest.register_union_member(GeneratedSources),
GenerateSourcesRequest.register_union_member(GenerateProtobufSourcesRequest),
] |
The limitation that a Although this seems related to #12946/#7490, I don't think that it actually is: just adjusting the |
…14583) Introduce `BSPContext` as a way for handler rules to interact with the BSP protocol driver. The first use for `BSPContext` is to store the client parameters sent by the BSP client during initialization. This is then used in the Scala BSP rules to not return any build targets if the client does not support Scala. Note: Due to limitations in the engine's API regarding what values can be part of a query for a union rule, `BSPContext` is stored in SessionValues. See #12934. We still need to update the `client_params` value after the connection is initialized though, which is accomplished by calling the `initialize_connection` method outside of the engine. Thus, while this class is mutable, it is **not** mutated during calls into the engine. Concurrency: There are some concurrency issues with `BSPContext` and the fact that multiple BSP handlers can be active since the protocol allows multiple in-flight requests. See the comments for specifics. [ci skip-rust] [ci skip-build-wheels]
…antsbuild#14583) Introduce `BSPContext` as a way for handler rules to interact with the BSP protocol driver. The first use for `BSPContext` is to store the client parameters sent by the BSP client during initialization. This is then used in the Scala BSP rules to not return any build targets if the client does not support Scala. Note: Due to limitations in the engine's API regarding what values can be part of a query for a union rule, `BSPContext` is stored in SessionValues. See pantsbuild#12934. We still need to update the `client_params` value after the connection is initialized though, which is accomplished by calling the `initialize_connection` method outside of the engine. Thus, while this class is mutable, it is **not** mutated during calls into the engine. Concurrency: There are some concurrency issues with `BSPContext` and the fact that multiple BSP handlers can be active since the protocol allows multiple in-flight requests. See the comments for specifics. [ci skip-rust] [ci skip-build-wheels]
Relatedly: allowing abstract methods of a EDIT: A good concrete example of this would be to have an optional method of class Field:
@rule_helper
@staticmethod
async def compute_default_value() -> Any: ...
class JvmResolveField:
@rule_helper
@staticmethod
async def compute_default_value() -> Any:
jvm = await Get(JvmSubsystem, ..)
return jvm.default_resolve |
As discussed on #16175, we don't currently consume the "dynamic" defaults of field values for the purposes of `parametrize`. That is at least partially because there is no generic way to do so: a `Field` has no way to declare a dynamic default currently, because `Field`s cannot declare a dependency `@rule_helper` to compute their value (...yet? see #12934 (comment)). This change adds a mechanism for generically declaring the default value of a `Field`. This is definitely not the most ergonomic API: over the next few versions, many dynamic `Field` defaults will hopefully move to `__defaults__`. And #12934 (comment) will hopefully allow for significantly cleaning up those that remain. Fixes #16175. [ci skip-rust] [ci skip-build-wheels]
As discussed on pantsbuild#16175, we don't currently consume the "dynamic" defaults of field values for the purposes of `parametrize`. That is at least partially because there is no generic way to do so: a `Field` has no way to declare a dynamic default currently, because `Field`s cannot declare a dependency `@rule_helper` to compute their value (...yet? see pantsbuild#12934 (comment)). This change adds a mechanism for generically declaring the default value of a `Field`. This is definitely not the most ergonomic API: over the next few versions, many dynamic `Field` defaults will hopefully move to `__defaults__`. And pantsbuild#12934 (comment) will hopefully allow for significantly cleaning up those that remain. Fixes pantsbuild#16175. [ci skip-rust] [ci skip-build-wheels]
As discussed on pantsbuild#16175, we don't currently consume the "dynamic" defaults of field values for the purposes of `parametrize`. That is at least partially because there is no generic way to do so: a `Field` has no way to declare a dynamic default currently, because `Field`s cannot declare a dependency `@rule_helper` to compute their value (...yet? see pantsbuild#12934 (comment)). This change adds a mechanism for generically declaring the default value of a `Field`. This is definitely not the most ergonomic API: over the next few versions, many dynamic `Field` defaults will hopefully move to `__defaults__`. And pantsbuild#12934 (comment) will hopefully allow for significantly cleaning up those that remain. Fixes pantsbuild#16175. [ci skip-rust] [ci skip-build-wheels]
) (#16220) As discussed on #16175, we don't currently consume the "dynamic" defaults of field values for the purposes of `parametrize`. That is at least partially because there is no generic way to do so: a `Field` has no way to declare a dynamic default currently, because `Field`s cannot declare a dependency `@rule_helper` to compute their value (...yet? see #12934 (comment)). This change adds a mechanism for generically declaring the default value of a `Field`. This is definitely not the most ergonomic API: over the next few versions, many dynamic `Field` defaults will hopefully move to `__defaults__`. And #12934 (comment) will hopefully allow for significantly cleaning up those that remain. Fixes #16175. [ci skip-rust] [ci skip-build-wheels]
) (#16219) As discussed on #16175, we don't currently consume the "dynamic" defaults of field values for the purposes of `parametrize`. That is at least partially because there is no generic way to do so: a `Field` has no way to declare a dynamic default currently, because `Field`s cannot declare a dependency `@rule_helper` to compute their value (...yet? see #12934 (comment)). This change adds a mechanism for generically declaring the default value of a `Field`. This is definitely not the most ergonomic API: over the next few versions, many dynamic `Field` defaults will hopefully move to `__defaults__`. And #12934 (comment) will hopefully allow for significantly cleaning up those that remain. Fixes #16175. [ci skip-rust] [ci skip-build-wheels]
As discussed on pantsbuild#16175, we don't currently consume the "dynamic" defaults of field values for the purposes of `parametrize`. That is at least partially because there is no generic way to do so: a `Field` has no way to declare a dynamic default currently, because `Field`s cannot declare a dependency `@rule_helper` to compute their value (...yet? see pantsbuild#12934 (comment)). This change adds a mechanism for generically declaring the default value of a `Field`. This is definitely not the most ergonomic API: over the next few versions, many dynamic `Field` defaults will hopefully move to `__defaults__`. And pantsbuild#12934 (comment) will hopefully allow for significantly cleaning up those that remain. Fixes pantsbuild#16175. [ci skip-rust] [ci skip-build-wheels]
Ok: introducing an While it might be possible to adjust basically all So a few potential designs for this ("allowing an in-scope type to be propagated in a
|
What is the desirability of that in non-union use cases? |
From a usability perspective, I don't think that there would be any reason to do this? The point of bounding what is in scope at a callsite like this is mostly to define a particular API for a plugin consumer. Most You could maybe try to bound APIs to improve the performance of Your question gets to a good point though: changes to the |
That's exactly what I was after. Agreed. |
To be more concrete here, the idea would be something like: @rule
async def my_rule(...) -> ...:
x = await Get(UnionBaseType, union_member_type, instance)
y = await x.helper_method() where the rule graph for |
That's what I was thinking, yea. Although in your example, whether "x" is declared to be of a particular union type via a |
Should this be closed? |
Yea, this is now implemented. We should open a new ticket for #12934 (comment) though. The connection to this issue was just biasing toward syntaxes which would make it easier to add that facility. EDIT: Opened #16763. |
EDIT: #12966 took a first step in this direction, but did so without changing the user-facing API of
@union
s. This ticket now covers only exposing a new API for unions.Currently, a
@union
is able to consume "whateverParams
are in scope at its usage site(s)". While this includes the explicitly providedParam
of theGet
, it also includes any others (some necessary, some unnecessary). In particular: theOptionsBootstrapper
/Environment
used to be necessary implicitParams
(and likely will be again after #11269), and thePlatform
should be optionally consumed as well. Since there is no bound on this API/interface, when solving the rule graph, we propagate allParams
that are in scope through the request for a@union
member.Both in order to improve our APIs (by making it more explicit what the contract for a
@union
is) and to improve the determinism of combining plugins (see #12889), we should add an explicit interface to@union
s. It's possible that while doing this we should also change how@union
s are defined, to avoid increasing the boilerplate for defining a generic interface.A strawdesign might look like a new rule type shaped very much like
Query
(and possibly they should be merged, because the impact of this interface would be that they could be solved identically: as roots in theRuleGraph
, rather than as inner nodes), namedProtocol
,Interface
,CallableProtocol
,RuleProtocol
, etc:When encountering a
Get
involving a@union
input type (or perhaps explicitly invoking the protocol somehow?), we'd require a corresponding protocol rule, and then rather than registering thatGet
as dependency of the relevant@rule
, we'd register it as a the equivalent of a root/Query
in theRuleGraph
(or, again, merge the concepts).The text was updated successfully, but these errors were encountered: