-
-
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
Lockfiles: what resolve to use with MyPy and Pylint operating on python_library
targets?
#12714
Comments
For completeness, clearly you could just pluralize the field and then check any given resolve has all file dependencies whose resolves contain it. This reflects libraries in-practice. They generally need to work with many resolves when you ship to PyPI. Pants is a good anti-example since it ships an API alongside a binary and really only works with 1 resolve since its all pinned out on PyPI. Plugin authors are then forced into that 1 resolve FAPP. |
Oh, interesting! Something like a Thanks for that metaphor of libraries vs. binaries in general, that makes sense. I want to think more about that, but this seems like a solid improvement from the "find dependees" proposal. Thank you! |
To be clear, the metaphor is not mine. This is standard parlance in all lockfile communities I've encountered and the language is uniformly, lockfiles are for binaries and never libraries. Here resolves ~= locks and the same considerations are in play. |
As discussed in the thread on this topic, there might be an alternative to the |
Ah, yeah, that type of check is possible via #12610. Interesting. It does require a lot of magic fwict, though, such as a user figuring out how to force a particular resolve to be used. For example, if the target doesn't have any roots and you don't want it to use -- So far, John's proposal of (We can also reduce the boilerplate with the bigger project of re-envisioning targets, so it's easier to say e.g. "this whole source tree uses these resolves") |
Presumably you reduce boilerplate by having a default_compatible_resolves option for defining up in pants.toml. By default say that's ALL. Then you only need to ad metadata to library targets that are not compatible with all resolves. Presumably, that cuts down on boilerplate significantly in many cases? |
I definitely like the idea of specifying multiple compatible resolutions here. I'm trying to wrap my head around how we deal with a library being used as a dependency for another target here. If the library has a requirement |
Requirements are modeled as That is, the lockfile would either need to include both targets; or if that's not possible with the double requirement, use two distinct resolves. |
I think Thanks again for the feedback everyone! |
Part of #11165 and builds off of #12703. Rather than having a single option `[python-setup].experimental_lockfile`, users set `[python-setup].experimental_resolves_to_lockfiles` to define 0-n "named resolves" that associate a lockfile with a name: ```toml [python-setup] experimental_resolves_to_lockfiles = { lock1 = "lock1.txt", lock2 = "lock2.txt" } ``` Then, individual `pex_binary` targets can specify which resolve to use: ```python pex_binary(name="reversion", entry_point="reversion.py", experimental_resolve="lock1") ``` In a followup, we'll add a mechanism to set the default resolve. Users can generate that lockfile with `./pants generate-lockfiles` (all resolves) or `./pants generate-lockfiles --resolve=<name>`: ``` ❯ ./pants generate-lockfiles --resolve=lock1 --resolve=lock2 15:55:56.60 [INFO] Completed: Generate lockfile for lock1 15:55:56.61 [INFO] Completed: Generate lockfile for lock2 15:55:57.02 [INFO] Wrote lockfile for the resolve `lock1` to lock1.txt 15:55:57.02 [INFO] Wrote lockfile for the resolve `lock2` to lock2.txt ``` Then, it will be consumed with `./pants package` and `./pants run`. Pants will extract the proper subset from that lockfile, meaning that the lockfile can safely be a superset of what is used for the particular build. ``` ❯ ./pants package build-support/bin: ... 15:56:33.87 [INFO] Completed: Installing lock1.txt for the resolve `lock1` 15:56:34.39 [INFO] Completed: Installing lock2.txt for the resolve `lock2` 15:56:34.48 [INFO] Completed: Extracting 1 requirement to build build-support.bin/generate_user_list.pex from lock1_lockfile.pex: pystache==0.5.4 ... ``` If the lockfile is incompatible, we will (soon) warn or error with instructions to either use a new resolve or regenerate the lockfile. In followups, this field will be hooked up to other targets like `python_awslambda` and `python_tests`. We will likely also add a new field `compatible_resolves` to `python_library`, per #12714, which is a list of resolves. "Root targets" like `python_tests` and `pex_binary` will validate that all their dependencies are compatible. When you operate directly on a `python_library` target, like running MyPy on it, we will choose any of the possible resolves. You will be able to set your own default for this field. [ci skip-rust] [ci skip-build-wheels]
Thus far, multiple user lockfiles has been following the approach of https://docs.google.com/document/d/1sr1O7W9517w8YbdzfE56xvDb72LafK94Fwxm0juIRFc/edit: only "root" targets like
python_tests
andpex_binary
have aresolve
field to choose which resolve/lockfile to use. Dependencies are irrelevant to choosing a resolve, e.g. if apython_tests
target depends on anotherpython_tests
target (weird to do, but legal), we simply go with the resolve of the target currently being built with./pants test
.This scheme works great for
./pants package
/pex_binary
and./pants test
/python_tests
. But it's foiled by MyPy and Pylint, which both require resolving 3rdparty dependencies and can treatpython_library
targets as the root, e.g../pants typecheck src/python/pants/util/strutil.py
.Naive, unaccpetable solution:
python_library
hasresolve
fieldIt would be unambiguous what resolve to use. However, we'd need to validate that the entire closure is using a consistent resolve, meaning all dependencies and dependees. (It's only possible to use a single resolve in a build)
This means that a
python_library
can only ever work with a single resolve, which is extremely limiting. For example, ifstrutil.py
is set todefault
, then anything that usesstrutil.py
transitively must use the same resolve. Especially given that we will requirepex_binary
targets w/platforms
set to use a platform-aware lockfile (#12612), that is a non-starter.Possible solution? Use dependees roots
Find all root targets that depend on the
python_library
and use their resolve.What to do if no roots
Use the
default
resolve.What to do if >1 resolves
If >1 roots, and those use different resolves, then we "should" be able to use any of those resolves. All those resolves, by definition, should be compatible with the library and its deps. If not, our invalidation check will warn/error.
We probably want to avoid choosing platform-aware resolves (#12612) when possible, as those might only work with platforms different than the host.
Performance concern
Computing
dependees
can be slow, as it requires scanning the whole repository. I don't know of a way to improve this.A simple hack is that if the # of resolves (from [python-setup]) is <= 1, then we can short circuit.
The text was updated successfully, but these errors were encountered: