Skip to content
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

Deprecate awslambda and binary goals in favor of new package goal #10881

Merged
merged 6 commits into from
Oct 1, 2020

Conversation

Eric-Arellano
Copy link
Contributor

@Eric-Arellano Eric-Arellano commented Sep 30, 2020

We now have several goals that build an asset:

  • binary
  • awslambda
  • setup-py

Soon, we will be adding support for an archive() target type as part of #10733. An archive is not really a binary, nor any of the other two. We don't want to add a fourth goal archive. It gets confusing when there are lots of goals; and it also makes things like runtime_binary_dependencies less flexible, e.g. not working with python_awslambda.

Semantically, the package goal is for transforming your code into a single exportable asset that goes into --distdir. This is different than, say, a compile goal; running on a library target should no-op.

This only deprecates awslambda and binary for now; setup-py will be a followup, as that doesn't port as easily due to passthrough args.

See https://docs.google.com/document/d/1q77XvcIU9dCzuzA2iFRQQ9AGIfV9jpR269415HdEt0E/edit# for a design doc diving more into this.

[ci skip-rust]
[ci skip-build-wheels]

@Eric-Arellano
Copy link
Contributor Author

FYI @asherf

@benjyw
Copy link
Contributor

benjyw commented Sep 30, 2020

Ignoring the implementation for now, I'm in favor of this in general: it's redundant to have a separate target type (python_awslambda) and also a separate goal awslambda. So I like the idea of having a single goal that creates an artifact, whatever that artifact type is.

But I would like to hear from others (e.g., @stuhood , @asherf, @jsirois ) what they think. Perhaps it makes sense to post this to Slack as well and get some feedback.

I also wouldn't mind doing a tiny bit of bikeshedding on the goal name. build is a bit generic and could mean different things to different people (e.g., I can imagine someone expecting build on a library target to compile it.) Other options that come to mind are bundle or assemble. But I could also be easily convinced that build is the right verb and that I am overthinking this...

@Eric-Arellano
Copy link
Contributor Author

that I am overthinking this...

This is worth overthinking. This is an extremely public API that we won't be able to change again.

@illicitonion
Copy link
Contributor

How will a user specify that they want each of the following, in a JVM world?

  • Single jar file containing all transitive deps and resources (in v1 this was binary)
  • Zip file containing each transitive dep as a separate jar (In v1 this was bundle with some config)
  • Directory containing each transitive dep as a separate jar (in v1 this was bundle)
  • Jar of just the target's classes without transitive deps (in v1 this was either compile except that wouldn't be copied to the distdir, or publish except that had other side effects, or bundle and traverse the distdir properly and ignore the lib directory)

@cosmicexplorer
Copy link
Contributor

cosmicexplorer commented Sep 30, 2020

Thoughts on the Goal Name

build is confusing to me as it doesn't seem to mean anything. Could we consider something like ./pants package or ./pants build-deployable? I'm also partial to assemble as @benjyw suggested, since I believe it's often used in a specific context in sbt to mean "create an uberjar" (this could be wrong, it's been a while).

Although I've used ./pants package in these examples, I think my current top-choice vote would go to ./pants assemble actually, because "package" is close to "build" in its ubiquity in speech and writing, and it could be perceived as applying to e.g. publishable python dists or maven artifacts, which I mention below that I would maybe want to put in a separate goal since the target types are completely different.

Proposal for an Enum Option

How will a user specify that they want each of the following, in a JVM world?

This was a super helpful glossary, thanks! To me (especially in the way you've described them here), I would perhaps expect the type of output to be controlled by an enum option e.g. ./pants package --format=xxx. This is especially useful because I have discussed with experienced pants v1 users many times about how difficult it is to remember the right ./pants incantation when these capabilities are split across separate goals -- it's not very discoverable (although the help output is much nicer in v2). This can lead to users just falling back to the goal they do know, and then using shell scripts or something to massage the output into something pants can actually create for them.

I tried to consider whether --format could be split into multiple enums at once, e.g. --format=deployable --containing=everything, but that would lead to cases @illicitonion didn't mention. However, if the only values of --containing are everything and just-this-target, that seems straightforward and generic enough. Individual language backends may decide to raise an error if they can't handle one of the values of --format or --containing, as with setup-py.

Example Usages

Single jar file containing all transitive deps and resources

./pants package --format=deployable-with-everything

Alternatively: ./pants package --format=deployable --containing=everything

Zip file containing each transitive dep as a separate jar

./pants package --format=packed-archive-with-separate-dependencies

Alternatively: ./pants package --format=packed-archive --containing=everything (the separated dependencies part would be implicit, as both of the use cases you've provided for non-jar archives assume that's how deps would be provided).

Directory containing each transitive dep as a separate jar

./pants package --format=unpacked-archive-with-separate-dependencies

Alternatively: ./pants package --format=unpacked-archive --containing=everything (perhaps --containing defaults to everything?).

Separately, I assume you raised this use case because you think it's a good idea to have a separate option for generating such an unpacked directory, but since we already assume the user's machine has an unzip binary (in a nice hygienic way), we may as well assume that they can run unzip themselves on the result (like ./pants package --format=archive xxx && mkdir dist/xxx && unzip -d dist/xxx dist/xxx.zip). But the mkdir part is a tad annoying, and not everyone remembers the argument to unzip into a specific directory, so I would understand if we decided it was worth making the option value a little longer to simplify that use case.

See https://github.com/Eric-Arellano/pants/blob/efef4c54d36e50ee2e14ad476ede8f3891dfa3f5/src/python/pants/backend/python/util_rules/extract_pex.py#L25

Jar of just the target's classes without transitive deps

./pants package --format=deployable-without-dependencies

Alternatively: ./pants package --format=deployable --containing=just-this-target

For the goals in the OP:

binary

Since @illicitonion discussed the JVM binary approaches, what's left is python binaries.

./pants package --format=deployable to create a PEX would be awesome. I believe that --containing=just-this-target would also make sense for the same reasons as the jvm, where PEX_PATH can be used to compose PEX files.

setup-py

./pants package --format=publishable (we would probably error if the user specified --containing=just-this-target, since I personally would not want to be able to publish broken wheels by specifying an otherwise-valid CLI option).

Although, thinking over it again, I would also actually expect "creating something you can publish to an artifact repository" to be a separate goal, in particular I would assume we would run ./pants publish, or ./pants publish --dry-run if we just wanted to create the wheel. pants users are presumably used to the idea of running ./pants publish on JVM targets with a provides parameter, and I think this target type is different enough from "deployable binary" that we should consider moving it to the publish goal, simply because that would otherwise introduce this --format=publishable enum value which works on a completely separate set of target types than ./pants package would.

awslambda

Not too sure about this one as I haven't used it, but possibly:
./pants package --format=deployable --containing=everything could work?

@jperkelens
Copy link
Contributor

@cosmicexplorer Regarding the --format enum, might it make sense to allow the format to be controlled by the target itself? In the python world the same code can be tied to multiple targets library binary, etc... and the build command would produce the appropriate artifact depending on the which targets specify that code as a source.

As a more specific example, we have a python library we use internally, but also publish as the entrypoint to a docker container (using a custom docker_image plugin). Our build file looks like:

python_library(
...
)

docker_image(
...
)

When we run our package goal (analogous to the proposed build goal) we generate both a wheel and build a docker image and on publish we push them to their respective repositories.

@cosmicexplorer
Copy link
Contributor

cosmicexplorer commented Sep 30, 2020

@jperkelens just dropped a great tidbit over slack:

for us its convenient to distinguish them b/c if there's an issue with publish -ables we usually want to prevent any deploys from going out

This is a great argument to separate setup-py into a publish goal vs ./pants package, I think. Especially once we reimplement "ipex" from #8793 in v2, it could be possible to create an ipex file, deploy it, then have it fail to hydrate its dependencies at runtime, or refuse to pick up your new changes if it depends on any of the libraries you've published (a bit of a long shot, but still).

@Eric-Arellano
Copy link
Contributor Author

might it make sense to allow the format to be controlled by the target itself? In the python world the same code can be tied to multiple targets library binary, etc... and the build command would produce the appropriate artifact depending on the which targets specify that code as a source.

Yes, this is exactly what would happen. ./pants build will figure out what the artifact type to build is based on the target type. Sorry I left off that context.

I don't think we can use a flag like --format to coerce a target to be built into something else. A python_binary will always be a .pex, it can't be coerced into something else. Unless the --format were to filter out the command line arguments to only be that format, rather than trying to coerce the format.

Instead, we can avoid inventing any new mechanisms and leverage filter: ./pants filter --target-type=python_library :: | xargs ./pants build. Once we have --query, that will have some sugar.

--

To answer @illicitonion's question:

Single jar file containing all transitive deps and resources (in v1 this was binary)

jvm_binary(name="jar", dependencies=[...])

Zip file containing each transitive dep as a separate jar (In v1 this was bundle with some config)

A new archive type, which replacespython_app, jvm_app etc.

archive(
  assets=["//:python_binary"],
  files=["//:files1"],
  format="zip",
)

Directory containing each transitive dep as a separate jar (in v1 this was bundle)

archive target type. Although, archive is meant to create a single file. Maybe we want to allow its format to be a loose directory, as well.

or publish except that had other side effects
When we run our package goal (analogous to the proposed build goal)

To clarify, publish would still be a separate goal. This new build/assemble goal is not meant to do anything more than to convert your code into a single asset in the dist/ folder. It should not publish/deploy.

Your publish goal could call the build goal as a precursor, though. Codewise, unifying all the goals into one makes this much easier to do. You'd be able to use have one code block for your publish goal to build any type of asset, whether it be an archive, python_distribution, python_binary, etc. Whereas right now, you have to have a distinct code block for each goal.

@illicitonion
Copy link
Contributor

What I'm reading from @Eric-Arellano's answers is that if there are multiple "flavours" of things you may want in the distdir, these distinctions should be made in BUILD files rather than from the goal specified, is that right?

If so, I think that build makes good sense as a single goal - if all of the target wrappers have a single obvious thing that "build"ing them does, build is a great verb for a build tool to do with them.

On the other hand, this seems like a bit of a step away from minimal BUILD files. I was under the impression we were generally trying to get as close to not needing BUILD files (and indeed explicit build targets) as possible. If that is the direction we're trying to head, I would have expected us to lean more into goals (e.g. by invoking ./pants singlejar --transitive some/file.java vs ./pants deployable-bundle --format=zip some/file.java), rather than by requiring users to manually create multiple targets in BUILD files for different output flavours...

@cosmicexplorer
Copy link
Contributor

cosmicexplorer commented Sep 30, 2020

Yes, this is exactly what would happen. ./pants build will figure out what the artifact type to build is based on the target type. Sorry I left off that context.

The alternative would be to instead have a ./pants publish command which only works on provides= libraries, and has options specific to publishing that would be out of place on a more general command that also produces deployable binaries.

I don't think we can use a flag like --format to coerce a target to be built into something else. A python_binary will always be a .pex, it can't be coerced into something else. Unless the --format were to filter out the command line arguments to only be that format, rather than trying to coerce the format.

I don't understand what you mean by coerce. I explicitly said that a python_binary() would likely only accept the --format=deployable value, and:

Individual language backends may decide to raise an error if they can't handle one of the values of --format or --containing, as with setup-py.

Separately, it is absolutely possible to separate the sources and the dependencies of a python_binary() target in the same way as described for jvm targets. Could you please explain why you state that "A python_binary will always be a .pex"?

A new archive type, which replaces python_app, jvm_app etc.

The pants v1 jvm backend supports multiple different output formats for a single input format. This is currently chosen between with goals. As stated above:

This is especially useful because I have discussed with experienced pants v1 users many times about how difficult it is to remember the right ./pants incantation when these capabilities are split across separate goals

@illicitonion was not describing jvm_app() vs jvm_binary(), the above four were all different and valid methods which were available for a jvm_binary() target and we would likely want to be able to support all four of them again. He also just made a great point above on minimal BUILD files.

Your publish goal could call the build goal as a precursor, though

Why are we relying on goals to call other goals? I was under the impression the v2 engine was intended to avoid that requirement?

You'd be able to use have one code block for your publish goal to build any type of asset

Why aren't we using ./pants publish --dry-run for producing distributions intended for publishing as we have for the v1 jvm backend? Not splitting this over goals would improve the performance of actually publishing multiple artifacts and allow it to be performed in parallel. The result of ./pants publish --dry-run is not a deployable binary whatsoever (it shouldn't have any dependencies embedded in it, for example) and I do not understand why we would mix it into the same goal that builds files you can immediately execute.

@Eric-Arellano
Copy link
Contributor Author

Eric-Arellano commented Sep 30, 2020

What I'm reading from @Eric-Arellano's answers is that if there are multiple "flavours" of things you may want in the distdir, these distinctions should be made in BUILD files rather than from the goal specified, is that right?

Yes, this is accurate.

On the other hand, this seems like a bit of a step away from minimal BUILD files.

The vision is to reduce boilerplate in BUILD files, such that all that remains is concise, declarative metadata about your code. We don't necessarily need to delete BUILD files entirely; they serve an important purpose, which is declaring metadata that Pants cannot infer from your code.

Here, I think it would fit the bill of being "concise, declarative metadata" in saying in your BUILD file what the format would be, e.g. python_binary vs. archive(format='tar', ...) vs. python_awslambda(). Further, each of those target types have specific distinct metadata that only makes sense to their context, such as python_awslambda having a runtime: 3.6|3.7|3.8 field, which is meaningless to another format like Pex.

I would have expected us to lean more into goals

One downside (imo) of instead expressing this metadata through the command line is that it's not checked into your repository; you have to remember to use the right combinations of flags every time you run Pants.

This is an actual issue right now with how we implemented the setup-py goal, where you have to use the passthrough args -- bdist_wheel --tag=py37.py38 to build a wheel. Unless you write a script like we have with packages.py, then everyone in your org needs to remember to use that specific invocation. Versus the metadata being committed to a BUILD file checked into VCS.

Another issue is that it makes it much harder to run ./pants build ::. You might need to tell artifact A to use a certain bit of metadata, but want artifact B to use another conflicting piece of metadata. The command line args get passed to every single matching target, so you would need to run in multiple smaller batches. This is also an issue with setup-py today.

But, one clarification. We can still allow you to use options to influence things, if we want. For example, we could add an [archive] subsystem, which you use to set the default format to something like .zip vs. .tar. This would allow users to do ./pants --archive-format=tgz --setup-py-tags=py37.py38 build ::, for example.

--

The alternative would be to instead have a ./pants publish command

I think Pants likely will want to have a publish goal in the future, but we don't have it yet. We're trying to make this breaking API change before 2.0 lands to avoid changing things on new users. It's not in anyone's scope that I know of to implement publish, so I don't think we'd want to use this goal as the new single verb, as it stands now.

Likewise, I think JP offered some interesting insight into their org's desire to have distinct package, deploy, and publish goals.

Why are we relying on goals to call other goals? I was under the impression the v2 engine was intended to avoid that requirement?

Yes, I was being slightly imprecise to keep the conversation higher level. The publish goal would not really be calling the @goal_rule. It would instead do what this code does so that ./pants test can use the result of ./pants binary in its test run:

# Create any binaries that the test depends on through the `runtime_binary_dependencies` field.
binaries: Tuple[CreatedBinary, ...] = ()
if request.field_set.runtime_binary_dependencies.value:
runtime_binary_addresses = await MultiGet(
Get(
Address,
AddressInput,
AddressInput.parse(v, relative_to=request.field_set.address.spec_path),
)
for v in request.field_set.runtime_binary_dependencies.value
)
runtime_binary_targets = await Get(Targets, Addresses(runtime_binary_addresses))
field_sets_per_target = await Get(
FieldSetsPerTarget,
FieldSetsPerTargetRequest(BinaryFieldSet, runtime_binary_targets),
)
binaries = await MultiGet(
Get(CreatedBinary, BinaryFieldSet, field_set)
for field_set in field_sets_per_target.field_sets
)

That is, find all the BinaryFieldSets and then convert those into CreatedBinarys through the engine. That now becomes Get(BuiltAsset, BuildFieldSet).

Right now, this code only works with something that can be built via binary. It does not work with python_awslambda, python_distribution, and the upcoming archive. If we change to a single goal, then this code would work with all those.

Not splitting this over goals would improve the performance of actually publishing multiple artifacts and allow it to be performed in parallel

If/when we add publish, it will use code like the above pytest_runner.py code that is like the user having had called ./pants build, but done automatically for the user. It will bring all the normal parallelism and caching you get with the engine.

@cosmicexplorer
Copy link
Contributor

cosmicexplorer commented Sep 30, 2020

One downside (imo) of instead expressing this metadata through the command line is that it's not checked into your repository

pants.toml exists and you can have multiple of them. This pattern is well-understood among pants users.

Another issue is that it makes it much harder to run ./pants build ::. You might need to tell artifact A to use a certain bit of metadata, but want artifact B to use another conflicting piece of metadata.

I don't think metadata is the same thing as the variance in output formats that was described for v1 jvm binary targets. The purpose of targets is absolutely to hold metadata. The proposed options or separate goals are for things that are independent of that metadata, such as the format you want the result to be in.

Additionally, BUILD file target definitions should be considered as stable as command-line goals until #9434 is productionized.

You also did not address this at all:

Separately, it is absolutely possible to separate the sources and the dependencies of a python_binary() target in the same way as described for jvm targets. Could you please explain why you state that "A python_binary will always be a .pex"?

This is precisely the distinction I am trying to draw between metadata and output formats. Is your goal to just not support multiple output formats because you think it's unnecessary? Could you clarify that? Because I do not understand how your proposal would support the multiple output formats that were discussed above. The difference is not between jvm_app and jvm_binary here. Either of those targets should be packageable into an uberjar, or into a form with dependencies separated. Is it clear why that is the case?

./pants --archive-format=tgz --setup-py-tags=py37.py38 build ::

This invocation would also build tons of pex files across the repo, and you quote a user who specifically describes the importance of having those goals separated. Could you speak to how your proposed solution would address that?

I'm incredibly confused by the below:

I think Pants likely will want to have a publish goal in the future, but we don't have it yet. We're trying to make this breaking API change before 2.0 lands to avoid changing things on new users. It's not in anyone's scope that I know of to implement publish, so I don't think we'd want to use this goal as the new single verb, as it stands now.

Likewise, I think JP offered some interesting insight into their org's desire to have distinct package, deploy, and publish goals.

You have misunderstood me. I was not suddenly changing my proposal to say we should use ./pants publish as a monolithic goal. I was very clear on this above.

Separately, in relation to the above, you directly refer to a user who specifically said they wanted different goals for package, deploy, and publish. How does this square with your proposal to differentiate that via highly specialized BUILD file targets? It seems you have contradicted yourself multiple times.

I'm also incredibly confused by the below:

We're trying to make this breaking API change before 2.0 lands to avoid changing things on new users. It's not in anyone's scope that I know of to implement publish, so I don't think we'd want to use this goal

If we do want to then implement a publish that goes further and uploads dists to pypi, it would become a breaking API change to not use it now and instead push it into the same goal that builds pex files. Your argument that having goals or options that don't work on all targets at first is somehow more breaking than the alternative seems completely backwards. You said this earlier:

This is worth overthinking. This is an extremely public API that we won't be able to change again.

Why would we take shortcuts now, then, instead of trying to figure out a more general API that e.g. will work on jvm targets?

Yes, I was being slightly imprecise to keep the conversation higher level.

The conversation is about which goals to create to satisfy requirements. I clearly do not understand the level of abstraction you are looking for right now. With the warning that this is an API that we won't be able to change again, why are we looking to be imprecise?

Right now, this code only works with something that can be built via binary. It does not work with python_awslambda, python_distribution, and the upcoming archive. If we change to a single goal, then this code would work with all those.

I do not understand why this is the case and it seems to be a central justification for your proposal.

@stuhood
Copy link
Member

stuhood commented Sep 30, 2020

This is probably not the right forum for this level of design discussion: it's challenging to respond directly to individual points without threading. But I would ask that everyone please recognize that the format is not ideal for discussion, and continue to assume that everyone has the same goal: making excellent software.

I've suggested that @Eric-Arellano move the design discussion onto a document.

@cosmicexplorer
Copy link
Contributor

cosmicexplorer commented Sep 30, 2020

continue to assume that everyone has the same goal: making excellent software.

I cannot tell you how much my heart sank to read this sentence. In the future when we're talking about decisions that "cannot be changed later" and are attached to an active PR please do not mistake a desire to contribute to a discussion for aggression and please make concerns about healthy discussion explicit (I do not know what this quote is referring to) and raise them in the appropriate forum.

@stuhood
Copy link
Member

stuhood commented Sep 30, 2020

please do not mistake a desire to contribute to a discussion for aggression and please make concerns about healthy discussion explicit (I do not know what this quote is referring to) and raise them in the appropriate forum.

@cosmicexplorer : I will happily discuss that in a DM if you are interested.

(EDIT: or in #committers if you would prefer that)

Thanks for the suggestion, JP!

# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
Copy link
Contributor

@cosmicexplorer cosmicexplorer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@benjyw was extremely patient in describing to me the relevance of this PR in the short term, and noted especially that the act of merging the python_awslambda() and python_binary() targets (proposed in #10893) would be a much bigger change than I had previously realized. I will be thinking about that separately and asynchronously.

After thinking a lot about it and getting more familiar with the implementation along with extremely helpful context from @Eric-Arellano, I will push any further longer-term abstract ideas about how to improve the BUILD file API for plugin authors into docs and out of the way of active work. The change in this diff (merging awslambda and binary into a single build goal) is 100% reasonable and I deeply appreciate the patience demonstrated in explaining it to me so many times.

Thanks to @stuhood as well for stepping in and recognizing the miscommunication here. That was extremely helpful.



class BuildSubsystem(GoalSubsystem):
"""Build an asset, such as an archive, JAR, PEX, AWS Lambda, etc."""
Copy link
Contributor

@cosmicexplorer cosmicexplorer Oct 1, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, this is the "asset" definition I was looking for! Thanks!

As mentioned in #10893, it's already possible to have ./pants help show information on "what's buildable" by using UnionRules (it's magic!!!!), so that addresses any other concerns I had for now (will think more on whether we can display it better).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me put up a change to rename this all to package :) I really like JP and your feedback on that rename, rather than build.


@union
class BuildFieldSet(FieldSet, metaclass=ABCMeta):
"""The fields necessary to build an asset from a target."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels not quite right to me to document this as "the fields necessary to build", when there are no fields declared and it's abstract. Perhaps

Suggested change
"""The fields necessary to build an asset from a target."""
"""The `@union` necessary to hook up a `./pants build` implementation from a target"""

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check out the docs on how we present FieldSets to plugin authors: https://www.pantsbuild.org/docs/rules-api-and-target-api#fieldsets

While the superclass is an interface, this description is accurate for subclasses. In our plugin docs and the example plugin docs, we only show how you subclass a field set. We don't talk about creating the interface itself, as that's only relevant when you're creating a generic goal like test, lint, etc; at that advanced of a use case, we'd either expect users to read our code for inspiration, or to reach out to us for help.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally would still then say in the docstring

Suggested change
"""The fields necessary to build an asset from a target."""
"""An abstract base class for the fields necessary to build an asset from a target."""

because it otherwise looks like code was moved without docstrings being updated. But I would also accept what is there now, because

While the superclass is an interface, this description is accurate for subclasses

Separately, we don't currently enforce e.g. that if a @union class has metaclass=ABCMeta, that all of its implementors must subclass it. Would that be a useful check to add? Right now, I believe it's possible to add UnionRule(BuildFieldSet, NoneType) without causing any "compile-time" error, for example. I believe the intent of this pattern has now become pretty canonical.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Separately, we don't currently enforce e.g. that if a @union class has metaclass=ABCMeta, that all of its implementors must subclass it. Would that be a useful check to add? Right now, I believe it's possible to add UnionRule(BuildFieldSet, NoneType) without causing any "compile-time" error, for example. I believe the intent of this pattern has now become pretty canonical.

Yes, I think this would be useful. There are now several times where we use this pattern of the superclass being an ABC, and expecting subclasses to implement it.

If possible, it'd be great for an implementation to leverage the stdlib and ABCs. I believe there are mechanisms already in Python to check if a subclass inherits an ABC (it might be as simple as isinstance).

Possible implementation:

base_is_an_interface = abc.is_abstract(union_base)  # I didn't check if this existed
invalid_members = [member for member in union_members if not issubclass(member, base)]
if invalid_members:
  raise Exception()

I do not think we need to support having this check be used if you are not using an ABC for your union base. It does not need to be more general than the above, imo.

Also, it's important that we continue to support a union base not being an ABC and the members not being subclasses. Namely, how we implement PluginField, where there is no inheritance relationship.

# Rust tests and lints will be skipped. Delete if not intended.
[ci skip-rust]

# Building wheels and fs_util will be skipped. Delete if not intended.
[ci skip-build-wheels]
@Eric-Arellano Eric-Arellano changed the title Deprecate awslambda and binary goals in favor of new build goal Deprecate awslambda and binary goals in favor of new package goal Oct 1, 2020
@coveralls
Copy link

Coverage Status

Coverage remained the same at 0.0% when pulling 4c0a8d2 on Eric-Arellano:build-goal into c03b89d on pantsbuild:master.

@Eric-Arellano Eric-Arellano merged commit 2525809 into pantsbuild:master Oct 1, 2020
@Eric-Arellano Eric-Arellano deleted the build-goal branch October 1, 2020 17:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants