-
-
Notifications
You must be signed in to change notification settings - Fork 611
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
Merge constraint contents for different envs #826
Comments
@atugushev thanks. I usually have |
@webknjaz does the #826 (comment) resolved the issue? |
No, it's not what I was asking about. |
I see. Unfortunately, it's not possible (at the moment). Currently, pip-tools compiles requirements, evaluates markers and outputs results without ones. The official stance of pip-tools regarding cross-environment usage is that But anyways, "merging markers" doesn't seem so impossible. At least |
@webknjaz With the new I'm not sure what the best solutions and workflows might be, but here's something I played around with. Maybe it can give you some ideas, and then you can give back some ideas!
#!/bin/dash -e
# platform-compile.sh [--base <basetxt>] [<reqsin>] [<pip-compile-arg>...]
python_version="$(python -c 'from __future__ import print_function; import platform; print(*platform.python_version_tuple()[:2], sep=".")')"
sys_platform="$(python -c 'from __future__ import print_function; import sys; print(sys.platform)')"
machine="$(python -c 'from __future__ import print_function; import platform; print(platform.machine())')"
if [ "$1" = '--base' ]; then
base="$2"
shift 2
else
unset base
fi
if [ -r "$1" ]; then
reqsin="$1"
shift
else
reqsin="requirements.in"
fi
txt="py${python_version}-${sys_platform}-${machine}-$(printf '%s' "${reqsin}" | sed 's/\.in$//').txt"
markers="; python_version ~= '${python_version}' and sys_platform == '${sys_platform}' and platform_machine == '${machine}'"
if [ "$base" ] && [ "$base" != "$txt" ]; then
cp "$base" "$txt"
fi
pip-compile --no-header "$reqsin" -o "$txt" "$@"
sed -i -E "s/(^[^;]+==[^;#]+)(#|$)/\1${markers} \2/g" "$txt" Then let's say we've got this
Running $ ./platform-compile.sh on my system in a py3 env will generate certifi==2020.4.5.1 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via httpx
chardet==3.0.4 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via httpx
h11==0.9.0 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via httpx
h2==3.2.0 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via httpx
hpack==3.0.0 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via h2
hstspreload==2020.4.14 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via httpx
httpx==0.12.1 ; python_version >= "3" # via -r requirements.in
hyperframe==5.2.0 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via h2
idna==2.9 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via httpx
rfc3986==1.4.0 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via httpx
sniffio==1.1.0 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via httpx
urllib3==1.25.9 ; python_version ~= '3.8' and sys_platform == 'linux' and platform_machine == 'x86_64' # via httpx Doing so in a py2 env will generate pathlib==1.0.1 ; python_version < "3" # via -r requirements.in Then I can have a manually created (though potentially scripted)
The result is that whether I'm in a py2 or py3 env, I can use either $ pip install -r requirements.txt or $ pip-sync requirements.txt to get up and running. So with something like this, you could also have a |
I imagine the 'real' solution to multi-environment locking to be a two step process. First, run in each environment to collect a bunch of info required for locking and store it to a file or files. Two, have a single run (that doesn't care what env it is in) that does the resolving using the sum of all collected info. The point here is to create a common set of requirements for all envs if at all possible. Processing each environment separately could easily result in This would apply equally I think to environment variance across Python version, interpreter, OS, and even 'groups'/'layers'. The last being used in my case for 'normal installation' vs. 'testing' vs. 'development' for example (kind of like 'extras' but not quite). So, I'm confident this would be a lot of work regardless but... does that sort of two-stage situation already exist in pip-tools even if not available presently as completely separate steps? |
Oh, for completeness, the entirely undocumented http://github.com/altendky/boots is what I use as my front end to achieve 1) multiple 'groups' and 2) multiple operating systems (via http://github.com/altendky/romp). https://github.com/altendky/pm/tree/master/requirements is an example using those. |
I think that could be achieved by modifying my hack above to treat one of the platforms as the default/standard/boss, and having the others |
Actually, no, it would probably ignore those "constraints" since the markers wouldn't match. Whoops. |
The intermediate data needs to describe acceptable ranges of versions, not pinned versions. |
Can you speak more about the intermediate data here? Another hack that actually does work for that would be to copy the "default" env's EDIT: I've updated the above |
@altendky EDIT: I think I understand what you mean about the "intermediate data" now -- you want full smart resolver behavior to account for all the |
I... thought I responded to your request for more details, but apparently I never submitted it. :[ Sorry. Yeah, by all means not a trivial task to actually implement all this. The key though seems to be in separating the collecting of requirements (x needs y between v1 and v3, y needs z after v4, etc) and the resolution of them. That way you can collect from multiple places (Python versions, platforms, etc) and then merge all that together in one Python process to resolve. While I 'understand' (am understanding of?) the difficulty both in implementing this and the extra resources needed by the user (access to all relevant platforms) it does strike me that among all the strong cross platform nature of Python and the community that locking is one thing that doesn't seem to be even remotely multi-platform ready. |
... parametrized by python version. We have a bunch of dependencies that are only needed on specific python versions (e.g. backports like dataclasses, importlib_resources, etc). We use conditional environment markers for those in the top-level requirements.in files. pip-compile does not yet support generating a combined requirements.txt file where differences across python versions/platforms/archs are reconciled using environmet markers: jazzband/pip-tools#826 They currently recommend running pip-compile on each targeted python environment generating as many concrete requirements.txt files jazzband/pip-tools#651 So this is what I have done here.
... containing all top-level (*.in) and concrete requirements.txt, the latter parametrized by python version. We have a bunch of dependencies that are only needed on specific python versions (e.g. backports like dataclasses, importlib_resources, etc). We use conditional environment markers for those in the top-level requirements.in files. pip-compile does not yet support generating a combined requirements.txt file where differences across python versions/platforms/archs are reconciled using environmet markers: jazzband/pip-tools#826 They currently recommend running pip-compile on each targeted python environment generating as many concrete requirements.txt files jazzband/pip-tools#651 So this is what I have done here.
... containing all top-level (*.in) and concrete requirements.txt, the latter parametrized by python version. We have a bunch of dependencies that are only needed on specific python versions (e.g. backports like dataclasses, importlib_resources, etc). We use conditional environment markers for those in the top-level requirements.in files. pip-compile does not yet support generating a combined requirements.txt file where differences across python versions/platforms/archs are reconciled using environmet markers: jazzband/pip-tools#826 They currently recommend running pip-compile on each targeted python environment generating as many concrete requirements.txt files jazzband/pip-tools#651 So this is what I have done here.
FWIW, we had a similar situation (one dependency, Until a real solution to this problem is found, I built a work-around using the following approach, though it only works if the platform-specific dependencies are fairly self-contained (little to no transitive deps), don't change often (as you'll see, we effectively pin their version instead of updating them using The trick is as follows:
The resulting To see what this looks like code-wise, see scality/metalk8s@f1f5f9c |
@AndydeCleyre I've been experimenting with an idea similar to yours lately and got a PoC with tox + per-env constraints files. But only the installation part, not matrix generation for which I'll look into how I can use GHA. As a result, I no longer think that merging constraints is the best idea. Linters constraints should not influence how the docs are built or how the tests are run. Also, limitations of the wheels available for macOS shouldn't influence the deps on Windows. Python 2 deps shouldn't necessarily define how Python 3-only bits are tested. And so on. |
There is value in some cases to have cross-talk between the envs. There's something nice about having the same version of library X everywhere. For some use cases... |
Agreed, it's usecase-dependent. I've figured out that my use case is probably different. Or at least, some of my envs need to be separate. |
Maybe we should close either #1326 or this one? I think they're getting at the same questions. |
So I now have all the peaces in my PoC. It's not generally reusable yet but feel free to lurk into these bits of automation:
I hope to make these more generic and composable one day, but for now, it's a nice and working demo, I've been using since Jan 4, 2022. |
@webknjaz I would really want to see a combined constraints file especially because I find the splitting into different files for each python/os/architecture a colossal maintenance PITA. Still, I don't know when pip-tools will be able to deal with this. I wonder if we could at least add support for most basic type of conditions, when we have something like:
IMHO, over 19/20 conditions are just like this, only looking for python_version, nothing else. |
I think some of the folks subscribed to this issue would also like to follow #2124. FYI. |
UPD (Nov 30, 2023): I changed my mind, having understood the consequences and having gained a better understanding of the challenges this would present:
Known closed and/or duplicate issues that may have some unique context/conversations:
--generate-hashes
,-c constraints.txt
,--resolver=backtracking
and--strip-extras
doesn't currently work #1752platform_system
not correctly respected #1793I want to run pip-compile against the same set of requirements files under different envs (Python interpreters) and get a combined output.Currently, it doesn't seem possible. Am I overlooking something?
Environment Versions
N/A
Steps to replicate
N/A
Expected result
resuling file is extended with extra entriesActual result
resulting file is overridden
The text was updated successfully, but these errors were encountered: