mirror of https://github.com/pypa/pip
Merge pull request #8440 from uranusjr/yaml-tests
This commit is contained in:
commit
8c3447e85e
|
@ -4,26 +4,13 @@ Tests for the resolver
|
|||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
import yaml
|
||||
|
||||
from tests.lib import DATA_DIR, create_basic_wheel_for_package, path_to_url
|
||||
|
||||
_conflict_finder_pat = re.compile(
|
||||
# Conflicting Requirements: \
|
||||
# A 1.0.0 requires B == 2.0.0, C 1.0.0 requires B == 1.0.0.
|
||||
r"""
|
||||
(?P<package>[\w\-_]+?)
|
||||
[ ]
|
||||
(?P<version>\S+?)
|
||||
[ ]requires[ ]
|
||||
(?P<selector>.+?)
|
||||
(?=,|\.$)
|
||||
""",
|
||||
re.X
|
||||
)
|
||||
|
||||
|
||||
def generate_yaml_tests(directory):
|
||||
"""
|
||||
|
@ -137,6 +124,28 @@ def handle_request(script, action, requirement, options, new_resolver=False):
|
|||
return {"result": result, "state": sorted(state)}
|
||||
|
||||
|
||||
def check_error(error, result):
|
||||
return_code = error.get('code')
|
||||
if return_code:
|
||||
assert result.returncode == return_code
|
||||
|
||||
stderr = error.get('stderr')
|
||||
if not stderr:
|
||||
return
|
||||
|
||||
if isinstance(stderr, str):
|
||||
patters = [stderr]
|
||||
elif isinstance(stderr, list):
|
||||
patters = stderr
|
||||
else:
|
||||
raise "string or list expected, found %r" % stderr
|
||||
|
||||
for patter in patters:
|
||||
match = re.search(patter, result.stderr, re.I)
|
||||
assert match, 'regex %r not found in stderr: %r' % (
|
||||
stderr, result.stderr)
|
||||
|
||||
|
||||
@pytest.mark.yaml
|
||||
@pytest.mark.parametrize(
|
||||
"case", generate_yaml_tests(DATA_DIR.parent / "yaml"), ids=id_func
|
||||
|
@ -175,24 +184,20 @@ def test_yaml_based(script, case):
|
|||
request[action],
|
||||
request.get('options', '').split(),
|
||||
case[':resolver:'] == 'new')
|
||||
result = effect['result']
|
||||
|
||||
if 0: # for analyzing output easier
|
||||
with open(DATA_DIR.parent / "yaml" /
|
||||
case[':name:'].replace('*', '-'), 'w') as fo:
|
||||
result = effect['result']
|
||||
fo.write("=== RETURNCODE = %d\n" % result.returncode)
|
||||
fo.write("=== STDERR ===:\n%s\n" % result.stderr)
|
||||
|
||||
if 'state' in response:
|
||||
assert effect['state'] == (response['state'] or []), \
|
||||
str(effect["result"])
|
||||
assert effect['state'] == (response['state'] or []), str(result)
|
||||
|
||||
error = False
|
||||
if 'conflicting' in response:
|
||||
error = True
|
||||
|
||||
if error:
|
||||
if case[":resolver:"] == 'old':
|
||||
assert effect["result"].returncode == 0, str(effect["result"])
|
||||
elif case[":resolver:"] == 'new':
|
||||
assert effect["result"].returncode == 1, str(effect["result"])
|
||||
error = response.get('error')
|
||||
if error and case[":resolver:"] == 'new' and sys.platform != 'win32':
|
||||
# Note: we currently skip running these tests on Windows, as they
|
||||
# were failing due to different error codes. There should not
|
||||
# be a reason for not running these this check on Windows.
|
||||
check_error(error, result)
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# New resolver error messages
|
||||
|
||||
|
||||
## Incompatible requirements
|
||||
|
||||
Most resolver error messages are due to incompatible requirements.
|
||||
That is, the dependency tree contains conflicting versions of the same
|
||||
package. Take the example:
|
||||
|
||||
base:
|
||||
available:
|
||||
- A 1.0.0; depends B == 1.0.0, C == 2.0.0
|
||||
- B 1.0.0; depends C == 1.0.0
|
||||
- C 1.0.0
|
||||
- C 2.0.0
|
||||
|
||||
Here, `A` cannot be installed because it depends on `B` (which depends on
|
||||
a different version of `C` than `A` itself. In real world examples, the
|
||||
conflicting version are not so easy to spot. I'm suggesting an error
|
||||
message which looks something like this:
|
||||
|
||||
A 1.0.0 -> B 1.0.0 -> C 1.0.0
|
||||
A 1.0.0 -> C 2.0.0
|
||||
|
||||
That is, for the conflicting package, we show the user where exactly the
|
||||
requirement came from.
|
||||
|
||||
|
||||
## Double requirement
|
||||
|
||||
I've noticed that in many cases the old resolver messages are more
|
||||
informative. For example, in the simple example:
|
||||
|
||||
base:
|
||||
available:
|
||||
- B 1.0.0
|
||||
- B 2.0.0
|
||||
|
||||
Now if we want to install both version of `B` at the same time,
|
||||
i.e. the requirement `B==1.0.0 B==2.0.0`, we get:
|
||||
|
||||
ERROR: Could not find a version that satisfies the requirement B==1.0.0
|
||||
ERROR: Could not find a version that satisfies the requirement B==2.0.0
|
||||
No matching distribution found for b, b
|
||||
|
||||
Even though both version are actually available and satisfy each requirement,
|
||||
just not at once. When trying to install a version of `B` which does not
|
||||
exist, say requirement `B==1.5.0`, you get the same type of error message:
|
||||
|
||||
Could not find a version that satisfies the requirement B==1.5.0
|
||||
No matching distribution found for b
|
||||
|
||||
For this case, the old error message was:
|
||||
|
||||
Could not find a version that satisfies the requirement B==1.5.0 (from versions: 1.0.0, 2.0.0)
|
||||
No matching distribution found for B==1.5.0
|
||||
|
||||
And the old error message for the requirement `B==1.0.0 B==2.0.0`:
|
||||
|
||||
Double requirement given: B==2.0.0 (already in B==1.0.0, name='B')
|
|
@ -1,11 +1,54 @@
|
|||
# Fixtures
|
||||
# YAML tests for pip's resolver
|
||||
|
||||
This directory contains fixtures for testing pip's resolver.
|
||||
The fixtures are written as `.yml` files, with a convenient format
|
||||
that allows for specifying a custom index for temporary use.
|
||||
|
||||
The `.yml` files are organized in the following way. A `base` section
|
||||
which ...
|
||||
The `.yml` files are typically organized in the following way. Here, we are
|
||||
going to take a closer look at the `simple.yml` file and step through the
|
||||
test cases. A `base` section defines which packages are available upstream:
|
||||
|
||||
base:
|
||||
available:
|
||||
- simple 0.1.0
|
||||
- simple 0.2.0
|
||||
- base 0.1.0; depends dep
|
||||
- dep 0.1.0
|
||||
|
||||
Each package has a name and version number. Here, there are two
|
||||
packages `simple` (with versoin `0.1.0` and `0.2.0`). The package
|
||||
`base 0.1.0` depends on the requirement `dep` (which simply means it
|
||||
depends on any version of `dep`. More generally, a package can also
|
||||
depend on a specific version of another package, or a range of versions.
|
||||
|
||||
Next, in our yaml file, we have the `cases:` section which is a list of
|
||||
test cases. Each test case has a request and a response. The request
|
||||
is what the user would want to do:
|
||||
|
||||
cases:
|
||||
-
|
||||
request:
|
||||
- install: simple
|
||||
- uninstall: simple
|
||||
response:
|
||||
- state:
|
||||
- simple 0.2.0
|
||||
- state: null
|
||||
|
||||
Here the first request is to install the package simple, this would
|
||||
basically be equivalent to typing `pip install simple`, and the corresponding
|
||||
first response is that the state of installed packages is `simple 0.2.0`.
|
||||
Note that by default the highest version of an available package will be
|
||||
installed.
|
||||
|
||||
The second request is to uninstall simple again, which will result in the
|
||||
state `null` (basically an empty list of installed packages).
|
||||
|
||||
When the yaml tests are run, each response is verified by checking which
|
||||
packages got actually installed. Note that this is check is done in
|
||||
alphabetical order.
|
||||
|
||||
|
||||
|
||||
The linter is very useful for initally checking `.yml` files, e.g.:
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ cases:
|
|||
- B 1.0.0
|
||||
- C 1.0.0
|
||||
- D 1.0.0
|
||||
skip: new
|
||||
-
|
||||
request:
|
||||
- install: B
|
||||
|
@ -26,7 +25,6 @@ cases:
|
|||
- B 1.0.0
|
||||
- C 1.0.0
|
||||
- D 1.0.0
|
||||
skip: new
|
||||
-
|
||||
request:
|
||||
- install: C
|
||||
|
@ -36,7 +34,6 @@ cases:
|
|||
- B 1.0.0
|
||||
- C 1.0.0
|
||||
- D 1.0.0
|
||||
skip: new
|
||||
-
|
||||
request:
|
||||
- install: D
|
||||
|
@ -46,4 +43,3 @@ cases:
|
|||
- B 1.0.0
|
||||
- C 1.0.0
|
||||
- D 1.0.0
|
||||
skip: new
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
base:
|
||||
available:
|
||||
- A 1.0.0; depends B == 1.0.0, B == 2.0.0
|
||||
- B 1.0.0
|
||||
- B 2.0.0
|
||||
|
||||
cases:
|
||||
-
|
||||
request:
|
||||
- install: A
|
||||
response:
|
||||
- error:
|
||||
code: 0
|
||||
stderr: ['requirement', 'is\s+incompatible']
|
||||
skip: old
|
||||
# -- currently the error message is:
|
||||
# a 1.0.0 has requirement B==1.0.0, but you'll have b 2.0.0 which is
|
||||
# incompatible.
|
||||
# -- better would be:
|
||||
# A 1.0.0 has incompatible requirements B==1.0.0, B==2.0.0
|
||||
|
||||
-
|
||||
request:
|
||||
- install: ['B==1.0.0', 'B']
|
||||
response:
|
||||
- state:
|
||||
- B 1.0.0
|
||||
skip: old
|
||||
# -- old error:
|
||||
# Double requirement given: B (already in B==1.0.0, name='B')
|
||||
|
||||
-
|
||||
request:
|
||||
- install: ['B==1.0.0', 'B==2.0.0']
|
||||
response:
|
||||
- state: null
|
||||
error:
|
||||
code: 1
|
||||
stderr: 'no\s+matching\s+distribution'
|
||||
skip: old
|
||||
# -- currently the (new resolver) error message is:
|
||||
# Could not find a version that satisfies the requirement B==1.0.0
|
||||
# Could not find a version that satisfies the requirement B==2.0.0
|
||||
# No matching distribution found for b, b
|
||||
# -- better would be:
|
||||
# cannot install different version (1.0.0, 2.0.0) of package B at the
|
||||
# same time.
|
||||
# -- the old error message was actually better here:
|
||||
# Double requirement given: B==2.0.0 (already in B==1.0.0, name='B')
|
||||
|
||||
-
|
||||
request:
|
||||
- install: B==1.5.0
|
||||
response:
|
||||
- state: null
|
||||
error:
|
||||
code: 1
|
||||
stderr: 'no\s+matching\s+distribution'
|
||||
skip: old
|
||||
# -- currently (new resolver) error message is:
|
||||
# Could not find a version that satisfies the requirement B==1.5.0
|
||||
# No matching distribution found for b
|
||||
# -- the old error message was actually better here:
|
||||
# Could not find a version that satisfies the requirement B==1.5.0 (from versions: 1.0.0, 2.0.0)
|
||||
# No matching distribution found for B==1.5.0
|
||||
|
||||
-
|
||||
request:
|
||||
- install: A==2.0
|
||||
response:
|
||||
- state: null
|
||||
error:
|
||||
code: 1
|
||||
stderr: 'no\s+matching\s+distribution'
|
||||
skip: old
|
||||
# -- currently the error message is:
|
||||
# Could not find a version that satisfies the requirement A==2.0
|
||||
# No matching distribution found for a
|
|
@ -0,0 +1,30 @@
|
|||
# Tzu-ping mentioned this example
|
||||
base:
|
||||
available:
|
||||
- name: virtualenv
|
||||
version: 20.0.2
|
||||
depends: ['six>=1.12.0,<2']
|
||||
- six 1.11
|
||||
- six 1.12
|
||||
- six 1.13
|
||||
|
||||
cases:
|
||||
-
|
||||
request:
|
||||
- install: virtualenv
|
||||
response:
|
||||
- state:
|
||||
- six 1.13
|
||||
- virtualenv 20.0.2
|
||||
-
|
||||
request:
|
||||
- install: ['six<1.12', 'virtualenv==20.0.2']
|
||||
response:
|
||||
- state: null
|
||||
error:
|
||||
stderr: 'no matching distribution found for six'
|
||||
skip: old
|
||||
# -- currently the error message is:
|
||||
# Could not find a version that satisfies the requirement six<1.12
|
||||
# Could not find a version that satisfies the requirement six<2,>=1.12.0 (from virtualenv)
|
||||
# No matching distribution found for six, six
|
|
@ -0,0 +1,22 @@
|
|||
base:
|
||||
available:
|
||||
- A 1.0.0; depends B == 1.0.0, C == 2.0.0
|
||||
- B 1.0.0; depends C == 1.0.0
|
||||
- C 1.0.0
|
||||
- C 2.0.0
|
||||
|
||||
cases:
|
||||
-
|
||||
request:
|
||||
- install: A
|
||||
response:
|
||||
- state: null
|
||||
skip: old
|
||||
# -- currently the error message is:
|
||||
# Could not find a version that satisfies the requirement C==2.0.0 (from a)
|
||||
# Could not find a version that satisfies the requirement C==1.0.0 (from b)
|
||||
# No matching distribution found for c, c
|
||||
# -- This is a bit confusing, as both versions of C are available.
|
||||
# -- better would be something like:
|
||||
# A 1.0.0 -> B 1.0.0 -> C 1.0.0
|
||||
# A 1.0.0 -> C 2.0.0
|
|
@ -9,9 +9,15 @@ cases:
|
|||
request:
|
||||
- install: A
|
||||
response:
|
||||
- conflicting:
|
||||
- required_by: [A 1.0.0, B 1.0.0]
|
||||
selector: D == 1.0.0
|
||||
- required_by: [A 1.0.0, C 1.0.0]
|
||||
selector: D == 2.0.0
|
||||
skip: true
|
||||
- error:
|
||||
code: 1
|
||||
stderr: 'no matching distribution found'
|
||||
skip: old
|
||||
# -- currently the error message is:
|
||||
# Could not find a version that satisfies the requirement D==2.0.0 (from c)
|
||||
# Could not find a version that satisfies the requirement D==1.0.0 (from b)
|
||||
# No matching distribution found for d, d
|
||||
# -- This is a bit confusing, as both versions of D are available.
|
||||
# -- better would be something like:
|
||||
# A 1.0.0 -> B 1.0.0 -> D 1.0.0
|
||||
# A 1.0.0 -> C 1.0.0 -> D 2.0.0
|
||||
|
|
|
@ -12,9 +12,9 @@ cases:
|
|||
- state:
|
||||
- A 1.0.0
|
||||
- C 1.0.0
|
||||
- conflicting:
|
||||
- required_by: [A 1.0.0]
|
||||
selector: C == 1.0.0
|
||||
- required_by: [B 1.0.0]
|
||||
selector: C == 2.0.0
|
||||
skip: true
|
||||
- error:
|
||||
code: 0
|
||||
stderr: ['requirement c==1\.0\.0', 'is incompatible']
|
||||
skip: old
|
||||
# -- currently the error message is:
|
||||
# a 1.0.0 has requirement C==1.0.0, but you'll have c 2.0.0 which is incompatible.
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
base:
|
||||
available:
|
||||
- A 1.0.0; depends B == 1.0.0, C == 1.0.0
|
||||
- A 0.8.0
|
||||
- B 1.0.0; depends D == 1.0.0
|
||||
- C 1.0.0; depends D == 2.0.0
|
||||
- D 1.0.0
|
||||
- D 2.0.0
|
||||
|
||||
cases:
|
||||
-
|
||||
request:
|
||||
- install: A
|
||||
response:
|
||||
- state:
|
||||
- A 0.8.0
|
||||
# the old resolver tries to install A 1.0.0 (which fails), but the new
|
||||
# resolver realises that A 1.0.0 cannot be installed and falls back to
|
||||
# installing the older version A 0.8.0 instead.
|
||||
skip: old
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,296 @@
|
|||
# The 129 available packages have been obtained by transforming a
|
||||
# conda repodata.json, and doing some manual fixes.
|
||||
base:
|
||||
available:
|
||||
- affine 2.2.0
|
||||
- affine 2.2.1
|
||||
- asn1crypto 0.22.0
|
||||
- asn1crypto 0.23.0
|
||||
- asn1crypto 0.24.0
|
||||
- backports 1.0
|
||||
- name: backports.functools_lru_cache
|
||||
version: '1.4'
|
||||
depends: ['backports', 'setuptools']
|
||||
- name: backports.functools_lru_cache
|
||||
version: '1.5'
|
||||
depends: ['backports', 'setuptools']
|
||||
- beautifulsoup4 4.6.0
|
||||
- beautifulsoup4 4.6.1
|
||||
- beautifulsoup4 4.6.3
|
||||
- name: cachecontrol
|
||||
version: 0.12.3
|
||||
depends: ['msgpack_python', 'requests']
|
||||
- name: cachecontrol
|
||||
version: 0.12.4
|
||||
depends: ['msgpack_python', 'requests']
|
||||
- name: cachecontrol
|
||||
version: 0.12.5
|
||||
depends: ['msgpack_python', 'requests']
|
||||
- certifi 2017.11.5
|
||||
- certifi 2017.7.27.1
|
||||
- certifi 2018.1.18
|
||||
- certifi 2018.4.16
|
||||
- certifi 2018.8.13
|
||||
# cffi is a bundled module in PyPy and causes resolution errors if pip
|
||||
# tries to installed it. Give it a different name since we are simply
|
||||
# checking the graph anyway and the identifier doesn't really matter.
|
||||
- name: cffi_not_really
|
||||
version: 1.10.0
|
||||
depends: ['pycparser']
|
||||
- name: cffi_not_really
|
||||
version: 1.11.2
|
||||
depends: ['pycparser']
|
||||
- name: cffi_not_really
|
||||
version: 1.11.4
|
||||
depends: ['pycparser']
|
||||
- name: cffi_not_really
|
||||
version: 1.11.5
|
||||
depends: ['pycparser']
|
||||
- chardet 3.0.4
|
||||
- click 6.7
|
||||
- colorama 0.3.9
|
||||
- colour 0.1.4
|
||||
- colour 0.1.5
|
||||
- contextlib2 0.5.5
|
||||
- name: cryptography
|
||||
version: 2.0.3
|
||||
depends: ['asn1crypto >=0.21.0', 'cffi_not_really >=1.7', 'idna >=2.1', 'six >=1.4.1']
|
||||
- name: cryptography
|
||||
version: 2.1.3
|
||||
depends: ['asn1crypto >=0.21.0', 'cffi_not_really >=1.7', 'idna >=2.1', 'six >=1.4.1']
|
||||
- name: cryptography
|
||||
version: 2.1.4
|
||||
depends: ['asn1crypto >=0.21.0', 'cffi_not_really >=1.7', 'idna >=2.1', 'six >=1.4.1']
|
||||
- name: cryptography
|
||||
version: 2.2.1
|
||||
depends: ['asn1crypto >=0.21.0', 'cffi_not_really >=1.7', 'idna >=2.1', 'six >=1.4.1']
|
||||
- name: cryptography
|
||||
version: '2.3'
|
||||
depends: ['asn1crypto >=0.21.0', 'cffi_not_really >=1.7', 'cryptography_vectors ~=2.3', 'idna >=2.1', 'six >=1.4.1']
|
||||
- cryptography_vectors 2.0.3
|
||||
- cryptography_vectors 2.1.3
|
||||
- cryptography_vectors 2.1.4
|
||||
- cryptography_vectors 2.2.1
|
||||
- cryptography_vectors 2.2.2
|
||||
- cryptography_vectors 2.3.0
|
||||
- name: cytoolz
|
||||
version: 0.8.2
|
||||
depends: ['toolz >=0.8.0']
|
||||
- name: cytoolz
|
||||
version: 0.9.0
|
||||
depends: ['toolz >=0.8.0']
|
||||
- name: cytoolz
|
||||
version: 0.9.0.1
|
||||
depends: ['toolz >=0.8.0']
|
||||
- distlib 0.2.5
|
||||
- distlib 0.2.6
|
||||
- distlib 0.2.7
|
||||
- enum34 1.1.6
|
||||
- filelock 2.0.12
|
||||
- filelock 2.0.13
|
||||
- filelock 3.0.4
|
||||
- future 0.16.0
|
||||
- futures 3.1.1
|
||||
- futures 3.2.0
|
||||
- glob2 0.5
|
||||
- glob2 0.6
|
||||
- name: html5lib
|
||||
version: '0.999999999'
|
||||
depends: ['six >=1.9', 'webencodings']
|
||||
- name: html5lib
|
||||
version: 1.0.1
|
||||
depends: ['six >=1.9', 'webencodings']
|
||||
- idna 2.6
|
||||
- idna 2.7
|
||||
- ipaddress 1.0.18
|
||||
- ipaddress 1.0.19
|
||||
- ipaddress 1.0.22
|
||||
- name: jinja2
|
||||
version: '2.10'
|
||||
depends: ['markupsafe >=0.23', 'setuptools']
|
||||
- name: jinja2
|
||||
version: 2.9.6
|
||||
depends: ['markupsafe >=0.23', 'setuptools']
|
||||
- lockfile 0.12.2
|
||||
- markupsafe 1.0
|
||||
- msgpack_python 0.4.8
|
||||
- msgpack_python 0.5.1
|
||||
- msgpack_python 0.5.5
|
||||
- msgpack_python 0.5.6
|
||||
- name: packaging
|
||||
version: '16.8'
|
||||
depends: ['pyparsing', 'six']
|
||||
- name: packaging
|
||||
version: '17.1'
|
||||
depends: ['pyparsing', 'six']
|
||||
- name: pip
|
||||
version: 10.0.1
|
||||
depends: ['setuptools', 'wheel']
|
||||
- name: pip
|
||||
version: 9.0.1
|
||||
depends: ['cachecontrol', 'colorama', 'distlib', 'html5lib', 'lockfile', 'packaging', 'progress', 'requests', 'setuptools', 'webencodings', 'wheel']
|
||||
- name: pip
|
||||
version: 9.0.3
|
||||
depends: ['setuptools', 'wheel']
|
||||
- pkginfo 1.4.1
|
||||
- pkginfo 1.4.2
|
||||
- progress 1.3
|
||||
- progress 1.4
|
||||
- psutil 5.2.2
|
||||
- psutil 5.3.1
|
||||
- psutil 5.4.0
|
||||
- psutil 5.4.1
|
||||
- psutil 5.4.3
|
||||
- psutil 5.4.5
|
||||
- psutil 5.4.6
|
||||
- pycosat 0.6.2
|
||||
- pycosat 0.6.3
|
||||
- pycparser 2.18
|
||||
- name: pyopenssl
|
||||
version: 17.2.0
|
||||
depends: ['cryptography >=1.9', 'six >=1.5.2']
|
||||
- name: pyopenssl
|
||||
version: 17.4.0
|
||||
depends: ['cryptography >=1.9', 'six >=1.5.2']
|
||||
- name: pyopenssl
|
||||
version: 17.5.0
|
||||
depends: ['cryptography >=2.1.4', 'six >=1.5.2']
|
||||
- name: pyopenssl
|
||||
version: 18.0.0
|
||||
depends: ['cryptography >=2.2.1', 'six >=1.5.2']
|
||||
- pyparsing 2.2.0
|
||||
- name: pysocks
|
||||
version: 1.6.7
|
||||
depends: ['win_inet_pton']
|
||||
- name: pysocks
|
||||
version: 1.6.8
|
||||
depends: ['win_inet_pton']
|
||||
- pywin32 221
|
||||
- pywin32 222
|
||||
- pywin32 223
|
||||
- pyyaml 3.12
|
||||
- pyyaml 3.13
|
||||
- name: requests
|
||||
version: 2.18.4
|
||||
depends: ['certifi >=2017.4.17', 'chardet >=3.0.2,<3.1.0', 'idna >=2.5,<2.7', 'urllib3 >=1.21.1,<1.23']
|
||||
- name: requests
|
||||
version: 2.19.1
|
||||
depends: ['certifi >=2017.4.17', 'chardet >=3.0.2,<3.1.0', 'idna >=2.5,<2.8', 'urllib3 >=1.21.1,<1.24']
|
||||
- scandir 1.5
|
||||
- scandir 1.6
|
||||
- scandir 1.7
|
||||
- scandir 1.8
|
||||
- scandir 1.9.0
|
||||
- name: setuptools
|
||||
version: 36.2.2
|
||||
depends: ['certifi', 'wincertstore']
|
||||
- name: setuptools
|
||||
version: 36.5.0
|
||||
depends: ['certifi', 'wincertstore']
|
||||
- name: setuptools
|
||||
version: 38.4.0
|
||||
depends: ['certifi >=2016.09', 'wincertstore >=0.2']
|
||||
- name: setuptools
|
||||
version: 38.5.1
|
||||
depends: ['certifi >=2016.09', 'wincertstore >=0.2']
|
||||
- name: setuptools
|
||||
version: 39.0.1
|
||||
depends: ['certifi >=2016.09', 'wincertstore >=0.2']
|
||||
- name: setuptools
|
||||
version: 39.1.0
|
||||
depends: ['certifi >=2016.09', 'wincertstore >=0.2']
|
||||
- name: setuptools
|
||||
version: 39.2.0
|
||||
depends: ['certifi >=2016.09', 'wincertstore >=0.2']
|
||||
- name: setuptools
|
||||
version: 40.0.0
|
||||
depends: ['certifi >=2016.09', 'wincertstore >=0.2']
|
||||
- six 1.8.2
|
||||
- six 1.10.0
|
||||
- six 1.11.0
|
||||
- toolz 0.8.2
|
||||
- toolz 0.9.0
|
||||
- name: urllib3
|
||||
version: '1.22'
|
||||
depends: ['certifi', 'cryptography >=1.3.4', 'idna >=2.0.0', 'pyopenssl >=0.14', 'pysocks >=1.5.6,<2.0,!=1.5.7']
|
||||
- name: urllib3
|
||||
version: '1.23'
|
||||
depends: ['certifi', 'cryptography >=1.3.4', 'idna >=2.0.0', 'pyopenssl >=0.14', 'pysocks >=1.5.6,<2.0,!=1.5.7']
|
||||
- webencodings 0.5.1
|
||||
- name: wheel
|
||||
version: 0.29.0
|
||||
depends: ['setuptools']
|
||||
- name: wheel
|
||||
version: 0.30.0
|
||||
depends: ['setuptools']
|
||||
- name: wheel
|
||||
version: 0.31.0
|
||||
depends: ['setuptools']
|
||||
- name: wheel
|
||||
version: 0.31.1
|
||||
depends: ['setuptools']
|
||||
- win_inet_pton 1.0.1
|
||||
- wincertstore 0.2
|
||||
|
||||
cases:
|
||||
-
|
||||
request:
|
||||
- install: affine
|
||||
response:
|
||||
- state:
|
||||
- affine 2.2.1
|
||||
-
|
||||
request:
|
||||
- install: cryptography
|
||||
response:
|
||||
- state:
|
||||
- asn1crypto 0.24.0
|
||||
- cffi_not_really 1.11.5
|
||||
- cryptography 2.3
|
||||
- cryptography_vectors 2.3.0
|
||||
- idna 2.7
|
||||
- pycparser 2.18
|
||||
- six 1.11.0
|
||||
skip: old
|
||||
-
|
||||
request:
|
||||
- install: cachecontrol
|
||||
response:
|
||||
- state:
|
||||
- asn1crypto 0.24.0
|
||||
- cachecontrol 0.12.5
|
||||
- certifi 2018.8.13
|
||||
- cffi_not_really 1.11.5
|
||||
- chardet 3.0.4
|
||||
- cryptography 2.3
|
||||
- cryptography_vectors 2.3.0
|
||||
- idna 2.7
|
||||
- msgpack_python 0.5.6
|
||||
- pycparser 2.18
|
||||
- pyopenssl 18.0.0
|
||||
- pysocks 1.6.8
|
||||
- requests 2.19.1
|
||||
- six 1.11.0
|
||||
- urllib3 1.23
|
||||
- win_inet_pton 1.0.1
|
||||
-
|
||||
request:
|
||||
- install: cytoolz
|
||||
response:
|
||||
- state:
|
||||
- cytoolz 0.9.0.1
|
||||
- toolz 0.9.0
|
||||
-
|
||||
request:
|
||||
- install: ['html5lib', 'six ==1.8.2']
|
||||
response:
|
||||
- state: null
|
||||
error:
|
||||
code: 1
|
||||
stderr: 'version that satisfies the requirement'
|
||||
skip: old
|
||||
# -- the new resolver tells:
|
||||
# Could not find a version that satisfies the requirement six==1.8.2
|
||||
# Could not find a version that satisfies the requirement six>=1.9 (from html5lib)
|
||||
# -- the old error message (which I think was better):
|
||||
# html5lib 1.0.1 has requirement six>=1.9, but you'll have six 1.8.2 which is incompatible.
|
|
@ -1,3 +1,4 @@
|
|||
import re
|
||||
import sys
|
||||
from pprint import pprint
|
||||
|
||||
|
@ -48,12 +49,27 @@ def lint_case(case, verbose=False):
|
|||
check_dict(package,
|
||||
required=['name', 'version'],
|
||||
optional=['depends', 'extras'])
|
||||
version = package['version']
|
||||
assert isinstance(version, str), repr(version)
|
||||
|
||||
for request, response in zip(requests, responses):
|
||||
check_dict(request, optional=['install', 'uninstall', 'options'])
|
||||
check_dict(response, optional=['state', 'conflicting'])
|
||||
assert len(response) == 1
|
||||
check_dict(response, optional=['state', 'error'])
|
||||
assert len(response) >= 1
|
||||
assert isinstance(response.get('state') or [], list)
|
||||
error = response.get('error')
|
||||
if error:
|
||||
check_dict(error, optional=['code', 'stderr'])
|
||||
stderr = error.get('stderr')
|
||||
if stderr:
|
||||
if isinstance(stderr, str):
|
||||
patters = [stderr]
|
||||
elif isinstance(stderr, list):
|
||||
patters = stderr
|
||||
else:
|
||||
raise "string or list expected, found %r" % stderr
|
||||
for patter in patters:
|
||||
re.compile(patter, re.I)
|
||||
|
||||
|
||||
def lint_yml(yml_file, verbose=False):
|
||||
|
|
Loading…
Reference in New Issue