test_pypi_repository.py (poetry-1.1.15) | : | test_pypi_repository.py (poetry-1.2.0) | ||
---|---|---|---|---|
from __future__ import annotations | ||||
import json | import json | |||
import shutil | import shutil | |||
from io import BytesIO | from io import BytesIO | |||
from pathlib import Path | ||||
from typing import TYPE_CHECKING | ||||
import pytest | import pytest | |||
from poetry.core.packages.dependency import Dependency | ||||
from poetry.core.semver.version import Version | ||||
from requests.exceptions import TooManyRedirects | from requests.exceptions import TooManyRedirects | |||
from requests.models import Response | from requests.models import Response | |||
from poetry.core.packages import Dependency | ||||
from poetry.factory import Factory | from poetry.factory import Factory | |||
from poetry.repositories.pypi_repository import PyPiRepository | from poetry.repositories.pypi_repository import PyPiRepository | |||
from poetry.utils._compat import PY35 | ||||
from poetry.utils._compat import Path | ||||
from poetry.utils._compat import encode | from poetry.utils._compat import encode | |||
class MockRepository(PyPiRepository): | if TYPE_CHECKING: | |||
from pytest_mock import MockerFixture | ||||
@pytest.fixture(autouse=True) | ||||
def _use_simple_keyring(with_simple_keyring: None) -> None: | ||||
pass | ||||
class MockRepository(PyPiRepository): | ||||
JSON_FIXTURES = Path(__file__).parent / "fixtures" / "pypi.org" / "json" | JSON_FIXTURES = Path(__file__).parent / "fixtures" / "pypi.org" / "json" | |||
DIST_FIXTURES = Path(__file__).parent / "fixtures" / "pypi.org" / "dists" | DIST_FIXTURES = Path(__file__).parent / "fixtures" / "pypi.org" / "dists" | |||
def __init__(self, fallback=False): | def __init__(self, fallback: bool = False) -> None: | |||
super(MockRepository, self).__init__( | super().__init__(url="http://foo.bar", disable_cache=True, fallback=fall | |||
url="http://foo.bar", disable_cache=True, fallback=fallback | back) | |||
) | ||||
def _get(self, url): | def _get(self, url: str) -> dict | None: | |||
parts = url.split("/")[1:] | parts = url.split("/")[1:] | |||
name = parts[0] | name = parts[0] | |||
if len(parts) == 3: | if len(parts) == 3: | |||
version = parts[1] | version = parts[1] | |||
else: | else: | |||
version = None | version = None | |||
if not version: | if not version: | |||
fixture = self.JSON_FIXTURES / (name + ".json") | fixture = self.JSON_FIXTURES / (name + ".json") | |||
else: | else: | |||
fixture = self.JSON_FIXTURES / name / (version + ".json") | fixture = self.JSON_FIXTURES / name / (version + ".json") | |||
if not fixture.exists(): | if not fixture.exists(): | |||
fixture = self.JSON_FIXTURES / (name + ".json") | fixture = self.JSON_FIXTURES / (name + ".json") | |||
if not fixture.exists(): | if not fixture.exists(): | |||
return | return None | |||
with fixture.open(encoding="utf-8") as f: | with fixture.open(encoding="utf-8") as f: | |||
return json.loads(f.read()) | return json.loads(f.read()) | |||
def _download(self, url, dest): | def _download(self, url: str, dest: Path) -> None: | |||
filename = url.split("/")[-1] | filename = url.split("/")[-1] | |||
fixture = self.DIST_FIXTURES / filename | fixture = self.DIST_FIXTURES / filename | |||
shutil.copyfile(str(fixture), dest) | shutil.copyfile(str(fixture), dest) | |||
def test_find_packages(): | def test_find_packages() -> None: | |||
repo = MockRepository() | repo = MockRepository() | |||
packages = repo.find_packages(Factory.create_dependency("requests", "^2.18") ) | packages = repo.find_packages(Factory.create_dependency("requests", "^2.18") ) | |||
assert len(packages) == 5 | assert len(packages) == 5 | |||
def test_find_packages_with_prereleases(): | def test_find_packages_with_prereleases() -> None: | |||
repo = MockRepository() | repo = MockRepository() | |||
packages = repo.find_packages(Factory.create_dependency("toga", ">=0.3.0.dev 2")) | packages = repo.find_packages(Factory.create_dependency("toga", ">=0.3.0.dev 2")) | |||
assert len(packages) == 7 | assert len(packages) == 7 | |||
def test_find_packages_does_not_select_prereleases_if_not_allowed(): | def test_find_packages_does_not_select_prereleases_if_not_allowed() -> None: | |||
repo = MockRepository() | repo = MockRepository() | |||
packages = repo.find_packages(Factory.create_dependency("pyyaml", "*")) | packages = repo.find_packages(Factory.create_dependency("pyyaml", "*")) | |||
assert len(packages) == 1 | assert len(packages) == 1 | |||
@pytest.mark.parametrize("constraint,count", [("*", 1), (">=1", 0), (">=19.0.0a0 | @pytest.mark.parametrize( | |||
", 1)]) | ["constraint", "count"], [("*", 1), (">=1", 0), (">=19.0.0a0", 1)] | |||
def test_find_packages_only_prereleases(constraint, count): | ) | |||
def test_find_packages_only_prereleases(constraint: str, count: int) -> None: | ||||
repo = MockRepository() | repo = MockRepository() | |||
packages = repo.find_packages(Factory.create_dependency("black", constraint) ) | packages = repo.find_packages(Factory.create_dependency("black", constraint) ) | |||
assert len(packages) == count | assert len(packages) == count | |||
def test_package(): | @pytest.mark.parametrize( | |||
["constraint", "expected"], | ||||
[ | ||||
# yanked 21.11b0 is ignored except for pinned version | ||||
("*", ["19.10b0"]), | ||||
(">=19.0a0", ["19.10b0"]), | ||||
(">=20.0a0", []), | ||||
(">=21.11b0", []), | ||||
("==21.11b0", ["21.11b0"]), | ||||
], | ||||
) | ||||
def test_find_packages_yanked(constraint: str, expected: list[str]) -> None: | ||||
repo = MockRepository() | repo = MockRepository() | |||
packages = repo.find_packages(Factory.create_dependency("black", constraint) ) | ||||
package = repo.package("requests", "2.18.4") | assert [str(p.version) for p in packages] == expected | |||
def test_package() -> None: | ||||
repo = MockRepository() | ||||
package = repo.package("requests", Version.parse("2.18.4")) | ||||
assert package.name == "requests" | assert package.name == "requests" | |||
assert len(package.requires) == 9 | assert len(package.requires) == 9 | |||
assert len([r for r in package.requires if r.is_optional()]) == 5 | assert len([r for r in package.requires if r.is_optional()]) == 5 | |||
assert len(package.extras["security"]) == 3 | assert len(package.extras["security"]) == 3 | |||
assert len(package.extras["socks"]) == 2 | assert len(package.extras["socks"]) == 2 | |||
assert package.files == [ | assert package.files == [ | |||
{ | { | |||
"file": "requests-2.18.4-py2.py3-none-any.whl", | "file": "requests-2.18.4-py2.py3-none-any.whl", | |||
"hash": "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f5 94193a40a38b", | "hash": "sha256:6a1b267aa90cac58ac3a765d067950e7dbbf75b1da07e895d1f5 94193a40a38b", # noqa: E501 | |||
}, | }, | |||
{ | { | |||
"file": "requests-2.18.4.tar.gz", | "file": "requests-2.18.4.tar.gz", | |||
"hash": "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132 edaa6dda237e", | "hash": "sha256:9c443e7324ba5b85070c4a818ade28bfabedf16ea10206da1132 edaa6dda237e", # noqa: E501 | |||
}, | }, | |||
] | ] | |||
win_inet = package.extras["socks"][0] | win_inet = package.extras["socks"][0] | |||
assert win_inet.name == "win-inet-pton" | assert win_inet.name == "win-inet-pton" | |||
assert win_inet.python_versions == "~2.7 || ~2.6" | assert win_inet.python_versions == "~2.7 || ~2.6" | |||
assert str(win_inet.marker) == ( | assert ( | |||
'sys_platform == "win32" and (python_version == "2.7" ' | str(win_inet.marker) | |||
'or python_version == "2.6") and extra == "socks"' | == 'sys_platform == "win32" and (python_version == "2.7"' | |||
' or python_version == "2.6") and extra == "socks"' | ||||
) | ) | |||
def test_fallback_on_downloading_packages(): | @pytest.mark.parametrize( | |||
"package_name, version, yanked, yanked_reason", | ||||
[ | ||||
("black", "19.10b0", False, ""), | ||||
("black", "21.11b0", True, "Broken regex dependency. Use 21.11b1 instead | ||||
."), | ||||
], | ||||
) | ||||
def test_package_yanked( | ||||
package_name: str, version: str, yanked: bool, yanked_reason: str | ||||
) -> None: | ||||
repo = MockRepository() | ||||
package = repo.package(package_name, Version.parse(version)) | ||||
assert package.name == package_name | ||||
assert str(package.version) == version | ||||
assert package.yanked is yanked | ||||
assert package.yanked_reason == yanked_reason | ||||
def test_package_not_canonicalized() -> None: | ||||
repo = MockRepository() | ||||
package = repo.package("discord.py", Version.parse("2.0.0")) | ||||
assert package.name == "discord-py" | ||||
assert package.pretty_name == "discord.py" | ||||
@pytest.mark.parametrize( | ||||
"package_name, version, yanked, yanked_reason", | ||||
[ | ||||
("black", "19.10b0", False, ""), | ||||
("black", "21.11b0", True, "Broken regex dependency. Use 21.11b1 instead | ||||
."), | ||||
], | ||||
) | ||||
def test_find_links_for_package_yanked( | ||||
package_name: str, version: str, yanked: bool, yanked_reason: str | ||||
) -> None: | ||||
repo = MockRepository() | ||||
package = repo.package(package_name, Version.parse(version)) | ||||
links = repo.find_links_for_package(package) | ||||
assert len(links) == 2 | ||||
for link in links: | ||||
assert link.yanked == yanked | ||||
assert link.yanked_reason == yanked_reason | ||||
def test_fallback_on_downloading_packages() -> None: | ||||
repo = MockRepository(fallback=True) | repo = MockRepository(fallback=True) | |||
package = repo.package("jupyter", "1.0.0") | package = repo.package("jupyter", Version.parse("1.0.0")) | |||
assert package.name == "jupyter" | assert package.name == "jupyter" | |||
assert len(package.requires) == 6 | assert len(package.requires) == 6 | |||
dependency_names = sorted([dep.name for dep in package.requires]) | dependency_names = sorted(dep.name for dep in package.requires) | |||
assert dependency_names == [ | assert dependency_names == [ | |||
"ipykernel", | "ipykernel", | |||
"ipywidgets", | "ipywidgets", | |||
"jupyter-console", | "jupyter-console", | |||
"nbconvert", | "nbconvert", | |||
"notebook", | "notebook", | |||
"qtconsole", | "qtconsole", | |||
] | ] | |||
def test_fallback_inspects_sdist_first_if_no_matching_wheels_can_be_found(): | def test_fallback_inspects_sdist_first_if_no_matching_wheels_can_be_found() -> N one: | |||
repo = MockRepository(fallback=True) | repo = MockRepository(fallback=True) | |||
package = repo.package("isort", "4.3.4") | package = repo.package("isort", Version.parse("4.3.4")) | |||
assert package.name == "isort" | assert package.name == "isort" | |||
assert len(package.requires) == 1 | assert len(package.requires) == 1 | |||
dep = package.requires[0] | dep = package.requires[0] | |||
assert dep.name == "futures" | assert dep.name == "futures" | |||
assert dep.python_versions == "~2.7" | assert dep.python_versions == "~2.7" | |||
@pytest.mark.skipif(not PY35, reason="AST parsing does not work for Python <3.4" | def test_fallback_can_read_setup_to_get_dependencies() -> None: | |||
) | ||||
def test_fallback_can_read_setup_to_get_dependencies(): | ||||
repo = MockRepository(fallback=True) | repo = MockRepository(fallback=True) | |||
package = repo.package("sqlalchemy", "1.2.12") | package = repo.package("sqlalchemy", Version.parse("1.2.12")) | |||
assert package.name == "sqlalchemy" | assert package.name == "sqlalchemy" | |||
assert len(package.requires) == 9 | assert len(package.requires) == 9 | |||
assert len([r for r in package.requires if r.is_optional()]) == 9 | assert len([r for r in package.requires if r.is_optional()]) == 9 | |||
assert package.extras == { | assert package.extras == { | |||
"mssql_pymssql": [Dependency("pymssql", "*")], | "mssql_pymssql": [Dependency("pymssql", "*")], | |||
"mssql_pyodbc": [Dependency("pyodbc", "*")], | "mssql_pyodbc": [Dependency("pyodbc", "*")], | |||
"mysql": [Dependency("mysqlclient", "*")], | "mysql": [Dependency("mysqlclient", "*")], | |||
"oracle": [Dependency("cx_oracle", "*")], | "oracle": [Dependency("cx_oracle", "*")], | |||
"postgresql": [Dependency("psycopg2", "*")], | "postgresql": [Dependency("psycopg2", "*")], | |||
"postgresql_pg8000": [Dependency("pg8000", "*")], | "postgresql_pg8000": [Dependency("pg8000", "*")], | |||
"postgresql_psycopg2binary": [Dependency("psycopg2-binary", "*")], | "postgresql_psycopg2binary": [Dependency("psycopg2-binary", "*")], | |||
"postgresql_psycopg2cffi": [Dependency("psycopg2cffi", "*")], | "postgresql_psycopg2cffi": [Dependency("psycopg2cffi", "*")], | |||
"pymysql": [Dependency("pymysql", "*")], | "pymysql": [Dependency("pymysql", "*")], | |||
} | } | |||
def test_pypi_repository_supports_reading_bz2_files(): | def test_pypi_repository_supports_reading_bz2_files() -> None: | |||
repo = MockRepository(fallback=True) | repo = MockRepository(fallback=True) | |||
package = repo.package("twisted", "18.9.0") | package = repo.package("twisted", Version.parse("18.9.0")) | |||
assert package.name == "twisted" | assert package.name == "twisted" | |||
assert 71 == len(package.requires) | assert len(package.requires) == 71 | |||
assert sorted( | assert sorted( | |||
[r for r in package.requires if not r.is_optional()], key=lambda r: r.na me | (r for r in package.requires if not r.is_optional()), key=lambda r: r.na me | |||
) == [ | ) == [ | |||
Dependency("attrs", ">=17.4.0"), | Dependency("attrs", ">=17.4.0"), | |||
Dependency("Automat", ">=0.3.0"), | Dependency("Automat", ">=0.3.0"), | |||
Dependency("constantly", ">=15.1"), | Dependency("constantly", ">=15.1"), | |||
Dependency("hyperlink", ">=17.1.1"), | Dependency("hyperlink", ">=17.1.1"), | |||
Dependency("incremental", ">=16.10.1"), | Dependency("incremental", ">=16.10.1"), | |||
Dependency("PyHamcrest", ">=1.9.0"), | Dependency("PyHamcrest", ">=1.9.0"), | |||
Dependency("zope.interface", ">=4.4.2"), | Dependency("zope.interface", ">=4.4.2"), | |||
] | ] | |||
skipping to change at line 197 | skipping to change at line 270 | |||
Dependency("idna", ">=0.6,!=2.3"), | Dependency("idna", ">=0.6,!=2.3"), | |||
Dependency("priority", ">=1.1.0,<2.0"), | Dependency("priority", ">=1.1.0,<2.0"), | |||
Dependency("pyasn1", "*"), | Dependency("pyasn1", "*"), | |||
Dependency("pyopenssl", ">=16.0.0"), | Dependency("pyopenssl", ">=16.0.0"), | |||
Dependency("pyserial", ">=3.0"), | Dependency("pyserial", ">=3.0"), | |||
Dependency("service_identity", "*"), | Dependency("service_identity", "*"), | |||
Dependency("soappy", "*"), | Dependency("soappy", "*"), | |||
] | ] | |||
} | } | |||
for name, deps in expected_extras.items(): | for name in expected_extras.keys(): | |||
assert expected_extras[name] == sorted( | assert ( | |||
package.extras[name], key=lambda r: r.name | sorted(package.extras[name], key=lambda r: r.name) == expected_extra | |||
s[name] | ||||
) | ) | |||
def test_invalid_versions_ignored(): | def test_invalid_versions_ignored() -> None: | |||
repo = MockRepository() | repo = MockRepository() | |||
# the json metadata for this package contains one malformed version | # the json metadata for this package contains one malformed version | |||
# and a correct one. | # and a correct one. | |||
packages = repo.find_packages(Factory.create_dependency("pygame-music-grid", "*")) | packages = repo.find_packages(Factory.create_dependency("pygame-music-grid", "*")) | |||
assert len(packages) == 1 | assert len(packages) == 1 | |||
def test_get_should_invalid_cache_on_too_many_redirects_error(mocker): | def test_get_should_invalid_cache_on_too_many_redirects_error( | |||
mocker: MockerFixture, | ||||
) -> None: | ||||
delete_cache = mocker.patch("cachecontrol.caches.file_cache.FileCache.delete ") | delete_cache = mocker.patch("cachecontrol.caches.file_cache.FileCache.delete ") | |||
response = Response() | response = Response() | |||
response.status_code = 200 | ||||
response.encoding = "utf-8" | response.encoding = "utf-8" | |||
response.raw = BytesIO(encode('{"foo": "bar"}')) | response.raw = BytesIO(encode('{"foo": "bar"}')) | |||
mocker.patch( | mocker.patch( | |||
"cachecontrol.adapter.CacheControlAdapter.send", | "poetry.utils.authenticator.Authenticator.get", | |||
side_effect=[TooManyRedirects(), response], | side_effect=[TooManyRedirects(), response], | |||
) | ) | |||
repository = PyPiRepository() | repository = PyPiRepository() | |||
repository._get("https://pypi.org/pypi/async-timeout/json") | repository._get("https://pypi.org/pypi/async-timeout/json") | |||
assert delete_cache.called | assert delete_cache.called | |||
def test_urls(): | def test_urls() -> None: | |||
repository = PyPiRepository() | repository = PyPiRepository() | |||
assert "https://pypi.org/simple/" == repository.url | assert repository.url == "https://pypi.org/simple/" | |||
assert "https://pypi.org/simple/" == repository.authenticated_url | assert repository.authenticated_url == "https://pypi.org/simple/" | |||
def test_use_pypi_pretty_name(): | def test_use_pypi_pretty_name() -> None: | |||
repo = MockRepository(fallback=True) | repo = MockRepository(fallback=True) | |||
package = repo.find_packages(Factory.create_dependency("twisted", "*")) | package = repo.find_packages(Factory.create_dependency("twisted", "*")) | |||
assert len(package) == 1 | assert len(package) == 1 | |||
assert package[0].pretty_name == "Twisted" | assert package[0].pretty_name == "Twisted" | |||
End of changes. 40 change blocks. | ||||
47 lines changed or deleted | 125 lines changed or added |