def test_merge_packages_simple() -> None:
    pkg = install_pinned.merge_packages(
        Package(
            'requests [security,tests] >= 2.8.1, == 2.8.* ; python_version >= "2.7"'
        ), Package('requests [security,foo] >= 2.7'))
    assert pkg == Package(
        'requests [security,foo,tests]>=2.7,>=2.8.1,==2.8.*; python_version >= "2.7"'
    )
def test_get_dependencies() -> None:
    reqs = install_pinned.get_dependencies(
        Requirement('dask[array] == 2021.2.0'))
    # Wrap into Package just to use its __eq__ and __hash__
    pkgs = set(Package(req) for req in reqs)
    assert pkgs == {
        Package('pyyaml'),
        Package('numpy>=1.15.1'),
        Package('toolz>=0.8.2')
    }
def test_merge_packages_url_and_specifier() -> None:
    pkg = install_pinned.merge_packages(Package('foo[bar] >= 2.0, < 3'),
                                        Package('foo @ https://invalid.com'))
    assert pkg == Package('foo[bar] @ https://invalid.com')

    with pytest.raises(
            ValueError,
            match='Cannot combine URL .* with exact specifier ==2.0'):
        install_pinned.merge_packages(Package('foo[bar] == 2.0'),
                                      Package('foo @ https://invalid.com'))
def test_resolve_fail() -> None:
    items: List[Union[Package, str]] = [
        Package('katsdptelstate == 0.10'),
        Package('redis == 3.5.3', constraint=True),
        Package('msgpack == 1.0.1', constraint=True),
        Package('six == 1.15.0', constraint=True),
    ]
    with pytest.raises(install_pinned.ResolutionError) as exc_info:
        install_pinned.resolve(items)
    assert set(exc_info.value.errors) == {
        'No version pinned for netifaces', 'No version pinned for numpy'
    }
def test_parse_requirements_nested(tmp_path) -> None:
    filename = tmp_path / 'base.txt'
    (tmp_path / 'requirements.txt').write_text('required>=1.0\n')
    (tmp_path / 'constraints.txt').write_text('constrained<=2.0\n')
    (tmp_path / 'defaults.txt').write_text('defaulted==1.5\n')
    filename.write_text(f'''
-d defaults.txt
-r requirements.txt
-c {tmp_path / "constraints.txt"}
''')
    reqs = install_pinned.parse_requirements(str(filename))
    assert set(reqs) == {
        Package('required>=1.0', constraint=False, weak=False),
        Package('constrained<=2.0', constraint=True, weak=False),
        Package('defaulted==1.5', constraint=True, weak=True)
    }
def test_parse_requirements_http(mocker) -> None:
    url1 = 'https://example.invalid/foo/requirements.txt'
    url2 = 'https://example.invalid/constraints.txt'
    content = {
        url1: b'-c ../constraints.txt\n'
        b'foo>=1.0\n',
        url2: b'bar==2.0\n'
    }

    def urlopen_replacement(url):
        return io.BytesIO(content[url])

    mocker.patch('urllib.request.urlopen', side_effect=urlopen_replacement)
    reqs = install_pinned.parse_requirements(url1)
    assert set(reqs) == {
        Package('foo>=1.0'),
        Package('bar==2.0', constraint=True)
    }
def test_merge_packages_override_default() -> None:
    pkg = install_pinned.merge_packages(Package('foo == 2.0', weak=True),
                                        Package('foo[test] == 2.1'))
    assert pkg == Package('foo[test] == 2.1')

    # Only exact versions override defaults
    pkg = install_pinned.merge_packages(Package('foo == 2.0', weak=True),
                                        Package('foo[test] >= 1.0'))
    assert pkg == Package('foo[test] >=1.0, ==2.0')
def test_parse_requirements_simple(tmp_path, constraint: bool,
                                   weak: bool) -> None:
    filename = tmp_path / 'requirements.txt'
    filename.write_text('''
# A comment
foo==0.1        # An end-of-line comment

--an-option
''')
    reqs = install_pinned.parse_requirements(str(filename),
                                             constraint=constraint,
                                             weak=weak)
    assert set(reqs) == {
        Package('foo==0.1', constraint=constraint, weak=weak), '--an-option'
    }
def test_merge_packages_constraint() -> None:
    req1 = Requirement('foo >= 2.8')
    req2 = Requirement('foo [test] < 3.0')
    pkg = install_pinned.merge_packages(Package(req1, constraint=True),
                                        Package(req2, constraint=True))
    assert pkg == Package('foo [test] >=2.8, <3.0', constraint=True)

    pkg = install_pinned.merge_packages(Package(req1, constraint=False),
                                        Package(req2, constraint=True))
    assert pkg == Package('foo [test] >=2.8, <3.0', constraint=False)
def test_merge_packages_markers() -> None:
    pkg = install_pinned.merge_packages(
        Package('foo >= 1.0 ; python_version >= "2.7"'),
        Package('foo < 1.0 ; python_version < "2.7"'))
    assert pkg == Package('foo >= 1.0 ; python_version >= "2.7"')

    # Check symmetry
    pkg = install_pinned.merge_packages(
        Package('foo < 1.0 ; python_version < "2.7"'),
        Package('foo >= 1.0 ; python_version >= "2.7"'))
    assert pkg == Package('foo >= 1.0 ; python_version >= "2.7"')
def test_parse_requirement_req() -> None:
    req = install_pinned.parse_requirement('requests[security]>=2.8.1')
    assert isinstance(req, install_pinned.Package)
    assert req == Package('requests[security]>=2.8.1',
                          constraint=False,
                          weak=False)
def test_resolve() -> None:
    items: List[Union[Package, str]] = [
        Package('katsdptelstate == 0.10'),
        Package('redis == 3.5.3', constraint=True),
        Package('msgpack == 1.0.1', constraint=True),
        Package('six == 1.15.0', constraint=True),
        Package('netifaces == 0.10.9', constraint=True),
        Package('numpy == 1.20.1', constraint=True),
        Package('pycuda == 2020.0',
                constraint=True),  # Not required so shouldn't be included
        Package('numpy == 1.17.1', constraint=True,
                weak=True),  # Should be overridden
        '--no-binary pycuda'  # Just to test options
    ]
    reqs = install_pinned.resolve(items)
    # Convert Requirement to Package just to use __eq__/__hash__
    pkgs = set(
        Package(req) if isinstance(req, Requirement) else req for req in reqs)
    assert pkgs == {
        Package('katsdptelstate == 0.10'),
        Package('redis == 3.5.3'),
        Package('msgpack == 1.0.1'),
        Package('six == 1.15.0'),
        Package('netifaces == 0.10.9'),
        Package('numpy == 1.20.1'), '--no-binary pycuda'
    }
def test_merge_packages_mixed_urls() -> None:
    with pytest.raises(ValueError, match='inconsistent URLs'):
        install_pinned.merge_packages(Package('foo @ http://url1'),
                                      Package('foo @ http://url2'))
def test_merge_packages_mixed_names() -> None:
    with pytest.raises(ValueError, match='different names'):
        install_pinned.merge_packages(Package('foo==0.1'), Package('bar==0.2'))
def test_requirement_eq() -> None:
    assert Package('foo[extra1,extra2] >=0.1, ==0.2') == Package(
        'foo[extra2,extra1] ==0.2, >=0.1')
    assert not (Package('foo >=0.1, ==0.2') != Package('foo ==0.2, >=0.1'))
    assert Package('foo >=0.1') != Package('foo ==0.1')
    assert Package('foo >=0.1') != Package('bar >=0.1')
    assert Package('foo [test]') != Package('foo [bar]')
    assert Package('foo@ http://example.invalid') != Package('foo')
    assert Package('foo; python_version < "2.7"') != Package(
        'foo; python_version > "2.7"')
    assert Package('foo', constraint=True) != Package('foo', constraint=False)
    assert Package('foo', weak=True) != Package('foo', weak=False)
def test_package_repr() -> None:
    pkg = Package(Requirement('foo==0.1'))
    assert repr(pkg) == "Package('foo==0.1', constraint=False, weak=False)"
    pkg = Package('foo==0.1', constraint=True, weak=True)
    assert repr(pkg) == "Package('foo==0.1', constraint=True, weak=True)"