-
-
Notifications
You must be signed in to change notification settings - Fork 642
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
update-build-files: add support for Buildifier formatter #21208
update-build-files: add support for Buildifier formatter #21208
Conversation
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.
Thanks for this.
I'm a little bit confused. It looks like the existing src/python/pants/backend/build_files/fmt/buildifier/rules.py
code is focused on formatting build files, e.g. its request is called Fmt
BuildFiles
Request
(emphasis mine). That suggests to me that there should already be a way to format build files with buildifier... but I'm probably missing something? Any insight?
# Note - this function is kept separate because it is invoked from update_build_files.py, but | ||
# not as a rule. |
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.
Could we either invoke it as a rule, or have buildfier_fmt
call this function to avoid the duplication?
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.
Yeah, I think having buildifier_fmt
call this function is probably best - this is how the ruff
backend works too:
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.
Oh of course, this has been now refactored.
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.
And now I recalled why it was duplicate - there seems to be an issue to have a call to a function which invokes other rules:
E native_engine.IntrinsicError:
Call(pants.core.util_rules.external_tool.download_external_tool,
DownloadedExternalTool) was not detected in your @rule
body at rule compile time.
This must a Pants architecture constraint. I'll poke around a bit more.
Leaving the duplicate code as is for now.
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.
Gotcha - I think this may be an issue with the new call-by-name syntax rather than the prior version of the engine. Good to know on a go-forward basis, and I think we'll need to take this into account for the other build files formatters.
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.
Huh, okay.
Happy to land this as is to keep moving forward, but it'd be good to have a follow-up that resolves the duplication... potentially even just deleting the @rule
entirely, since it's apparently not used for anything real? (as evidenced by the backend not being wired up/not doing anything)
That said, I believe it is be normal and supported for @rule
s to call non-@rule
async def
functions, so if call-by-name doesn't support it, we should know about it and probably fix it, e.g. random example await _get_relevant_source_files
:
pants/src/python/pants/backend/javascript/install_node_package.py
Lines 65 to 88 in 459d39b
async def _get_relevant_source_files( | |
sources: Iterable[SourcesField], with_js: bool = False | |
) -> SourceFiles: | |
return await Get( | |
SourceFiles, | |
SourceFilesRequest( | |
sources, | |
for_sources_types=(PackageJsonSourceField, FileSourceField) | |
+ ((ResourceSourceField, JSSourceField) if with_js else ()), | |
enable_codegen=True, | |
), | |
) | |
@rule | |
async def install_node_packages_for_address( | |
req: InstalledNodePackageRequest, union_membership: UnionMembership | |
) -> InstalledNodePackage: | |
project_env = await Get(NodeJsProjectEnvironment, NodeJSProjectEnvironmentRequest(req.address)) | |
target = project_env.ensure_target() | |
transitive_tgts = await Get(TransitiveTargets, TransitiveTargetsRequest([target.address])) | |
source_files = await _get_relevant_source_files( |
As part of the follow-up can you investigate more deeply and maybe file a bug?
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.
Thanks, Huon. Definitely, let me either do a follow-up PR with either a clean up or refactoring or filing an issue.
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.
Looks good minus Huon's comment - I think what was missing here before was wiring the existing backend up within goals/update_build_files.py
; while the backend was created in #16573, it doesn't appear that the Buildifier rules were added to the goal.
@huonw It appears the formatter was added, but not wired. This might have been intentional because it's a bit of a special usage as Buildifier is intended to be used on Starlark, not on Pants BUILD files which may have unsupported syntax. |
# Note - this function is kept separate because it is invoked from update_build_files.py, but | ||
# not as a rule. | ||
# not as a rule. This function cannot be called from the `buildifier_fmt` rule because other rules |
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.
If this is true, then _run_buildifier_fmt
is only used in tests? I think we'll also probably have to figure out another way to run the rules in other formatter backends as well since they follow the same pattern.
Is it possible to invoke the buildifier_fmt
rule in the tests directly without needing this helper function?
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.
Nope, it's used in the sources, actually:
result = await _run_buildifier_fmt( |
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 see. Is it possible to make that call invoke the buildifier_fmt
rule instead of the helper function, or does that not work either?
If the call-by-name architecture constraint is that rules should not be invoked from non-rule functions, then we should keep the chain consistent and call the right rule from format_build_file_with_buildifier
.
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.
Nope, can't call buildifier_fmt
rule from the format_build_file_with_buildifier
rule.
File "/home/username/code/pants/src/python/pants/engine/internals/rule_visitor.py", line 277, in _get_byname_awaitable
raise parse_error(
pants.engine.internals.selectors.GetParseError: Invalid Get.
/home/username/code/pants/src/python/pants/core/goals/update_build_files.py:440:
Expected an `**implicitly(..)` application as the only keyword input.
Failed for Get() in /home/username/code/pants/src/python/pants/core/goals/update_build_files.py.
@huonw I am sorry I am not sure if you have any feedback for me since the last interaction we had in this PR? No rush, just checking if I have missed something. |
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.
Looks good, other than:
- the note about follow up
- can you update the PR description before merging to reference Add buildifier as a BUILD file formatter #16573 and how this is doing the wiring up that was missed there?
# ------------------------------------------------------------------------------------------ | ||
|
||
|
||
def run_buildifier( |
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.
Good to have a test 👍
# Note - this function is kept separate because it is invoked from update_build_files.py, but | ||
# not as a rule. |
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.
Huh, okay.
Happy to land this as is to keep moving forward, but it'd be good to have a follow-up that resolves the duplication... potentially even just deleting the @rule
entirely, since it's apparently not used for anything real? (as evidenced by the backend not being wired up/not doing anything)
That said, I believe it is be normal and supported for @rule
s to call non-@rule
async def
functions, so if call-by-name doesn't support it, we should know about it and probably fix it, e.g. random example await _get_relevant_source_files
:
pants/src/python/pants/backend/javascript/install_node_package.py
Lines 65 to 88 in 459d39b
async def _get_relevant_source_files( | |
sources: Iterable[SourcesField], with_js: bool = False | |
) -> SourceFiles: | |
return await Get( | |
SourceFiles, | |
SourceFilesRequest( | |
sources, | |
for_sources_types=(PackageJsonSourceField, FileSourceField) | |
+ ((ResourceSourceField, JSSourceField) if with_js else ()), | |
enable_codegen=True, | |
), | |
) | |
@rule | |
async def install_node_packages_for_address( | |
req: InstalledNodePackageRequest, union_membership: UnionMembership | |
) -> InstalledNodePackage: | |
project_env = await Get(NodeJsProjectEnvironment, NodeJSProjectEnvironmentRequest(req.address)) | |
target = project_env.ensure_target() | |
transitive_tgts = await Get(TransitiveTargets, TransitiveTargetsRequest([target.address])) | |
source_files = await _get_relevant_source_files( |
As part of the follow-up can you investigate more deeply and maybe file a bug?
buildifier
was added to the list of supported formatters that can be used to format the BUILD files with theupdate-build-files
goal.It may be helpful if your organization is migrating from Bazel and wants to keep the style of the BUILD files
consistent or if for any other reason you may want to adopt the formatting style that is enforced by
buildifier
.The addition of the Buildifier support was added in https://github.com/pantsbuild/pants/pull/16573/files; this PR adds a relevant option to actually let users choose it to format their BUILD files.
The
buildifier
can be used on its own, but it can also be used in pair with a Python formatter, such asblack
or
ruff
. For instance, you could first runbuildifier
to sort the target fields alphabetically,and then run
black
to keep the style consistent with the rest of the Python code.E.g. running
would convert
to