-
Notifications
You must be signed in to change notification settings - Fork 2.3k
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
Support lock dependencies with lowest solvable versions #3527
Comments
You might want to look into tox, it is very good at this kind of things. |
Thank you for the rapid response @sinoroc. tox could definitely help to run the tests, but this feature is for defining the dependencies instead. (As far as I know, you have to provide the dependencies to tox) This feature will simplify the definition of an environment with the lowest possible dependency versions which could potentially be an environment where final users could install the package with So for example, if my library support As you can imagen, this could cause bad user experience as users with flask Sorry if tox offers this feature already that I am not aware of, if so, could you please share an example @sinoroc ? |
tox allows you test against multiple versions of the same library, for example here: So you would need to specify a test environment that tests your project against the lowest boundary of the version range for flask for example, and one for the highest, and then a couple more significant ones in the middle. Actually, usually you would pick some significant libraries your project relies on: flask is a perfect example. And then create one test environment for each And then if your test suite, actually covers all the features of flask that your project uses, then you will immediately know if something is wrong when the test suite fails in one of the test environments. |
exactly, that is a way to do it, I guess I am just lazy that I don't want to do that manually and prefer to have poetry to define my lower and higher dependency boundaries in a "smart way". Although there is manual configuration that can be done, I still think having this feature can improve the dev-cycle and be useful for multiple cases. I found in the code that by just changing the poetry/poetry/puzzle/provider.py Lines 136 to 142 in 2aab9bc
it will prioritize the lower versions and then by just connecting with the commands, it can easily be implemented (hopefully) |
From my point of view, this is a case where you really want to have a human brain deciding which combinations of versions of dependencies should be tested.
Not sure what you mean here. Poetry really gives you just 1 combination, which does not necessarily end up including the highest bound of your dependency. Unless everyone using your library also uses your lockfile (which is most likely not the case for obvious reasons), everyone will likely end up with a different combination of dependencies, nothing poetry can do about it, different tools have different dependecy resolution algorithms that deliver different results. So it is up to you to pick up some meaningful combinations of dependencies to test against. Dependencies and dependencies of dependencies are not as simple as one might think, testing only the lowest and the highest is not meaningful in my eyes. Again poetry just offers you 1 working combination, not the "highest" bound (whatever that could possibly mean). |
Thank you @sinoroc your feedback is always very clear and instructive, but I have a couple of comments.
related to that ☝️ , I kind of think the opposite. In an ideal world, you should only have to define your dependencies constrains (we do this in pyproject.toml already) and then a computer would go through the dependency graph and decide what are the best set of dependencies to tests. I am not saying this is easy to do or if poetry should be responsible for it, but I think it is theoretically doable and this feature request is a step closer to that world.
About this ☝️ , poetry claims to get the latest versions of the dependencies with the
and looking at the code and it looks like the Also, I agree with you that testing only the lowest and the highest dependencies might not be fully meaningful in some cases, but it can complement the manual approach you mentioned before (https://stackoverflow.com/q/59377071) and definitely improve the current normal behavior of only testing you application once. Any way, I feel like this can help the community, but if you don't think this is useful, I can build an external script to solve my issues otherwise Thank you again for the patience and all the feedback :) |
I totally see your point, and I wish it were possible to automate this as you described (more or less), I am just not confident it is technically feasible in a meaningful way (there are just way too many moving parts, finding one working combination is already hard enough). Just my personal opinion, the maintainers will decide. Maybe there is a compromise to be found somewhere, or probably this could be feasible with a plug-in. |
I really like this idea and was looking for this functionality myself. A library will often try to keep the version dependencies as open as possible so that it is as compatible as possible with other libraries that may be installed into the same virtual environment. But since you're local environment may have newer library versions (in your lock file), it is easy to use functionality from a library that was introduced after your lowest allowed library versions. Of course you cannot check all combinations of dependencies, but I think checking all of the minimum permissible versions and the latest/newest versions would give pretty strong support if yours test coverage is good. |
I think, rather than asking Poetry to solve for the lowest possible version, one could ask it to install exactly the lowest specified versions of everything. On one hand, this can seem restrictive, as one would need to manually fix versions until this becomes solvable. But on the other hand, I think it's better, because
And, well, then it also becomes definitely technically feasible. For now I'll be doing literal replacement of sed -i -E 's/"(\^|>=)([0-9])/"==\2/' pyproject.toml And I'm actually happy with that, because I don't pull in Poetry for my CI anyway. |
IMHO, this feature makes a lot of sense. It is under discussion for pip as well. |
Ya, I don't understand how library authors are not clamoring for this. If you put a minimum version in your dependency, it makes 100% sense to test with that version in case you accidentally use a feature from a newer version of it. Then you can decide to bump the dependency version or re-work your code to maintain that older compatibility. I've seen this happen on multiple projects and each would have been saved by testing against the minimum version dependencies. |
I had a script that tweaked the lock file to do just this (use lowest version of all specified deps), but it’s now busted because of the sha256 check fix in 1.1.10 Not that I’m suggesting that shouldn’t work as is, it’s just annoying that there’s not a way to ask poetry to do this at the moment. |
I just opened a PR to fix this kind of problem in Poetry itself... I was trying to figure out how you could test (and prevent) this kind of thing and having an option to install minimum versions feels like a really good answer. Searched to see if it existed and found this issue... |
This exact issue you created a PR for regarding urllib3 is one of the places I've seen this crop up a number of times. |
I went off of @JorgeGarciaIrazabal's comment and played with here if anyone is curious: https://github.com/davegaeddert/poetry/pull/1 Very rough stab and I'm sure there's all kinds of quirks that could come out of it. But, it was enough for me to poke around some of my own packages and do |
I've just cobbled together something for an internal library where, in CI tests, I first I appreciate that I could be more thorough about the testing, and that checking "top and bottom" is only one step better than what I was doing before, which is to only automate testing the top versions of everything. But my library has 10 dependencies, so it's not really feasible to run a test matrix over every combination of versions that I claim to support, and "top and bottom" feel like the places most likely to catch bugs. What I discovered, in case anyone is interested in the benefits of this feature, is that my declared minimum dependency versions passed my tests on Python 3.8, but core-dumped on Python 3.10. So that was good to know, and now I've bumped my dependency constraints for Python 3.10 to versions that actually claim to work on 3.10 (in their changelogs, I mean, as opposed to just according to some wildly optimistic |
I am not sure just 'minimum versions' covers all the use-cases. A current lock file might also not have the max version. If I can formulate it differently:
|
For my use case, I would want it to be the "minimum" in the same sense that If everyone in the world set all their constraints (including Python) to I accept that this doesn't provide a full testing strategy, and I certainly wouldn't object to a means of defining a testing matrix within the poetry config and allowing poetry to spit out a lock file for each point in the matrix. I wouldn't use that right away, though. I would use a minimum solve right away, because if my tests don't all pass on the lower-bound versions I declare for all my dependencies, then as the publisher of a library that makes me a lying liar. My goal is to avoid falsely claiming compatibility with old versions that my code doesn't really work with. |
Is there any update on this PR? I am developing a lib, and I would like to have the same behavior as well. To have all my libs pinned to the lower boundary in lock file, so I am always testing my lib in a most backward compatible way to my users. |
uv seems to have support for something like this: https://github.com/astral-sh/uv?tab=readme-ov-file#resolution-strategy uv is not comparable to Poetry, though. It is only an installer, not a development workflow tool. But now I guess we can say that there is "prior art" in this domain. |
The point is that I don't want to have another "better pip" in python. There is already WAY too many tools, yet none can have all the major use cases covered. If poetry declares that it supports both App and Lib development, then it should live up to its promises; not pointing to another lib A for case A and lib B for case B. I don't see that this is a healthy way of thinking how things should be done. And in terms of your argument, I think you are overthinking what the majority of lib devs want for this tool. Ofc it would be great that we can test against all the combos that the Lib wants to have. But in reality, many will be satiesfied that we have tested on the oldest version, knowing that most dependencies will keep to the SemVer convention. If I really want to have this combo testing strategy, yes, I will setup tox in addition to add onto this. But for a minimal approach, I would love to have a switch on Poetry to stick the lock to that. |
I assume we are open to such a feature in general. I'm not convinced about adding the flag to each of the commands ( Maybe, it's sufficient to start with a flag for Regarding lowest versions, I think there are two strategies that make sense:
My proposal would be So if someone wants to implement this feature I believe this description should lead to an implementation that will probably be accepted. |
@radoering Thanks. I agree with the "start small" approach. We can always expand the scope and converge on UX later. But I am happy to see there is something happening to cover this area. Thanks a lot for the great work Poetry team! Looking forward to see this getting release. |
Just a warning (of course ignore me if you like): if you define it now to overwrite the standard |
Yeah, I know. It makes sense to think about the target image. Unfortunately, I'm not sure about that. I just know, I don't really like the initial proposal to add Maybe, you are right and we should define the target image before and decide afterwards if it is a good idea to start with overwriting the default lockfile. Several questions come to my mind:
|
Adding my one datapoint - I mostly would not actively use both environments on my development machine, just like I mostly don't have separate python3.8/3.9/3.10/3.11/3.12 environments for every component I work on. It's in CI where I run all the permutations. I don't mind if CI commands are verbose because I only have to type them once, ever. Personally for library development I don't check in any lock files, and I believe that's common. But possibly for test reproducibility some people would want to be able to check in both lock files. They could still do that even if |
FYI, take a look at https://github.com/astral-sh/uv?tab=readme-ov-file#resolution-strategy for their resolution strategy |
Combine several points you have mentioned before, personally, I would not mind to have
For majority of the projects, they will either do app or lib, so one env is enough, they can follow one of the two commands for all lifecycles. For the rest: If I wish to have two different envs for two different resolution strategies, say one default env called I hope this workflow makes sense somehow. |
This patch adds the initial project structure, including licenses, linters and their configuration, and a basic test case that ensures that the module can be loaded. Most tooling is taken over from pynitrokey. One notable exception is that flit is replaced by poetry. This only affects developers, not end users. We recently made the same change in nitrokey-app2 [0], so I think it makes sense to use the same tooling here. [0] Nitrokey/nitrokey-app2#172 In the CI builds, we check that both the lockfile and the latest dependency versions work. It would good to also run it for the minimum versions specified in pyproject.toml, but this is currently not supported by poetry or pip: python-poetry/poetry#3527 pypa/pip#8085
This patch adds the initial project structure, including licenses, linters and their configuration, and a basic test case that ensures that the module can be loaded. Most tooling is taken over from pynitrokey. One notable exception is that flit is replaced by poetry. This only affects developers, not end users. We recently made the same change in nitrokey-app2 [0], so I think it makes sense to use the same tooling here. [0] Nitrokey/nitrokey-app2#172 In the CI builds, we check that both the lockfile and the latest dependency versions work. It would good to also run it for the minimum versions specified in pyproject.toml, but this is currently not supported by poetry or pip: python-poetry/poetry#3527 pypa/pip#8085
This patch adds the initial project structure, including licenses, linters and their configuration, and a basic test case that ensures that the module can be loaded. Most tooling is taken over from pynitrokey. One notable exception is that flit is replaced by poetry. This only affects developers, not end users. We recently made the same change in nitrokey-app2 [0], so I think it makes sense to use the same tooling here. [0] Nitrokey/nitrokey-app2#172 In the CI builds, we check that both the lockfile and the latest dependency versions work. It would good to also run it for the minimum versions specified in pyproject.toml, but this is currently not supported by poetry or pip: python-poetry/poetry#3527 pypa/pip#8085
This patch adds the initial project structure, including licenses, linters and their configuration, and a basic test case that ensures that the module can be loaded. Most tooling is taken over from pynitrokey. One notable exception is that flit is replaced by poetry. This only affects developers, not end users. We recently made the same change in nitrokey-app2 [0], so I think it makes sense to use the same tooling here. [0] Nitrokey/nitrokey-app2#172 In the CI builds, we check that both the lockfile and the latest dependency versions work. It would good to also run it for the minimum versions specified in pyproject.toml, but this is currently not supported by poetry or pip: python-poetry/poetry#3527 pypa/pip#8085
This patch adds the initial project structure, including licenses, linters and their configuration, and a basic test case that ensures that the module can be loaded. Most tooling is taken over from pynitrokey. One notable exception is that flit is replaced by poetry. This only affects developers, not end users. We recently made the same change in nitrokey-app2 [0], so I think it makes sense to use the same tooling here. [0] Nitrokey/nitrokey-app2#172 In the CI builds, we check that both the lockfile and the latest dependency versions work. It would good to also run it for the minimum versions specified in pyproject.toml, but this is currently not supported by poetry or pip: python-poetry/poetry#3527 pypa/pip#8085
Is there an update with this issue? If someone makes a PR, would this get merged in? @radoering I think it would be sufficient to add It may also be nice to add |
Nothing except for comments in this issue afaik.
It depends on what the PR implements. I do not think there is a clear target image yet.
Yes, I think everything more sophisticated than just writing into the same However, I think it might make more sense to introduce something like
You mean, only updating a specific package to the lowest version and keeping the rest? Yes, that might make sense. In sum, I think adding a flag like |
Also, calling it |
Feature Request
Context
When I am implementing a library, it would be super useful to run my tests with all the supported dependencies defined with the constrains in pyproject.toml. This will prevent errors in production when, for example, I use some new features of a dependency and forget to update the dependencies constrains (this is hard to identify when you are coding).
Because running the tests with all the possible combinations of all your supported libraries is virtually impossible, maybe running them with the lowest versions solvable solution and also the highest (I think this is the default behavior)
Proposal
--use-lowest-versions
to the cli entrypoints (install
,sync
,lock
,shell
, andrun
)poetry.lowest.lock
lowest
after the project name like this:{project_name}-lowest-{id}-{python-version}
Main simple use case
In ci do the following steps
Note: I would appreciate if anyone has a better name for the flag (naming is the hardest part :P)
Also, I looked a the code and I think I know where add this logic, I can create a PR if you like the idea
The text was updated successfully, but these errors were encountered: