Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Exclude certain dependency version ranges in setuptools/pip

Currently the Django Project supports 1.4, 1.7 and 1.8. In my setup.py I want to reflect these versions as being supported.

install_requires=['Django>=1.4.2,<1.8.99,!=1.5,!=1.6']

However this still allows 1.5.x and 1.6.x releases. How can I exclude a complete range?

Setuptools lists the following valid requirements as an example:

PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1

However this doesn't work with pip (it should at least match 1.4.x / 1.5.x):

No matching distribution found for PickyThing!=1.9.6,<1.6,<2.0a0,==2.4c1,>1.9

Update with example
For example; I only want to include currently supported versions of Django. This would be 1.4.x, 1.7.x and 1.8.x. So I would write;

#setup.py
install_requires=['Django>=1.4.2,<1.4.99,>=1.7,<1.8.99']

However if I run pip install -e . on this project, it fails with;

Collecting Django<1.4.99,<1.8.99,>=1.4.2,>=1.7 (from ...)
Could not find a version that satisfies the requirement Django<1.4.99,<1.8.99,>=1.4.2,>=1.7 (from django-two-factor-auth==1.2.0) (from versions: 1.1.3, 1.1.4, 1.2, 1.2.1, 1.2.2, 1.2.3, 1.2.4, 1.2.5, 1.2.6, 1.2.7, 1.3, 1.3.1, 1.3.2, 1.3.3, 1.3.4, 1.3.5, 1.3.6, 1.3.7, 1.4, 1.4.1, 1.4.2, 1.4.3, 1.4.4, 1.4.5, 1.4.6, 1.4.7, 1.4.8, 1.4.9, 1.4.10, 1.4.11, 1.4.12, 1.4.13, 1.4.14, 1.4.15, 1.4.16, 1.4.17, 1.4.18, 1.4.19, 1.4.20, 1.5, 1.5.1, 1.5.2, 1.5.3, 1.5.4, 1.5.5, 1.5.6, 1.5.7, 1.5.8, 1.5.9, 1.5.10, 1.5.11, 1.5.12, 1.6, 1.6.1, 1.6.2, 1.6.3, 1.6.4, 1.6.5, 1.6.6, 1.6.7, 1.6.8, 1.6.9, 1.6.10, 1.6.11, 1.7, 1.7.1, 1.7.2, 1.7.3, 1.7.4, 1.7.5, 1.7.6, 1.7.7, 1.7.8, 1.8a1, 1.8b1, 1.8b2, 1.8rc1, 1.8, 1.8.1)
No matching distribution found for Django<1.4.99,<1.8.99,>=1.4.2,>=1.7 (from ...)

It is obvious that a version number 1.4.20 cannot satisfy >=1.7 and 1.8.1 cannot satisfy <1.4.99. However the documentation from Setuptools (see above) does suggest that something along these lines should be possible. However, this is non-obvious to me.

like image 593
Bouke Avatar asked Apr 07 '15 19:04

Bouke


People also ask

How do I install specific versions on setuptools?

To use a specific version of setuptools it is necessary to have it in both locations - in pyproject. toml and at the beginning of install_requires of setup.py. The tool like pip will use the version from pyproject. toml to build the project.

Does pip depend on setuptools?

In Fedora, our pip package Recommends setuptools. Practically that means: Majority of users who install pip will get setuptools by default. Users can explicitly uninstall setuptools after installing pip or exclude setuptools when installing pip.

What is Setup_requires in setup py?

The items listed in setup_requires get implicitly installed whenever you execute the setup.py but one of the common ways that the setup.py is executed is via another tool, such as pip , who is already managing dependencies.


2 Answers

You can use

Django>=1.4.2,<1.9,!=1.5.*,!=1.6.*

This is defined inside PEP440.

You can test this behavior with the packaging module that is vendored inside the last versions of setuptools and pip.

In [1]: from packaging import specifiers

In [2]: sp=specifiers.SpecifierSet(">=1.4.2,<1.9,!=1.5.*,!=1.6.*")

In [3]: sp.contains("1.4.2")
Out[3]: True

In [4]: sp.contains("1.6.4")
Out[4]: False

In [5]: sp.contains("1.8.2")
Out[5]: True
like image 168
xav.fernandez Avatar answered Oct 11 '22 00:10

xav.fernandez


I've read some related code of pkg_resources. I think the document here is not accurate. Not only pip fails to find the right package version, python setup.py install, which actually uses setuptools, also fails.

Some of the related code:

pip/_vendor/packaging/specifiers.py

# If we have any specifiers, then we want to wrap our iterable in the
# filter method for each one, this will act as a logical AND amongst
# each specifier.
if self._specs:
    for spec in self._specs:
        iterable = spec.filter(iterable, prereleases=prereleases)
    return iterable

You can see that in the comment, the author emphasized that this will cause an AND amongst each specifier, not OR. So if you do this:

PickyThing<1.6,>1.9,!=1.9.6,<2.0a0,==2.4c1

You will get nothing!

I tried with this code below:

import pkg_resources

a = ['1.4', '1.8', '1.9.2']
d = pkg_resources.Requirement.parse('PickyThing<1.6,>1.9,!=1.9.6')
r = d.specifier.filter(a)

print(list(r)) # Nothing, just an empty list []

You may want to file a bug to pip so they can fix it.

like image 43
skyline75489 Avatar answered Oct 10 '22 23:10

skyline75489