Excluding packages when extra is specified
See original GitHub issueI have found a dependency resolving issue in PIP, here is how to reproduce. Nor the “old” or the “2020-resolver” is able to fix the issue. However they both fails in different ways.
This is a very problematic for us. When do you think it could be resolved ?
Environment:
pip list
Package Version
---------- -------
pip 20.2
setuptools 49.2.0
wheel 0.34.2
Script to reproduce
#!/usr/bin/env python
"""The setup script."""
from setuptools import setup
# We ask to install is-sorted by pinned version (points to 0.0.1: https://pypi.org/project/is-sorted/0.0.1/)
install_requires = ["is-sorted==0.0.1; extra != 'env_A' and extra != 'env_B'"]
extras_require = {
"env_a": ["random-string"], # a random package
"env_b": ["get-random"], # a random package
}
# We ask to install is-sorted by range (currently points to 0.0.2: https://pypi.org/project/is-sorted/0.0.2/)
extras_require = {key: val + ["is-sorted>=0.0.1,<0.1"] for key, val in extras_require.items()}
setup(
name='test_pkg',
version='1.0.0',
install_requires=install_requires,
extras_require=extras_require,
)
What shall be expected
- if installed without any extra require environment, only installs:
is-sorted==0.0.1
- if installed with extra require environment
env_a
:random-string
is-sorted>=0.0.1,<0.1
- if installed with extra require environment
env_b
:get-random
is-sorted>=0.0.1,<0.1
- if installed with extra require environments
env_a && env_b
:random-string
# fromenv_a
get-random
# fromenv_b
is-sorted>=0.0.1,<0.1
What happens in practice ?
0. Let’s build the wheel: python setup.py bdist_wheel
$ python setup.py bdist_wheel
running bdist_wheel
running build
installing to build/bdist.linux-x86_64/wheel
running install
running install_egg_info
running egg_info
writing test_pkg.egg-info/PKG-INFO
writing dependency_links to test_pkg.egg-info/dependency_links.txt
writing requirements to test_pkg.egg-info/requires.txt
writing top-level names to test_pkg.egg-info/top_level.txt
reading manifest file 'test_pkg.egg-info/SOURCES.txt'
writing manifest file 'test_pkg.egg-info/SOURCES.txt'
Copying test_pkg.egg-info to build/bdist.linux-x86_64/wheel/test_pkg-1.0.0-py3.6.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/test_pkg-1.0.0.dist-info/WHEEL
creating 'dist/test_pkg-1.0.0-py3-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it
adding 'test_pkg-1.0.0.dist-info/METADATA'
adding 'test_pkg-1.0.0.dist-info/WHEEL'
adding 'test_pkg-1.0.0.dist-info/top_level.txt'
adding 'test_pkg-1.0.0.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel
If we look at the file METADATA
, everything seems OK:
Requires-Dist: is-sorted (==0.0.1) ; extra != "env_a" and extra != "env_b"
Provides-Extra: env_a
Requires-Dist: random-string ; extra == 'env_a'
Requires-Dist: is-sorted (<0.1,>=0.0.1) ; extra == 'env_a'
Provides-Extra: env_b
Requires-Dist: get-random ; extra == 'env_b'
Requires-Dist: is-sorted (<0.1,>=0.0.1) ; extra == 'env_b'
1. Install with NO extra require
$ pip install "dist/test_pkg-1.0.0-py3-none-any.whl"
Processing ./dist/test_pkg-1.0.0-py3-none-any.whl
Collecting is-sorted==0.0.1; extra != "env_a" and extra != "env_b"
Downloading is_sorted-0.0.1-py3-none-any.whl (2.8 kB)
Installing collected packages: is-sorted, test-pkg
Successfully installed is-sorted-0.0.1 test-pkg-1.0.0
Conclusion: Everything is good.
2. Install with env_a
extra require
$ pip install "dist/test_pkg-1.0.0-py3-none-any.whl[env_a]"
Processing ./dist/test_pkg-1.0.0-py3-none-any.whl
Ignoring is-sorted: markers 'extra != "env_a" and extra != "env_b"' don't match your environment
Collecting is-sorted<0.1,>=0.0.1; extra == "env_a"
Downloading is_sorted-0.0.2-py3-none-any.whl (3.3 kB)
Collecting random-string; extra == "env_a"
Downloading random-string-1.00.tar.gz (996 bytes)
Building wheels for collected packages: random-string
Building wheel for random-string (setup.py) ... done
Created wheel for random-string: filename=random_string-1.0-py3-none-any.whl size=1897 sha256=14603b692b5977dfe8f4bc8d4c3ea54a796b5b4c86760c0bb97b21f3f1aaf2cb
Stored in directory: /root/.cache/pip/wheels/47/2d/fe/13b8d8df913e51efdd1c8a66632a2dd0b54abb101c2c9ac399
Successfully built random-string
Installing collected packages: is-sorted, random-string, test-pkg
ERROR: After October 2020 you may experience errors when installing or updating packages. This is because pip will change the way that it resolves dependency conflicts.
We recommend you use --use-feature=2020-resolver to test your packages with the new resolver before it becomes the default.
test-pkg 1.0.0 requires is-sorted==0.0.1; extra != "env_a" and extra != "env_b", but you'll have is-sorted 0.0.2 which is incompatible.
Successfully installed is-sorted-0.0.2 random-string-1.0 test-pkg-1.0.0
Conclusion: A scary “red” warning for no reason: is-sorted-0.0.2 random-string-1.0 test-pkg-1.0.0
shall be the correct set of dependencies
3. Install with env_b
extra require
$ pip install "dist/test_pkg-1.0.0-py3-none-any.whl[env_b]"
Processing ./dist/test_pkg-1.0.0-py3-none-any.whl
Ignoring is-sorted: markers 'extra != "env_a" and extra != "env_b"' don't match your environment
Collecting get-random; extra == "env_b"
Downloading get_random-0.1.4.tar.gz (2.5 kB)
Collecting is-sorted<0.1,>=0.0.1; extra == "env_b"
Downloading is_sorted-0.0.2-py3-none-any.whl (3.3 kB)
Building wheels for collected packages: get-random
Building wheel for get-random (setup.py) ... done
Created wheel for get-random: filename=get_random-0.1.4-py3-none-any.whl size=2516 sha256=5fdc7d9551dcf4e377d839c938865dc5053745d1e67aaad9cfd418fa67e28848
Stored in directory: /root/.cache/pip/wheels/ec/01/58/0ee893b104a22036186016ffaf29e1aca6a343c11341677a4c
Successfully built get-random
Installing collected packages: get-random, is-sorted, test-pkg
ERROR: After October 2020 you may experience errors when installing or updating packages. This is because pip will change the way that it resolves dependency conflicts.
We recommend you use --use-feature=2020-resolver to test your packages with the new resolver before it becomes the default.
test-pkg 1.0.0 requires is-sorted==0.0.1; extra != "env_a" and extra != "env_b", but you'll have is-sorted 0.0.2 which is incompatible.
Successfully installed get-random-0.1.4 is-sorted-0.0.2 test-pkg-1.0.0
Conclusion: A scary “red” warning for no reason: get-random-0.1.4 is-sorted-0.0.2 test-pkg-1.0.0
shall be the correct set of dependencies
4. Install with env_a && env_b
extra require
$ pip install "dist/test_pkg-1.0.0-py3-none-any.whl[env_a,env_b]"
Processing ./dist/test_pkg-1.0.0-py3-none-any.whl
Ignoring is-sorted: markers 'extra != "env_a" and extra != "env_b"' don't match your environment
Collecting is-sorted<0.1,>=0.0.1; extra == "env_a"
Downloading is_sorted-0.0.2-py3-none-any.whl (3.3 kB)
Collecting random-string; extra == "env_a"
Downloading random-string-1.00.tar.gz (996 bytes)
Collecting get-random; extra == "env_b"
Downloading get_random-0.1.4.tar.gz (2.5 kB)
Building wheels for collected packages: random-string, get-random
Building wheel for random-string (setup.py) ... done
Created wheel for random-string: filename=random_string-1.0-py3-none-any.whl size=1897 sha256=6a38795c861b315d94332cc3b381c7441fc83c54dd8a4db8b34cd43f638dca33
Stored in directory: /root/.cache/pip/wheels/47/2d/fe/13b8d8df913e51efdd1c8a66632a2dd0b54abb101c2c9ac399
Building wheel for get-random (setup.py) ... done
Created wheel for get-random: filename=get_random-0.1.4-py3-none-any.whl size=2516 sha256=aa4677747ad67d64a16789e1611e3ca781800d380b8798bde2466aa6f005e4ba
Stored in directory: /root/.cache/pip/wheels/ec/01/58/0ee893b104a22036186016ffaf29e1aca6a343c11341677a4c
Successfully built random-string get-random
Installing collected packages: is-sorted, random-string, get-random, test-pkg
ERROR: After October 2020 you may experience errors when installing or updating packages. This is because pip will change the way that it resolves dependency conflicts.
We recommend you use --use-feature=2020-resolver to test your packages with the new resolver before it becomes the default.
test-pkg 1.0.0 requires is-sorted==0.0.1; extra != "env_a" and extra != "env_b", but you'll have is-sorted 0.0.2 which is incompatible.
Successfully installed get-random-0.1.4 is-sorted-0.0.2 random-string-1.0 test-pkg-1.0.0
Conclusion: A scary “red” warning for no reason: get-random-0.1.4 is-sorted-0.0.2 random-string-1.0 test-pkg-1.0.0
shall be the correct set of dependencies
How about with --use-feature=2020-resolver
?
Now it’s even worse, we don’t have a warning anymore because the dependency resolving is wrong.
1. Install with NO extra require and --use-feature=2020-resolver
$ pip install "dist/test_pkg-1.0.0-py3-none-any.whl" --use-feature=2020-resolver
Processing ./dist/test_pkg-1.0.0-py3-none-any.whl
Collecting is-sorted==0.0.1
Downloading is_sorted-0.0.1-py3-none-any.whl (2.8 kB)
Installing collected packages: is-sorted, test-pkg
Successfully installed is-sorted-0.0.1 test-pkg-1.0.0
Conclusion: Everything is good.
2. Install with env_a
extra require and --use-feature=2020-resolver
$ pip install "dist/test_pkg-1.0.0-py3-none-any.whl[env_a]" --use-feature=2020-resolver
Processing ./dist/test_pkg-1.0.0-py3-none-any.whl
Ignoring is-sorted: markers 'extra != "env_a" and extra != "env_b"' don't match your environment
Collecting random-string
Downloading random-string-1.00.tar.gz (996 bytes)
Collecting is-sorted<0.1,>=0.0.1
Downloading is_sorted-0.0.1-py3-none-any.whl (2.8 kB)
Building wheels for collected packages: random-string
Building wheel for random-string (setup.py) ... done
Created wheel for random-string: filename=random_string-1.0-py3-none-any.whl size=1897 sha256=f9032cea18e5f1c5af61ad3041c16722150c1360aee6ac34c7134e03c30577be
Stored in directory: /root/.cache/pip/wheels/47/2d/fe/13b8d8df913e51efdd1c8a66632a2dd0b54abb101c2c9ac399
Successfully built random-string
Installing collected packages: is-sorted, test-pkg, random-string
Successfully installed is-sorted-0.0.1 random-string-1.0 test-pkg-1.0.0
Conclusion: No error anymore, however it installs is-sorted-0.0.1
and should install: is-sorted-0.0.2
3. Install with env_b
extra require and --use-feature=2020-resolver
$ pip install "dist/test_pkg-1.0.0-py3-none-any.whl[env_b]" --use-feature=2020-resolver
Processing ./dist/test_pkg-1.0.0-py3-none-any.whl
Ignoring is-sorted: markers 'extra != "env_a" and extra != "env_b"' don't match your environment
Collecting is-sorted<0.1,>=0.0.1
Downloading is_sorted-0.0.1-py3-none-any.whl (2.8 kB)
Collecting get-random
Downloading get_random-0.1.4.tar.gz (2.5 kB)
Building wheels for collected packages: get-random
Building wheel for get-random (setup.py) ... done
Created wheel for get-random: filename=get_random-0.1.4-py3-none-any.whl size=2516 sha256=abca081d35ed6ae4bb11f1f36da294b25bad3b0dd7bb3293bc6912ec2e54f81c
Stored in directory: /root/.cache/pip/wheels/ec/01/58/0ee893b104a22036186016ffaf29e1aca6a343c11341677a4c
Successfully built get-random
Installing collected packages: is-sorted, test-pkg, get-random
Successfully installed get-random-0.1.4 is-sorted-0.0.1 test-pkg-1.0.0
Conclusion: No error anymore, however it installs is-sorted-0.0.1
and should install: is-sorted-0.0.2
4. Install with env_a && env_b
extra require and --use-feature=2020-resolver
:
$ pip install "dist/test_pkg-1.0.0-py3-none-any.whl[env_a,env_b]" --use-feature=2020-resolver
Processing ./dist/test_pkg-1.0.0-py3-none-any.whl
Ignoring is-sorted: markers 'extra != "env_a" and extra != "env_b"' don't match your environment
Collecting random-string
Downloading random-string-1.00.tar.gz (996 bytes)
Collecting is-sorted<0.1,>=0.0.1
Downloading is_sorted-0.0.1-py3-none-any.whl (2.8 kB)
Collecting get-random
Downloading get_random-0.1.4.tar.gz (2.5 kB)
Building wheels for collected packages: random-string, get-random
Building wheel for random-string (setup.py) ... done
Created wheel for random-string: filename=random_string-1.0-py3-none-any.whl size=1897 sha256=6ba41eea0fce2aa432afc47ad7bd5e4aa89c5e1992c0a7dacfb27c3885838dbb
Stored in directory: /root/.cache/pip/wheels/47/2d/fe/13b8d8df913e51efdd1c8a66632a2dd0b54abb101c2c9ac399
Building wheel for get-random (setup.py) ... done
Created wheel for get-random: filename=get_random-0.1.4-py3-none-any.whl size=2516 sha256=2eacde1bfa69a76ad369ea1e085e812c9302e2bd866c8882f148f772948faf50
Stored in directory: /root/.cache/pip/wheels/ec/01/58/0ee893b104a22036186016ffaf29e1aca6a343c11341677a4c
Successfully built random-string get-random
Installing collected packages: is-sorted, test-pkg, random-string, get-random
Successfully installed get-random-0.1.4 is-sorted-0.0.1 random-string-1.0 test-pkg-1.0.0
Conclusion: No error anymore, however it installs is-sorted-0.0.1
and should install: is-sorted-0.0.2
Issue Analytics
- State:
- Created 3 years ago
- Comments:26 (14 by maintainers)
I think the misunderstanding is the communication channel is not as well-known as I anticipated, sorry. PyPA does have multiple communication channels, as listed on pypa.io. The Discourse forum is the most active these days, but feel free to choose any one you feel the most comfortable with.
Unfortunately this is not pip bug, but a design issue. The reason pip does not do what you want is not because pip interprets
extra != "env_a"
incorrectly, but that expression does not mean what you think it does (if it does, pip would have done what you want).When you install a package with extras, say
foo[a,b]
, you are actually requesting extras, not an extra singular. The way this is broken down, as specified in PEP 508, is to break a package’s dependencies into parts: those included when no extras are specified, those included only if you want thea
extra, and those included only if you want theb
extra. Whenfoo[a,b]
is requested, all three are combined together.Now if you extend the concept to when only one extra is requested, say
foo[a]
, you are still not requesting one extra, but a list of extras of length 1, and the package’s dependencies are broken into two: those included when no extras are specified, and those only if you want thea
extra. With this design,bar; extra != 'a'
goes into the former group—because when no extras are requested, the expression evaluates to True. So when those parts are installed,bar
will still end up in the environment.I hope this clears up why the current PEP 508 markers is confusing in this aspect and why pip seems to interpret it wrong. Again, pip actually interprets it correctly as designed; its the design that gives
extra != value
a semantic meaning that you do not expect (and that semantic meaning means there’s currently no way to achieve what you want). So this should not be fixed by pip because pip should not deviate from the standard; you need to fix the standard.