| import inspect |
| import re |
| import textwrap |
| import functools |
| |
| import pytest |
| |
| import pkg_resources |
| |
| from .test_resources import Metadata |
| |
| |
| def strip_comments(s): |
| return '\n'.join( |
| l for l in s.split('\n') |
| if l.strip() and not l.strip().startswith('#') |
| ) |
| |
| |
| def parse_distributions(s): |
| ''' |
| Parse a series of distribution specs of the form: |
| {project_name}-{version} |
| [optional, indented requirements specification] |
| |
| Example: |
| |
| foo-0.2 |
| bar-1.0 |
| foo>=3.0 |
| [feature] |
| baz |
| |
| yield 2 distributions: |
| - project_name=foo, version=0.2 |
| - project_name=bar, version=1.0, |
| requires=['foo>=3.0', 'baz; extra=="feature"'] |
| ''' |
| s = s.strip() |
| for spec in re.split('\n(?=[^\s])', s): |
| if not spec: |
| continue |
| fields = spec.split('\n', 1) |
| assert 1 <= len(fields) <= 2 |
| name, version = fields.pop(0).split('-') |
| if fields: |
| requires = textwrap.dedent(fields.pop(0)) |
| metadata = Metadata(('requires.txt', requires)) |
| else: |
| metadata = None |
| dist = pkg_resources.Distribution(project_name=name, |
| version=version, |
| metadata=metadata) |
| yield dist |
| |
| |
| class FakeInstaller(object): |
| |
| def __init__(self, installable_dists): |
| self._installable_dists = installable_dists |
| |
| def __call__(self, req): |
| return next(iter(filter(lambda dist: dist in req, |
| self._installable_dists)), None) |
| |
| |
| def parametrize_test_working_set_resolve(*test_list): |
| idlist = [] |
| argvalues = [] |
| for test in test_list: |
| ( |
| name, |
| installed_dists, |
| installable_dists, |
| requirements, |
| expected1, expected2 |
| ) = [ |
| strip_comments(s.lstrip()) for s in |
| textwrap.dedent(test).lstrip().split('\n\n', 5) |
| ] |
| installed_dists = list(parse_distributions(installed_dists)) |
| installable_dists = list(parse_distributions(installable_dists)) |
| requirements = list(pkg_resources.parse_requirements(requirements)) |
| for id_, replace_conflicting, expected in ( |
| (name, False, expected1), |
| (name + '_replace_conflicting', True, expected2), |
| ): |
| idlist.append(id_) |
| expected = strip_comments(expected.strip()) |
| if re.match('\w+$', expected): |
| expected = getattr(pkg_resources, expected) |
| assert issubclass(expected, Exception) |
| else: |
| expected = list(parse_distributions(expected)) |
| argvalues.append(pytest.param(installed_dists, installable_dists, |
| requirements, replace_conflicting, |
| expected)) |
| return pytest.mark.parametrize('installed_dists,installable_dists,' |
| 'requirements,replace_conflicting,' |
| 'resolved_dists_or_exception', |
| argvalues, ids=idlist) |
| |
| |
| @parametrize_test_working_set_resolve( |
| ''' |
| # id |
| noop |
| |
| # installed |
| |
| # installable |
| |
| # wanted |
| |
| # resolved |
| |
| # resolved [replace conflicting] |
| ''', |
| |
| ''' |
| # id |
| already_installed |
| |
| # installed |
| foo-3.0 |
| |
| # installable |
| |
| # wanted |
| foo>=2.1,!=3.1,<4 |
| |
| # resolved |
| foo-3.0 |
| |
| # resolved [replace conflicting] |
| foo-3.0 |
| ''', |
| |
| ''' |
| # id |
| installable_not_installed |
| |
| # installed |
| |
| # installable |
| foo-3.0 |
| foo-4.0 |
| |
| # wanted |
| foo>=2.1,!=3.1,<4 |
| |
| # resolved |
| foo-3.0 |
| |
| # resolved [replace conflicting] |
| foo-3.0 |
| ''', |
| |
| ''' |
| # id |
| not_installable |
| |
| # installed |
| |
| # installable |
| |
| # wanted |
| foo>=2.1,!=3.1,<4 |
| |
| # resolved |
| DistributionNotFound |
| |
| # resolved [replace conflicting] |
| DistributionNotFound |
| ''', |
| |
| ''' |
| # id |
| no_matching_version |
| |
| # installed |
| |
| # installable |
| foo-3.1 |
| |
| # wanted |
| foo>=2.1,!=3.1,<4 |
| |
| # resolved |
| DistributionNotFound |
| |
| # resolved [replace conflicting] |
| DistributionNotFound |
| ''', |
| |
| ''' |
| # id |
| installable_with_installed_conflict |
| |
| # installed |
| foo-3.1 |
| |
| # installable |
| foo-3.5 |
| |
| # wanted |
| foo>=2.1,!=3.1,<4 |
| |
| # resolved |
| VersionConflict |
| |
| # resolved [replace conflicting] |
| foo-3.5 |
| ''', |
| |
| ''' |
| # id |
| not_installable_with_installed_conflict |
| |
| # installed |
| foo-3.1 |
| |
| # installable |
| |
| # wanted |
| foo>=2.1,!=3.1,<4 |
| |
| # resolved |
| VersionConflict |
| |
| # resolved [replace conflicting] |
| DistributionNotFound |
| ''', |
| |
| ''' |
| # id |
| installed_with_installed_require |
| |
| # installed |
| foo-3.9 |
| baz-0.1 |
| foo>=2.1,!=3.1,<4 |
| |
| # installable |
| |
| # wanted |
| baz |
| |
| # resolved |
| foo-3.9 |
| baz-0.1 |
| |
| # resolved [replace conflicting] |
| foo-3.9 |
| baz-0.1 |
| ''', |
| |
| ''' |
| # id |
| installed_with_conflicting_installed_require |
| |
| # installed |
| foo-5 |
| baz-0.1 |
| foo>=2.1,!=3.1,<4 |
| |
| # installable |
| |
| # wanted |
| baz |
| |
| # resolved |
| VersionConflict |
| |
| # resolved [replace conflicting] |
| DistributionNotFound |
| ''', |
| |
| ''' |
| # id |
| installed_with_installable_conflicting_require |
| |
| # installed |
| foo-5 |
| baz-0.1 |
| foo>=2.1,!=3.1,<4 |
| |
| # installable |
| foo-2.9 |
| |
| # wanted |
| baz |
| |
| # resolved |
| VersionConflict |
| |
| # resolved [replace conflicting] |
| baz-0.1 |
| foo-2.9 |
| ''', |
| |
| ''' |
| # id |
| installed_with_installable_require |
| |
| # installed |
| baz-0.1 |
| foo>=2.1,!=3.1,<4 |
| |
| # installable |
| foo-3.9 |
| |
| # wanted |
| baz |
| |
| # resolved |
| foo-3.9 |
| baz-0.1 |
| |
| # resolved [replace conflicting] |
| foo-3.9 |
| baz-0.1 |
| ''', |
| |
| ''' |
| # id |
| installable_with_installed_require |
| |
| # installed |
| foo-3.9 |
| |
| # installable |
| baz-0.1 |
| foo>=2.1,!=3.1,<4 |
| |
| # wanted |
| baz |
| |
| # resolved |
| foo-3.9 |
| baz-0.1 |
| |
| # resolved [replace conflicting] |
| foo-3.9 |
| baz-0.1 |
| ''', |
| |
| ''' |
| # id |
| installable_with_installable_require |
| |
| # installed |
| |
| # installable |
| foo-3.9 |
| baz-0.1 |
| foo>=2.1,!=3.1,<4 |
| |
| # wanted |
| baz |
| |
| # resolved |
| foo-3.9 |
| baz-0.1 |
| |
| # resolved [replace conflicting] |
| foo-3.9 |
| baz-0.1 |
| ''', |
| |
| ''' |
| # id |
| installable_with_conflicting_installable_require |
| |
| # installed |
| foo-5 |
| |
| # installable |
| foo-2.9 |
| baz-0.1 |
| foo>=2.1,!=3.1,<4 |
| |
| # wanted |
| baz |
| |
| # resolved |
| VersionConflict |
| |
| # resolved [replace conflicting] |
| baz-0.1 |
| foo-2.9 |
| ''', |
| |
| ''' |
| # id |
| conflicting_installables |
| |
| # installed |
| |
| # installable |
| foo-2.9 |
| foo-5.0 |
| |
| # wanted |
| foo>=2.1,!=3.1,<4 |
| foo>=4 |
| |
| # resolved |
| VersionConflict |
| |
| # resolved [replace conflicting] |
| VersionConflict |
| ''', |
| |
| ''' |
| # id |
| installables_with_conflicting_requires |
| |
| # installed |
| |
| # installable |
| foo-2.9 |
| dep==1.0 |
| baz-5.0 |
| dep==2.0 |
| dep-1.0 |
| dep-2.0 |
| |
| # wanted |
| foo |
| baz |
| |
| # resolved |
| VersionConflict |
| |
| # resolved [replace conflicting] |
| VersionConflict |
| ''', |
| |
| ''' |
| # id |
| installables_with_conflicting_nested_requires |
| |
| # installed |
| |
| # installable |
| foo-2.9 |
| dep1 |
| dep1-1.0 |
| subdep<1.0 |
| baz-5.0 |
| dep2 |
| dep2-1.0 |
| subdep>1.0 |
| subdep-0.9 |
| subdep-1.1 |
| |
| # wanted |
| foo |
| baz |
| |
| # resolved |
| VersionConflict |
| |
| # resolved [replace conflicting] |
| VersionConflict |
| ''', |
| ) |
| def test_working_set_resolve(installed_dists, installable_dists, requirements, |
| replace_conflicting, resolved_dists_or_exception): |
| ws = pkg_resources.WorkingSet([]) |
| list(map(ws.add, installed_dists)) |
| resolve_call = functools.partial( |
| ws.resolve, |
| requirements, installer=FakeInstaller(installable_dists), |
| replace_conflicting=replace_conflicting, |
| ) |
| if inspect.isclass(resolved_dists_or_exception): |
| with pytest.raises(resolved_dists_or_exception): |
| resolve_call() |
| else: |
| assert sorted(resolve_call()) == sorted(resolved_dists_or_exception) |