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
Write virtualenvs for python tools as part of export
#15098
Write virtualenvs for python tools as part of export
#15098
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.
Exciting!
Do you know where to go from here? I'm thinking that we could try adding Doc formatter as another hardcoded tool. That will make it a little more obvious how we can de-duplicate things.
Once we have that good to go, we can start to tackle the issue of interpreter constraints like flake8.
Great progress so far! After looking at what you have, I am pretty sure that we are going to need a dedicated plug-in hook. I was hoping that we can leverage the plug-in hook we already have for generating lock files. But I do not think that is sufficient because it does not have information about how to install from a lock file that already exists. It only tells us how to generate a new lock file. As briefly discussed over direct message, it is unfortunate to require plug-in authors to implement a new plug-in implementation if they want to integrate with the export goal. But it is a worthy trade-off to have more boilerplate for a much better user experience, given how requested this feature is. We will also try to make the boiler plate as minimal as possible. -- To create a new plug-in hook, we are going to use unions: https://www.pantsbuild.org/docs/rules-api-unions Define a class like pants/src/python/pants/core/goals/generate_lockfiles.py Lines 56 to 68 in cc4e180
pants/src/python/pants/backend/python/lint/isort/subsystem.py Lines 97 to 105 in cc4e180
Similar to the above, we want each rule for each tool to generally only be one line. Note that there are only two things that are different in your isort and Doc formatter rules:
So, we want our plug-in hook for each tool to return back to us a From there, we can define a rule that goes from Finally, we need to call the new plug-in hook. In your rule pants/src/python/pants/core/goals/generate_lockfiles.py Lines 351 to 354 in cc4e180
(that will do some really cool engine magic. You'll go from |
Huzzah! That looks great! So now you will want to set that plug-in hook up for each python tool we have. You can search for In direct message, you mentioned an import cycle. You will need to delete the hardcoded iSort and doc formatter code from the file you have been using. I wonder if we actually want to export every tool? There are some, like the Docker parser, which do not seem useful for end-users. What do you think? -- Most tools will be exactly like you already have. The only tricky things I think will be ones that set interpreter constraints from user code, along w/ plugins: pants/src/python/pants/backend/python/lint/flake8/rules.py Lines 56 to 59 in cc4e180
In that case, you can leverage what we already have with generating lock files. Use a line like |
237bc37
to
7a711b7
Compare
export
nearly working for isort
tool.export
The final `pex venv` invocation is failing because of path conflicts. Pushing this commit for early review. [ci skip-rust] [ci skip-build-wheels]
Centralize changes in `src/python/pants/core/goals/export.py`. Had to use an ugly work-around combining export results to make mypy happy - I expect it'll change in the next commit once a 2nd tool is added. [ci skip-rust] [ci skip-build-wheels]
Nearly all of the code is duplicated from the `isort` export - a follow-up commit will clean things up once we have enough examples to identify the common patterns. [ci skip-rust] [ci skip-build-wheels]
Still only covering `isort` and `docformatter` for now. [ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
Be more consistent with exports from user code. [ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
Enabling/exposing the hook causes a panic from the rule graph. [ci skip-rust] [ci skip-build-wheels]
Rebasing onto the latest `main` seems to have fixed the "Loop subgraph" panic from the engine. [ci skip-rust] [ci skip-build-wheels]
7a711b7
to
ee0c6cd
Compare
The existing test logic is more of a unit test. Rename for clarity since I'm about to add a real integration test. [ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
[ci skip-rust] [ci skip-build-wheels]
|
||
import pytest |
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.
Deleted code here was moved to another file, see the new export_test.py
# 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]
# 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]
Pushed two commits: the latter one actually addresses the 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.
Awesome! This looks ready to merge to me beyond minor review feedback. Thank you 🎉
@@ -50,6 +50,17 @@ def debug_hint(self) -> str | None: | |||
return self.resolve | |||
|
|||
|
|||
@union | |||
class ExportPythonToolSentinel: | |||
"""Pythion tools use this as an entry point to say how to export their tool virtualenv.""" |
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.
"""Pythion tools use this as an entry point to say how to export their tool virtualenv.""" | |
"""Python tools use this as an entry point to say how to export their tool virtualenv.""" |
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 would probably be good to add instructions in a new paragraph. Subclass this class then create a rule going from the sub class to ExportPythonTool. Register the union rule. If the tool is in pantsbuild/pants, update the export test.
dest = os.path.join("python", "virtualenvs", "tools") | ||
pex = await Get(Pex, PexRequest, request.pex_request) | ||
|
||
# NOTE: We add a unique-per-tool prefix to the pex_pex path to avoid conflicts when |
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.
Excellent comment!
|
||
|
||
@dataclass(frozen=True) | ||
class ExportPythonTool: |
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 would be good to subclass EngineAwareParameter
and implement debug_hint()
to return self.resolve_name
) | ||
# TODO: We request the `ExportPythonTool` entries independently of the `ExportResult`s because | ||
# inlining the request causes a rule graph issue. Revisit after #11269. | ||
all_export_tool_results = await MultiGet( |
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 variable could probably use a rename. Maybe all export tool requests
name: str | ||
version: str | ||
experimental: bool = False | ||
type: str = "lint" |
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.
Consider using an enum
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.
Or, calling it backend prefix might be better. Use none to represent built in
# Wanted to check the output of calling each tool pex with `--version`, but not all tools | ||
# implement that flag. |
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 comment could probably be removed. Or, maybe reword it to say: "Note: not every tool implements --version
so this is the best we can get."
if BanditFieldSet.is_applicable(tgt) | ||
} | ||
constraints = InterpreterConstraints(itertools.chain.from_iterable(unique_constraints)) | ||
|
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.
Nit: unnecessary white space imo since these lines are highly related. There are a couple other places in the PR like this. Feel free to ignore, not a very big deal
export
export
[ci skip-rust] [ci skip-build-wheels]
Awesome! Thank you so much :) this will go out in the newest dev release for 2.12 |
Closes #14837
Still TODO:
pytest
plugin[ci skip-rust]
[ci skip-build-wheels]