Commit ab087af1 by Sébastien Eustace Committed by Randy Döring

Add a wheel builder

parent 444fe078
......@@ -32,6 +32,31 @@ files = [
]
[[package]]
name = "build"
version = "0.10.0"
description = "A simple, correct Python build frontend"
category = "main"
optional = false
python-versions = ">= 3.7"
files = [
{file = "build-0.10.0-py3-none-any.whl", hash = "sha256:af266720050a66c893a6096a2f410989eeac74ff9a68ba194b3f6473e8e26171"},
{file = "build-0.10.0.tar.gz", hash = "sha256:d5b71264afdb5951d6704482aac78de887c80691c52b88a9ad195983ca2c9269"},
]
[package.dependencies]
colorama = {version = "*", markers = "os_name == \"nt\""}
importlib-metadata = {version = ">=0.22", markers = "python_version < \"3.8\""}
packaging = ">=19.0"
pyproject_hooks = "*"
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
[package.extras]
docs = ["furo (>=2021.08.31)", "sphinx (>=4.0,<5.0)", "sphinx-argparse-cli (>=1.5)", "sphinx-autodoc-typehints (>=1.10)"]
test = ["filelock (>=3)", "pytest (>=6.2.4)", "pytest-cov (>=2.12)", "pytest-mock (>=2)", "pytest-rerunfailures (>=9.1)", "pytest-xdist (>=1.34)", "setuptools (>=42.0.0)", "setuptools (>=56.0.0)", "toml (>=0.10.0)", "wheel (>=0.36.0)"]
typing = ["importlib-metadata (>=5.1)", "mypy (==0.991)", "tomli", "typing-extensions (>=3.7.4.3)"]
virtualenv = ["virtualenv (>=20.0.35)"]
[[package]]
name = "cachecontrol"
version = "0.12.11"
description = "httplib2 caching for requests"
......@@ -288,7 +313,7 @@ rapidfuzz = ">=2.2.0,<3.0.0"
name = "colorama"
version = "0.4.6"
description = "Cross-platform colored terminal text."
category = "dev"
category = "main"
optional = false
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7"
files = [
......@@ -1227,6 +1252,21 @@ files = [
]
[[package]]
name = "pyproject-hooks"
version = "1.0.0"
description = "Wrappers to call pyproject.toml-based build backend hooks."
category = "main"
optional = false
python-versions = ">=3.7"
files = [
{file = "pyproject_hooks-1.0.0-py3-none-any.whl", hash = "sha256:283c11acd6b928d2f6a7c73fa0d01cb2bdc5f07c57a2eeb6e83d5e56b97976f8"},
{file = "pyproject_hooks-1.0.0.tar.gz", hash = "sha256:f271b298b97f5955d53fb12b72c1fb1948c22c1a6b70b315c54cedaca0264ef5"},
]
[package.dependencies]
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
[[package]]
name = "pyrsistent"
version = "0.19.3"
description = "Persistent/Functional/Immutable data structures"
......@@ -1945,4 +1985,4 @@ testing = ["flake8 (<5)", "func-timeout", "jaraco.functools", "jaraco.itertools"
[metadata]
lock-version = "2.0"
python-versions = "^3.7"
content-hash = "47c828b086d203975e5d1e6deecc4821d51d09eb8dea20141f08fdd1cb280e03"
content-hash = "077b0abda1e0e5ffc7286738a63098606e3bd4650f7bb2569efe16cc106e055e"
......@@ -50,6 +50,7 @@ python = "^3.7"
poetry-core = "1.5.0"
poetry-plugin-export = "^1.3.0"
"backports.cached-property" = { version = "^1.0.2", python = "<3.8" }
build = "^0.10.0"
cachecontrol = { version = "^0.12.9", extras = ["filecache"] }
cleo = "^2.0.0"
crashtest = "^0.4.1"
......@@ -66,6 +67,7 @@ packaging = ">=20.4"
pexpect = "^4.7.0"
pkginfo = "^1.9.4"
platformdirs = "^2.5.2"
pyproject-hooks = "^1.0.0"
requests = "^2.18"
requests-toolbelt = ">=0.9.1,<0.11.0"
shellingham = "^1.5"
......@@ -167,22 +169,22 @@ enable_error_code = [
# warning.
[[tool.mypy.overrides]]
module = [
'poetry.console.commands.self.show.plugins',
'poetry.plugins.plugin_manager',
'poetry.repositories.installed_repository',
'poetry.utils.env',
'poetry.console.commands.self.show.plugins',
'poetry.plugins.plugin_manager',
'poetry.repositories.installed_repository',
'poetry.utils.env',
]
warn_unused_ignores = false
[[tool.mypy.overrides]]
module = [
'cachecontrol.*',
'lockfile.*',
'pexpect.*',
'requests_toolbelt.*',
'shellingham.*',
'virtualenv.*',
'xattr.*',
'cachecontrol.*',
'lockfile.*',
'pexpect.*',
'requests_toolbelt.*',
'shellingham.*',
'virtualenv.*',
'xattr.*',
]
ignore_missing_imports = true
......
......@@ -100,7 +100,6 @@ class PackageFilterPolicy:
logger = logging.getLogger(__name__)
_default_config: Config | None = None
......@@ -124,7 +123,11 @@ class Config:
"prefer-active-python": False,
"prompt": "{project_name}-py{python_version}",
},
"experimental": {"new-installer": True, "system-git-client": False},
"experimental": {
"new-installer": True,
"system-git-client": False,
"wheel-installer": True,
},
"installer": {"parallel": True, "max-workers": None, "no-binary": None},
}
......@@ -267,6 +270,7 @@ class Config:
"virtualenvs.options.prefer-active-python",
"experimental.new-installer",
"experimental.system-git-client",
"experimental.wheel-installer",
"installer.parallel",
}:
return boolean_normalizer
......
......@@ -2,28 +2,179 @@ from __future__ import annotations
import hashlib
import json
import tarfile
import tempfile
import zipfile
from contextlib import redirect_stdout
from io import StringIO
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Callable
from typing import Collection
from build import BuildBackendException
from build import ProjectBuilder
from build.env import IsolatedEnv as BaseIsolatedEnv
from poetry.core.utils.helpers import temporary_directory
from pyproject_hooks import quiet_subprocess_runner # type: ignore[import]
from poetry.installation.chooser import InvalidWheelName
from poetry.installation.chooser import Wheel
from poetry.utils.env import ephemeral_environment
if TYPE_CHECKING:
from contextlib import AbstractContextManager
from poetry.core.packages.utils.link import Link
from poetry.config.config import Config
from poetry.utils.env import Env
class ChefError(Exception):
...
class ChefBuildError(ChefError):
...
class IsolatedEnv(BaseIsolatedEnv):
def __init__(self, env: Env, config: Config) -> None:
self._env = env
self._config = config
@property
def executable(self) -> str:
return str(self._env.python)
@property
def scripts_dir(self) -> str:
return str(self._env._bin_dir)
def install(self, requirements: Collection[str]) -> None:
from cleo.io.null_io import NullIO
from poetry.core.packages.dependency import Dependency
from poetry.core.packages.project_package import ProjectPackage
from poetry.config.config import Config
from poetry.factory import Factory
from poetry.installation.installer import Installer
from poetry.packages.locker import Locker
from poetry.repositories.installed_repository import InstalledRepository
# We build Poetry dependencies from the requirements
package = ProjectPackage("__root__", "0.0.0")
package.python_versions = ".".join(str(v) for v in self._env.version_info[:3])
for requirement in requirements:
dependency = Dependency.create_from_pep_508(requirement)
package.add_dependency(dependency)
pool = Factory.create_pool(self._config)
installer = Installer(
NullIO(),
self._env,
package,
Locker(self._env.path.joinpath("poetry.lock"), {}),
pool,
Config.create(),
InstalledRepository.load(self._env),
)
installer.update(True)
installer.run()
class Chef:
def __init__(self, config: Config, env: Env) -> None:
self._config = config
self._env = env
self._cache_dir = (
Path(config.get("cache-dir")).expanduser().joinpath("artifacts")
)
def prepare(self, archive: Path, output_dir: Path | None = None) -> Path:
if not self._should_prepare(archive):
return archive
if archive.is_dir():
tmp_dir = tempfile.mkdtemp(prefix="poetry-chef-")
return self._prepare(archive, Path(tmp_dir))
return self._prepare_sdist(archive, destination=output_dir)
def _prepare(self, directory: Path, destination: Path) -> Path:
with ephemeral_environment(self._env.python) as venv:
env = IsolatedEnv(venv, self._config)
builder = ProjectBuilder(
directory,
python_executable=env.executable,
scripts_dir=env.scripts_dir,
runner=quiet_subprocess_runner,
)
env.install(builder.build_system_requires)
env.install(
builder.build_system_requires | builder.get_requires_for_build("wheel")
)
stdout = StringIO()
with redirect_stdout(stdout):
try:
return Path(
builder.build(
"wheel",
destination.as_posix(),
)
)
except BuildBackendException as e:
raise ChefBuildError(str(e))
def _prepare_sdist(self, archive: Path, destination: Path | None = None) -> Path:
from poetry.core.packages.utils.link import Link
suffix = archive.suffix
context: Callable[
[str], AbstractContextManager[zipfile.ZipFile | tarfile.TarFile]
]
if suffix == ".zip":
context = zipfile.ZipFile
else:
context = tarfile.open
with temporary_directory() as tmp_dir:
with context(archive.as_posix()) as archive_archive:
archive_archive.extractall(tmp_dir)
archive_dir = Path(tmp_dir)
elements = list(archive_dir.glob("*"))
if len(elements) == 1 and elements[0].is_dir():
sdist_dir = elements[0]
else:
sdist_dir = archive_dir / archive.name.rstrip(suffix)
if not sdist_dir.is_dir():
sdist_dir = archive_dir
if destination is None:
destination = self.get_cache_directory_for_link(Link(archive.as_uri()))
destination.mkdir(parents=True, exist_ok=True)
return self._prepare(
sdist_dir,
destination,
)
def _should_prepare(self, archive: Path) -> bool:
return archive.is_dir() or not self._is_wheel(archive)
@classmethod
def _is_wheel(cls, archive: Path) -> bool:
return archive.suffix == ".whl"
def get_cached_archive_for_link(self, link: Link) -> Path | None:
archives = self.get_cached_archives_for_link(link)
if not archives:
......
......@@ -16,13 +16,13 @@ from typing import Any
from cleo.io.null_io import NullIO
from poetry.core.packages.utils.link import Link
from poetry.core.pyproject.toml import PyProjectTOML
from poetry.installation.chef import Chef
from poetry.installation.chooser import Chooser
from poetry.installation.operations import Install
from poetry.installation.operations import Uninstall
from poetry.installation.operations import Update
from poetry.installation.wheel_installer import WheelInstaller
from poetry.utils._compat import decode
from poetry.utils.authenticator import Authenticator
from poetry.utils.env import EnvCommandError
......@@ -60,6 +60,8 @@ class Executor:
self._dry_run = False
self._enabled = True
self._verbose = False
self._wheel_installer = WheelInstaller(self._env)
self._use_wheel_installer = config.get("experimental.wheel-installer", True)
if parallel is None:
parallel = config.get("installer.parallel", True)
......@@ -471,18 +473,21 @@ class Executor:
message = f" <fg=blue;options=bold>•</> {op_msg}: <info>Removing...</info>"
self._write(operation, message)
return self._remove(operation)
return self._remove(operation.package)
def _install(self, operation: Install | Update) -> int:
package = operation.package
if package.source_type == "directory":
if not self._use_wheel_installer:
return self._install_directory_without_wheel_installer(operation)
return self._install_directory(operation)
if package.source_type == "git":
return self._install_git(operation)
if package.source_type == "file":
archive = self._prepare_file(operation)
archive = self._prepare_archive(operation)
elif package.source_type == "url":
assert package.source_url is not None
archive = self._download_link(operation, Link(package.source_url))
......@@ -495,14 +500,18 @@ class Executor:
" <info>Installing...</info>"
)
self._write(operation, message)
return self.pip_install(archive, upgrade=operation.job_type == "update")
if not self._use_wheel_installer:
return self.pip_install(archive, upgrade=operation.job_type == "update")
self._wheel_installer.install(archive)
return 0
def _update(self, operation: Install | Update) -> int:
return self._install(operation)
def _remove(self, operation: Uninstall) -> int:
package = operation.package
def _remove(self, package: Package) -> int:
# If we have a VCS package, remove its source directory
if package.source_type == "git":
src_dir = self._env.path / "src" / package.name
......@@ -517,7 +526,7 @@ class Executor:
raise
def _prepare_file(self, operation: Install | Update) -> Path:
def _prepare_archive(self, operation: Install | Update) -> Path:
package = operation.package
operation_message = self.get_operation_message(operation)
......@@ -532,9 +541,54 @@ class Executor:
if not Path(package.source_url).is_absolute() and package.root_dir:
archive = package.root_dir / archive
return archive
return self._chef.prepare(archive)
def _install_directory(self, operation: Install | Update) -> int:
package = operation.package
operation_message = self.get_operation_message(operation)
message = (
f" <fg=blue;options=bold>•</> {operation_message}:"
" <info>Building...</info>"
)
self._write(operation, message)
assert package.source_url is not None
if package.root_dir:
req = package.root_dir / package.source_url
else:
req = Path(package.source_url).resolve(strict=False)
if package.source_subdirectory:
req /= package.source_subdirectory
if package.develop:
# Editable installations are currently not supported
# for PEP-517 build systems so we defer to pip.
# TODO: Remove this workaround once either PEP-660 or PEP-662 is accepted
return self.pip_install(req, editable=True)
archive = self._prepare_archive(operation)
try:
if operation.job_type == "update":
# Uninstall first
# TODO: Make an uninstaller and find a way to rollback in case
# the new package can't be installed
assert isinstance(operation, Update)
self._remove(operation.initial_package)
self._wheel_installer.install(archive)
finally:
archive.unlink()
return 0
def _install_directory_without_wheel_installer(
self, operation: Install | Update
) -> int:
from poetry.core.pyproject.toml import PyProjectTOML
from poetry.factory import Factory
package = operation.package
......@@ -644,6 +698,7 @@ class Executor:
def _download_link(self, operation: Install | Update, link: Link) -> Path:
package = operation.package
output_dir = self._chef.get_cache_directory_for_link(link)
archive = self._chef.get_cached_archive_for_link(link)
if archive is None:
# No cached distributions was found, so we download and prepare it
......@@ -659,7 +714,16 @@ class Executor:
raise
if package.files:
if archive.suffix != ".whl":
message = (
f" <fg=blue;options=bold>•</> {self.get_operation_message(operation)}:"
" <info>Preparing...</info>"
)
self._write(operation, message)
archive = self._chef.prepare(archive, output_dir=output_dir)
if package.files and archive.name in {f["file"] for f in package.files}:
archive_hash = self._validate_archive_hash(archive, package)
self._hashes[package.name] = archive_hash
......
......@@ -10,10 +10,13 @@ from poetry.core.packages.package import Package
from poetry.__version__ import __version__
from poetry.factory import Factory
from poetry.installation.executor import Executor
from poetry.installation.wheel_installer import WheelInstaller
if TYPE_CHECKING:
from cleo.testers.command_tester import CommandTester
from pytest_mock import MockerFixture
from tests.helpers import TestRepository
from tests.types import CommandTesterFactory
......@@ -22,6 +25,19 @@ FIXTURES = Path(__file__).parent.joinpath("fixtures")
@pytest.fixture()
def setup(mocker: MockerFixture, fixture_dir: Path):
mocker.patch.object(
Executor,
"_download",
return_value=fixture_dir("distributions").joinpath(
"demo-0.1.2-py2.py3-none-any.whl"
),
)
mocker.patch.object(WheelInstaller, "install")
@pytest.fixture()
def tester(command_tester_factory: CommandTesterFactory) -> CommandTester:
return command_tester_factory("self update")
......
......@@ -787,6 +787,7 @@ def test_add_constraint_with_platform(
):
platform = sys.platform
env._platform = platform
env._marker_env = None
cachy2 = get_package("cachy", "0.2.0")
......@@ -1824,6 +1825,7 @@ def test_add_constraint_with_platform_old_installer(
):
platform = sys.platform
env._platform = platform
env._marker_env = None
cachy2 = get_package("cachy", "0.2.0")
......
......@@ -53,6 +53,7 @@ def test_list_displays_default_value_if_not_set(
expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false
experimental.wheel-installer = true
installer.max-workers = null
installer.no-binary = null
installer.parallel = true
......@@ -82,6 +83,7 @@ def test_list_displays_set_get_setting(
expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false
experimental.wheel-installer = true
installer.max-workers = null
installer.no-binary = null
installer.parallel = true
......@@ -135,6 +137,7 @@ def test_list_displays_set_get_local_setting(
expected = f"""cache-dir = {cache_dir}
experimental.new-installer = true
experimental.system-git-client = false
experimental.wheel-installer = true
installer.max-workers = null
installer.no-binary = null
installer.parallel = true
......
from __future__ import annotations
import os
import shutil
from distutils.command.build_ext import build_ext
from distutils.core import Distribution
from distutils.core import Extension
extensions = [Extension("extended.extended", ["extended/extended.c"])]
def build():
distribution = Distribution({"name": "extended", "ext_modules": extensions})
distribution.package_dir = "extended"
cmd = build_ext(distribution)
cmd.ensure_finalized()
cmd.run()
# Copy built extensions back to the project
for output in cmd.get_outputs():
relative_extension = os.path.relpath(output, cmd.build_lib)
shutil.copyfile(output, relative_extension)
mode = os.stat(relative_extension).st_mode
mode |= (mode & 0o444) >> 2
os.chmod(relative_extension, mode)
if __name__ == "__main__":
build()
#include <Python.h>
static PyObject *hello(PyObject *self) {
return PyUnicode_FromString("Hello");
}
static PyMethodDef module_methods[] = {
{
"hello",
(PyCFunction) hello,
NULL,
PyDoc_STR("Say hello.")
},
{NULL}
};
#if PY_MAJOR_VERSION >= 3
static struct PyModuleDef moduledef = {
PyModuleDef_HEAD_INIT,
"extended",
NULL,
-1,
module_methods,
NULL,
NULL,
NULL,
NULL,
};
#endif
PyMODINIT_FUNC
#if PY_MAJOR_VERSION >= 3
PyInit_extended(void)
#else
init_extended(void)
#endif
{
PyObject *module;
#if PY_MAJOR_VERSION >= 3
module = PyModule_Create(&moduledef);
#else
module = Py_InitModule3("extended", module_methods, NULL);
#endif
if (module == NULL)
#if PY_MAJOR_VERSION >= 3
return NULL;
#else
return;
#endif
#if PY_MAJOR_VERSION >= 3
return module;
#endif
}
[tool.poetry]
name = "extended"
version = "0.1"
description = "Some description."
authors = [
"Sébastien Eustace <sebastien@eustace.io>"
]
license = "MIT"
readme = "README.md"
homepage = "https://python-poetry.org/"
include = [
# C extensions must be included in the wheel distributions
{path = "extended/*.so", format = "wheel"},
{path = "extended/*.pyd", format = "wheel"},
]
[tool.poetry.build]
script = "build.py"
generate-setup-file = false
[build-system]
requires = ["poetry-core>=1.5.0"]
build-backend = "poetry.core.masonry.api"
......@@ -28,7 +28,7 @@ category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
files = [
{file = "demo-0.1.0.tar.gz", hash = "sha256:72e8531e49038c5f9c4a837b088bfcb8011f4a9f76335c8f0654df6ac539b3d6"}
{file = "demo-0.1.0.tar.gz", hash = "sha256:9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad"}
]
[package.source]
type = "url"
......
......@@ -8,8 +8,12 @@ import pytest
from packaging.tags import Tag
from poetry.core.packages.utils.link import Link
from poetry.factory import Factory
from poetry.installation.chef import Chef
from poetry.repositories import RepositoryPool
from poetry.utils.env import EnvManager
from poetry.utils.env import MockEnv
from tests.repositories.test_pypi_repository import MockRepository
if TYPE_CHECKING:
......@@ -18,6 +22,20 @@ if TYPE_CHECKING:
from tests.conftest import Config
@pytest.fixture()
def pool() -> RepositoryPool:
pool = RepositoryPool()
pool.add_repository(MockRepository())
return pool
@pytest.fixture(autouse=True)
def setup(mocker: MockerFixture, pool: RepositoryPool) -> None:
mocker.patch.object(Factory, "create_pool", return_value=pool)
@pytest.mark.parametrize(
("link", "cached"),
[
......@@ -82,7 +100,7 @@ def test_get_cached_archives_for_link(config: Config, mocker: MockerFixture):
)
assert archives
assert set(archives) == {Path(path) for path in distributions.glob("demo-0.1.0*")}
assert set(archives) == set(distributions.glob("demo-0.1.*"))
def test_get_cache_directory_for_link(config: Config, config_cache_dir: Path):
......@@ -103,3 +121,47 @@ def test_get_cache_directory_for_link(config: Config, config_cache_dir: Path):
)
assert directory == expected
def test_prepare_sdist(config: Config, config_cache_dir: Path) -> None:
chef = Chef(config, EnvManager.get_system_env())
archive = (
Path(__file__)
.parent.parent.joinpath("fixtures/distributions/demo-0.1.0.tar.gz")
.resolve()
)
destination = chef.get_cache_directory_for_link(Link(archive.as_uri()))
wheel = chef.prepare(archive)
assert wheel.parent == destination
assert wheel.name == "demo-0.1.0-py3-none-any.whl"
def test_prepare_directory(config: Config, config_cache_dir: Path):
chef = Chef(config, EnvManager.get_system_env())
archive = Path(__file__).parent.parent.joinpath("fixtures/simple_project").resolve()
wheel = chef.prepare(archive)
assert wheel.name == "simple_project-1.2.3-py2.py3-none-any.whl"
def test_prepare_directory_with_extensions(
config: Config, config_cache_dir: Path
) -> None:
env = EnvManager.get_system_env()
chef = Chef(config, env)
archive = (
Path(__file__)
.parent.parent.joinpath("fixtures/extended_with_no_setup")
.resolve()
)
wheel = chef.prepare(archive)
assert wheel.name == f"extended-0.1-{env.supported_tags[0]}.whl"
......@@ -8,6 +8,7 @@ import shutil
from pathlib import Path
from typing import TYPE_CHECKING
from typing import Any
from urllib.parse import urlparse
import pytest
......@@ -16,11 +17,13 @@ from cleo.io.buffered_io import BufferedIO
from poetry.core.packages.package import Package
from poetry.core.packages.utils.link import Link
from poetry.installation.chef import Chef as BaseChef
from poetry.installation.executor import Executor
from poetry.installation.operations import Install
from poetry.installation.operations import Uninstall
from poetry.installation.operations import Update
from poetry.repositories.repository_pool import RepositoryPool
from poetry.installation.wheel_installer import WheelInstaller
from poetry.repositories.pool import RepositoryPool
from poetry.utils.env import MockEnv
from tests.repositories.test_pypi_repository import MockRepository
......@@ -37,6 +40,29 @@ if TYPE_CHECKING:
from tests.types import FixtureDirGetter
class Chef(BaseChef):
_directory_wheel = None
_sdist_wheel = None
def set_directory_wheel(self, wheel: Path) -> None:
self._directory_wheel = wheel
def set_sdist_wheel(self, wheel: Path) -> None:
self._sdist_wheel = wheel
def _prepare_sdist(self, archive: Path, destination: Path | None = None) -> Path:
if self._sdist_wheel is not None:
return self._sdist_wheel
return super()._prepare_sdist(archive)
def _prepare(self, directory: Path, destination: Path) -> Path:
if self._directory_wheel is not None:
return self._directory_wheel
return super()._prepare(directory, destination)
@pytest.fixture
def env(tmp_dir: str) -> MockEnv:
path = Path(tmp_dir) / ".venv"
......@@ -85,12 +111,18 @@ def mock_file_downloads(http: type[httpretty.httpretty]) -> None:
def callback(
request: HTTPrettyRequest, uri: str, headers: dict[str, Any]
) -> list[int | dict[str, Any] | str]:
name = Path(urlparse(uri).path).name
fixture = Path(__file__).parent.parent.joinpath(
"fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl"
"repositories/fixtures/pypi.org/dists/" + name
)
with fixture.open("rb") as f:
return [200, headers, f.read()]
if not fixture.exists():
fixture = Path(__file__).parent.parent.joinpath(
"fixtures/distributions/demo-0.1.0-py2.py3-none-any.whl"
)
return [200, headers, fixture.read_bytes()]
http.register_uri(
http.GET,
......@@ -99,6 +131,20 @@ def mock_file_downloads(http: type[httpretty.httpretty]) -> None:
)
@pytest.fixture()
def wheel(tmp_dir: Path) -> Path:
shutil.copyfile(
Path(__file__)
.parent.parent.joinpath(
"fixtures/distributions/demo-0.1.2-py2.py3-none-any.whl"
)
.as_posix(),
Path(tmp_dir).joinpath("demo-0.1.2-py2.py3-none-any.whl").as_posix(),
)
return Path(tmp_dir).joinpath("demo-0.1.2-py2.py3-none-any.whl")
def test_execute_executes_a_batch_of_operations(
mocker: MockerFixture,
config: Config,
......@@ -107,12 +153,18 @@ def test_execute_executes_a_batch_of_operations(
tmp_dir: str,
mock_file_downloads: None,
env: MockEnv,
wheel: Path,
):
pip_install = mocker.patch("poetry.installation.executor.pip_install")
wheel_install = mocker.patch.object(WheelInstaller, "install")
config.merge({"cache-dir": tmp_dir})
chef = Chef(config, env)
chef.set_directory_wheel(wheel)
chef.set_sdist_wheel(wheel)
executor = Executor(env, pool, config, io)
executor._chef = chef
file_package = Package(
"demo",
......@@ -171,11 +223,10 @@ Package operations: 4 installs, 1 update, 1 removal
expected = set(expected.splitlines())
output = set(io.fetch_output().splitlines())
assert output == expected
assert len(env.executed) == 1
assert wheel_install.call_count == 4
# One pip uninstall and one pip editable install
assert len(env.executed) == 2
assert return_code == 0
assert pip_install.call_count == 5
assert pip_install.call_args.kwargs.get("upgrade", False)
assert pip_install.call_args.kwargs.get("editable", False)
@pytest.mark.parametrize(
......@@ -290,7 +341,6 @@ Package operations: 1 install, 0 updates, 0 removals
def test_execute_works_with_ansi_output(
mocker: MockerFixture,
config: Config,
pool: RepositoryPool,
io_decorated: BufferedIO,
......@@ -302,24 +352,19 @@ def test_execute_works_with_ansi_output(
executor = Executor(env, pool, config, io_decorated)
install_output = (
"some string that does not contain a keyb0ard !nterrupt or cance11ed by u$er"
)
mocker.patch.object(env, "_run", return_value=install_output)
return_code = executor.execute(
[
Install(Package("pytest", "3.5.1")),
Install(Package("cleo", "1.0.0a5")),
]
)
env._run.assert_called_once()
# fmt: off
expected = [
"\x1b[39;1mPackage operations\x1b[39;22m: \x1b[34m1\x1b[39m install, \x1b[34m0\x1b[39m updates, \x1b[34m0\x1b[39m removals", # noqa: E501
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m3.5.1\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mPending...\x1b[39m", # noqa: E501
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m3.5.1\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mDownloading...\x1b[39m", # noqa: E501
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m3.5.1\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mInstalling...\x1b[39m", # noqa: E501
"\x1b[32;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mpytest\x1b[39m\x1b[39m (\x1b[39m\x1b[32m3.5.1\x1b[39m\x1b[39m)\x1b[39m", # finished # noqa: E501
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mcleo\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m1.0.0a5\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mPending...\x1b[39m", # noqa: E501
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mcleo\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m1.0.0a5\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mDownloading...\x1b[39m", # noqa: E501
"\x1b[34;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mcleo\x1b[39m\x1b[39m (\x1b[39m\x1b[39;1m1.0.0a5\x1b[39;22m\x1b[39m)\x1b[39m: \x1b[34mInstalling...\x1b[39m", # noqa: E501
"\x1b[32;1m•\x1b[39;22m \x1b[39mInstalling \x1b[39m\x1b[36mcleo\x1b[39m\x1b[39m (\x1b[39m\x1b[32m1.0.0a5\x1b[39m\x1b[39m)\x1b[39m", # finished # noqa: E501
]
# fmt: on
......@@ -344,21 +389,16 @@ def test_execute_works_with_no_ansi_output(
executor = Executor(env, pool, config, io_not_decorated)
install_output = (
"some string that does not contain a keyb0ard !nterrupt or cance11ed by u$er"
)
mocker.patch.object(env, "_run", return_value=install_output)
return_code = executor.execute(
[
Install(Package("pytest", "3.5.1")),
Install(Package("cleo", "1.0.0a5")),
]
)
env._run.assert_called_once()
expected = """
Package operations: 1 install, 0 updates, 0 removals
• Installing pytest (3.5.1)
• Installing cleo (1.0.0a5)
"""
expected = set(expected.splitlines())
output = set(io_not_decorated.fetch_output().splitlines())
......@@ -540,14 +580,26 @@ def test_executor_should_write_pep610_url_references_for_files(
def test_executor_should_write_pep610_url_references_for_directories(
tmp_venv: VirtualEnv, pool: RepositoryPool, config: Config, io: BufferedIO
tmp_venv: VirtualEnv,
pool: RepositoryPool,
config: Config,
io: BufferedIO,
wheel: Path,
):
url = Path(__file__).parent.parent.joinpath("fixtures/simple_project").resolve()
url = (
Path(__file__)
.parent.parent.joinpath("fixtures/git/github.com/demo/demo")
.resolve()
)
package = Package(
"simple-project", "1.2.3", source_type="directory", source_url=url.as_posix()
"demo", "0.1.2", source_type="directory", source_url=url.as_posix()
)
chef = Chef(config, tmp_venv)
chef.set_directory_wheel(wheel)
executor = Executor(tmp_venv, pool, config, io)
executor._chef = chef
executor.execute([Install(package)])
verify_installed_distribution(
tmp_venv, package, {"dir_info": {}, "url": url.as_uri()}
......@@ -555,18 +607,30 @@ def test_executor_should_write_pep610_url_references_for_directories(
def test_executor_should_write_pep610_url_references_for_editable_directories(
tmp_venv: VirtualEnv, pool: RepositoryPool, config: Config, io: BufferedIO
tmp_venv: VirtualEnv,
pool: RepositoryPool,
config: Config,
io: BufferedIO,
wheel: Path,
):
url = Path(__file__).parent.parent.joinpath("fixtures/simple_project").resolve()
url = (
Path(__file__)
.parent.parent.joinpath("fixtures/git/github.com/demo/demo")
.resolve()
)
package = Package(
"simple-project",
"1.2.3",
"demo",
"0.1.2",
source_type="directory",
source_url=url.as_posix(),
develop=True,
)
chef = Chef(config, tmp_venv)
chef.set_directory_wheel(wheel)
executor = Executor(tmp_venv, pool, config, io)
executor._chef = chef
executor.execute([Install(package)])
verify_installed_distribution(
tmp_venv, package, {"dir_info": {"editable": True}, "url": url.as_uri()}
......@@ -600,6 +664,7 @@ def test_executor_should_write_pep610_url_references_for_git(
config: Config,
io: BufferedIO,
mock_file_downloads: None,
wheel: Path,
):
package = Package(
"demo",
......@@ -610,7 +675,11 @@ def test_executor_should_write_pep610_url_references_for_git(
source_url="https://github.com/demo/demo.git",
)
chef = Chef(config, tmp_venv)
chef.set_directory_wheel(wheel)
executor = Executor(tmp_venv, pool, config, io)
executor._chef = chef
executor.execute([Install(package)])
verify_installed_distribution(
tmp_venv,
......@@ -632,10 +701,11 @@ def test_executor_should_write_pep610_url_references_for_git_with_subdirectories
config: Config,
io: BufferedIO,
mock_file_downloads: None,
wheel: Path,
):
package = Package(
"two",
"2.0.0",
"demo",
"0.1.2",
source_type="git",
source_reference="master",
source_resolved_reference="123456",
......@@ -643,7 +713,11 @@ def test_executor_should_write_pep610_url_references_for_git_with_subdirectories
source_subdirectory="two",
)
chef = Chef(config, tmp_venv)
chef.set_directory_wheel(wheel)
executor = Executor(tmp_venv, pool, config, io)
executor._chef = chef
executor.execute([Install(package)])
verify_installed_distribution(
tmp_venv,
......@@ -723,7 +797,7 @@ def test_executor_should_be_initialized_with_correct_workers(
assert executor._max_workers == expected_workers
def test_executer_fallback_on_poetry_create_error(
def test_executor_fallback_on_poetry_create_error_without_wheel_installer(
mocker: MockerFixture,
config: Config,
pool: RepositoryPool,
......@@ -741,7 +815,7 @@ def test_executer_fallback_on_poetry_create_error(
"poetry.factory.Factory.create_poetry", side_effect=RuntimeError
)
config.merge({"cache-dir": tmp_dir})
config.merge({"cache-dir": tmp_dir, "experimental": {"wheel-installer": False}})
executor = Executor(env, pool, config, io)
......
{
"info": {
"author": "Sébastien Eustace",
"author_email": "sebastien@eustace.io",
"bugtrack_url": null,
"classifiers": [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9"
],
"description": "",
"description_content_type": "text/markdown",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://github.com/python-poetry/cleo",
"keywords": "cli,commands",
"license": "MIT",
"maintainer": "",
"maintainer_email": "",
"name": "cleo",
"package_url": "https://pypi.org/project/cleo/",
"platform": null,
"project_url": "https://pypi.org/project/cleo/",
"project_urls": {
"Homepage": "https://github.com/python-poetry/cleo"
},
"release_url": "https://pypi.org/project/cleo/1.0.0a5/",
"requires_dist": [
"crashtest (>=0.3.1,<0.4.0)",
"pylev (>=1.3.0,<2.0.0)"
],
"requires_python": ">=3.7,<4.0",
"summary": "Cleo allows you to create beautiful and testable command-line interfaces.",
"version": "1.0.0a5",
"yanked": false,
"yanked_reason": null
},
"last_serial": 14027784,
"urls": [
{
"comment_text": "",
"digests": {
"md5": "4c78006d13e68c0c0796b954b1df0a3f",
"sha256": "ff53056589300976e960f75afb792dfbfc9c78dcbb5a448e207a17b643826360"
},
"downloads": -1,
"filename": "cleo-1.0.0a5-py3-none-any.whl",
"has_sig": false,
"md5_digest": "4c78006d13e68c0c0796b954b1df0a3f",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7,<4.0",
"size": 78701,
"upload_time": "2022-06-03T20:16:19",
"upload_time_iso_8601": "2022-06-03T20:16:19.386916Z",
"url": "https://files.pythonhosted.org/packages/45/0c/3825603bf62f360829b1eea29a43dadce30829067e288170b3bf738aafd0/cleo-1.0.0a5-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "90e60b2ad117d3534f92a4ce37f9f462",
"sha256": "097c9d0e0332fd53cc89fc11eb0a6ba0309e6a3933c08f7b38558555486925d3"
},
"downloads": -1,
"filename": "cleo-1.0.0a5.tar.gz",
"has_sig": false,
"md5_digest": "90e60b2ad117d3534f92a4ce37f9f462",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7,<4.0",
"size": 61431,
"upload_time": "2022-06-03T20:16:21",
"upload_time_iso_8601": "2022-06-03T20:16:21.133890Z",
"url": "https://files.pythonhosted.org/packages/2f/16/1c1902b225756745f9860451a44a2e2a3c26ee91c72295e83c63df605ed1/cleo-1.0.0a5.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"vulnerabilities": []
}
{
"name": "importlib-metadata",
"files": [
{
"hashes": {
"md5": "8ae1f31228e29443c08e07501a99d1b8",
"sha256": "dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"
},
"filename": "importlib_metadata-1.7.0-py2.py3-none-any.whl",
"requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7",
"url": "https://files.pythonhosted.org/packages/8e/58/cdea07eb51fc2b906db0968a94700866fc46249bdc75cac23f9d13168929/importlib_metadata-1.7.0-py2.py3-none-any.whl"
},
{
"hashes": {
"md5": "4505ea85600cca1e693a4f8f5dd27ba8",
"sha256": "90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"
},
"filename": "importlib_metadata-1.7.0.tar.gz",
"requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7",
"url": "https://files.pythonhosted.org/packages/e2/ae/0b037584024c1557e537d25482c306cf6327b5a09b6c4b893579292c1c38/importlib_metadata-1.7.0.tar.gz"
}
],
"meta": {
"api-version": "1.0",
"_last-serial": 3879671
}
}
{
"info": {
"author": "Barry Warsaw",
"author_email": "barry@python.org",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 3",
"Topic :: Software Development :: Libraries"
],
"description": "",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "http://importlib-metadata.readthedocs.io/",
"keywords": "",
"license": "Apache Software License",
"maintainer": "",
"maintainer_email": "",
"name": "importlib-metadata",
"package_url": "https://pypi.org/project/importlib-metadata/",
"platform": "",
"project_url": "https://pypi.org/project/importlib-metadata/",
"project_urls": {
"Homepage": "http://importlib-metadata.readthedocs.io/"
},
"release_url": "https://pypi.org/project/importlib-metadata/1.7.0/",
"requires_dist": [
"zipp (>=0.5)",
"pathlib2 ; python_version < \"3\"",
"contextlib2 ; python_version < \"3\"",
"configparser (>=3.5) ; python_version < \"3\"",
"sphinx ; extra == 'docs'",
"rst.linker ; extra == 'docs'",
"packaging ; extra == 'testing'",
"pep517 ; extra == 'testing'",
"importlib-resources (>=1.3) ; (python_version < \"3.9\") and extra == 'testing'"
],
"requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7",
"summary": "Read metadata from Python packages",
"version": "1.7.0",
"yanked": false,
"yanked_reason": null
},
"last_serial": 10821863,
"releases": {
"1.7.0": [
{
"comment_text": "",
"digests": {
"md5": "8ae1f31228e29443c08e07501a99d1b8",
"sha256": "dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"
},
"downloads": -1,
"filename": "importlib_metadata-1.7.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "8ae1f31228e29443c08e07501a99d1b8",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7",
"size": 31809,
"upload_time": "2020-06-26T21:38:16",
"upload_time_iso_8601": "2020-06-26T21:38:16.079439Z",
"url": "https://files.pythonhosted.org/packages/8e/58/cdea07eb51fc2b906db0968a94700866fc46249bdc75cac23f9d13168929/importlib_metadata-1.7.0-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "4505ea85600cca1e693a4f8f5dd27ba8",
"sha256": "90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"
},
"downloads": -1,
"filename": "importlib_metadata-1.7.0.tar.gz",
"has_sig": false,
"md5_digest": "4505ea85600cca1e693a4f8f5dd27ba8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7",
"size": 29233,
"upload_time": "2020-06-26T21:38:17",
"upload_time_iso_8601": "2020-06-26T21:38:17.338581Z",
"url": "https://files.pythonhosted.org/packages/e2/ae/0b037584024c1557e537d25482c306cf6327b5a09b6c4b893579292c1c38/importlib_metadata-1.7.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "8ae1f31228e29443c08e07501a99d1b8",
"sha256": "dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"
},
"downloads": -1,
"filename": "importlib_metadata-1.7.0-py2.py3-none-any.whl",
"has_sig": false,
"md5_digest": "8ae1f31228e29443c08e07501a99d1b8",
"packagetype": "bdist_wheel",
"python_version": "py2.py3",
"requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7",
"size": 31809,
"upload_time": "2020-06-26T21:38:16",
"upload_time_iso_8601": "2020-06-26T21:38:16.079439Z",
"url": "https://files.pythonhosted.org/packages/8e/58/cdea07eb51fc2b906db0968a94700866fc46249bdc75cac23f9d13168929/importlib_metadata-1.7.0-py2.py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "4505ea85600cca1e693a4f8f5dd27ba8",
"sha256": "90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"
},
"downloads": -1,
"filename": "importlib_metadata-1.7.0.tar.gz",
"has_sig": false,
"md5_digest": "4505ea85600cca1e693a4f8f5dd27ba8",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7",
"size": 29233,
"upload_time": "2020-06-26T21:38:17",
"upload_time_iso_8601": "2020-06-26T21:38:17.338581Z",
"url": "https://files.pythonhosted.org/packages/e2/ae/0b037584024c1557e537d25482c306cf6327b5a09b6c4b893579292c1c38/importlib_metadata-1.7.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
]
}
{
"files": [
{
"filename": "poetry_core-1.5.0-py3-none-any.whl",
"hashes": {
"sha256": "e216b70f013c47b82a72540d34347632c5bfe59fd54f5fe5d51f6a68b19aaf84"
},
"requires-python": ">=3.7,<4.0",
"url": "https://files.pythonhosted.org/packages/2d/99/6b0c5fe90e247b2b7b96a27cdf39ee59a02aab3c01d7243fc0c63cd7fb73/poetry_core-1.5.0-py3-none-any.whl",
"yanked": false
},
{
"filename": "poetry_core-1.5.0.tar.gz",
"hashes": {
"sha256": "253521bb7104e1df81f64d7b49ea1825057c91fa156d7d0bd752fefdad6f8c7a"
},
"requires-python": ">=3.7,<4.0",
"url": "https://files.pythonhosted.org/packages/57/bb/2435fef60bb01f6c0891d9482c7053b50e90639f0f74d7658e99bdd4da69/poetry_core-1.5.0.tar.gz",
"yanked": false
}
],
"meta": {
"_last-serial": 16600250,
"api-version": "1.0"
},
"name": "poetry-core"
}
{
"info": {
"author": "Sébastien Eustace",
"author_email": "sebastien@eustace.io",
"bugtrack_url": null,
"classifiers": [
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Topic :: Software Development :: Build Tools",
"Topic :: Software Development :: Libraries :: Python Modules"
],
"description": "",
"description_content_type": "text/markdown",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://github.com/python-poetry/poetry-core",
"keywords": "packaging,dependency,poetry",
"license": "MIT",
"maintainer": "",
"maintainer_email": "",
"name": "poetry-core",
"package_url": "https://pypi.org/project/poetry-core/",
"platform": null,
"project_url": "https://pypi.org/project/poetry-core/",
"project_urls": {
"Bug Tracker": "https://github.com/python-poetry/poetry/issues",
"Homepage": "https://github.com/python-poetry/poetry-core",
"Repository": "https://github.com/python-poetry/poetry-core"
},
"release_url": "https://pypi.org/project/poetry-core/1.5.0/",
"requires_dist": [
"importlib-metadata (>=1.7.0) ; python_version < \"3.8\""
],
"requires_python": ">=3.7,<4.0",
"summary": "Poetry PEP 517 Build Backend",
"version": "1.5.0",
"yanked": false,
"yanked_reason": null
},
"last_serial": 16600250,
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "2d996b0c5fe90e247b2b7b96a27cdf39ee59a02aab3c01d7243fc0c63cd7fb73",
"md5": "be7589b4902793e66d7d979bd8581591",
"sha256": "e216b70f013c47b82a72540d34347632c5bfe59fd54f5fe5d51f6a68b19aaf84"
},
"downloads": -1,
"filename": "poetry_core-1.5.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "be7589b4902793e66d7d979bd8581591",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.7,<4.0",
"size": 464992,
"upload_time": "2023-01-28T10:52:52",
"upload_time_iso_8601": "2023-01-28T10:52:52.445537Z",
"url": "https://files.pythonhosted.org/packages/2d/99/6b0c5fe90e247b2b7b96a27cdf39ee59a02aab3c01d7243fc0c63cd7fb73/poetry_core-1.5.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "57bb2435fef60bb01f6c0891d9482c7053b50e90639f0f74d7658e99bdd4da69",
"md5": "481671a4895af7cdda4944eab67f3843",
"sha256": "253521bb7104e1df81f64d7b49ea1825057c91fa156d7d0bd752fefdad6f8c7a"
},
"downloads": -1,
"filename": "poetry_core-1.5.0.tar.gz",
"has_sig": false,
"md5_digest": "481671a4895af7cdda4944eab67f3843",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.7,<4.0",
"size": 448812,
"upload_time": "2023-01-28T10:52:53",
"upload_time_iso_8601": "2023-01-28T10:52:53.916268Z",
"url": "https://files.pythonhosted.org/packages/57/bb/2435fef60bb01f6c0891d9482c7053b50e90639f0f74d7658e99bdd4da69/poetry_core-1.5.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"vulnerabilities": []
}
{
"name": "zipp",
"files": [
{
"hashes": {
"md5": "0ec47fbf522751f6c5fa904cb33f1f59",
"sha256": "957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"
},
"filename": "zipp-3.5.0-py3-none-any.whl",
"requires_python": ">=3.6",
"url": "https://files.pythonhosted.org/packages/92/d9/89f433969fb8dc5b9cbdd4b4deb587720ec1aeb59a020cf15002b9593eef/zipp-3.5.0-py3-none-any.whl"
},
{
"hashes": {
"md5": "617efbf3edb707c57008ec00f408972f",
"sha256": "f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"
},
"filename": "zipp-3.5.0.tar.gz",
"requires_python": ">=3.6",
"url": "https://files.pythonhosted.org/packages/3a/9f/1d4b62cbe8d222539a84089eeab603d8e45ee1f897803a0ae0860400d6e7/zipp-3.5.0.tar.gz"
}
],
"meta": {
"api-version": "1.0",
"_last-serial": 3879671
}
}
{
"info": {
"author": "Jason R. Coombs",
"author_email": "jaraco@jaraco.com",
"bugtrack_url": null,
"classifiers": [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only"
],
"description": "",
"description_content_type": "",
"docs_url": null,
"download_url": "",
"downloads": {
"last_day": -1,
"last_month": -1,
"last_week": -1
},
"home_page": "https://github.com/jaraco/zipp",
"keywords": "",
"license": "",
"maintainer": "",
"maintainer_email": "",
"name": "zipp",
"package_url": "https://pypi.org/project/zipp/",
"platform": "",
"project_url": "https://pypi.org/project/zipp/",
"project_urls": {
"Homepage": "https://github.com/jaraco/zipp"
},
"release_url": "https://pypi.org/project/zipp/3.5.0/",
"requires_dist": [
"sphinx ; extra == 'docs'",
"jaraco.packaging (>=8.2) ; extra == 'docs'",
"rst.linker (>=1.9) ; extra == 'docs'",
"pytest (>=4.6) ; extra == 'testing'",
"pytest-checkdocs (>=2.4) ; extra == 'testing'",
"pytest-flake8 ; extra == 'testing'",
"pytest-cov ; extra == 'testing'",
"pytest-enabler (>=1.0.1) ; extra == 'testing'",
"jaraco.itertools ; extra == 'testing'",
"func-timeout ; extra == 'testing'",
"pytest-black (>=0.3.7) ; (platform_python_implementation != \"PyPy\" and python_version < \"3.10\") and extra == 'testing'",
"pytest-mypy ; (platform_python_implementation != \"PyPy\" and python_version < \"3.10\") and extra == 'testing'"
],
"requires_python": ">=3.6",
"summary": "Backport of pathlib-compatible object wrapper for zip files",
"version": "3.5.0",
"yanked": false,
"yanked_reason": null
},
"last_serial": 10811847,
"releases": {
"3.5.0": [
{
"comment_text": "",
"digests": {
"md5": "0ec47fbf522751f6c5fa904cb33f1f59",
"sha256": "957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"
},
"downloads": -1,
"filename": "zipp-3.5.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0ec47fbf522751f6c5fa904cb33f1f59",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 5700,
"upload_time": "2021-07-02T23:51:45",
"upload_time_iso_8601": "2021-07-02T23:51:45.759726Z",
"url": "https://files.pythonhosted.org/packages/92/d9/89f433969fb8dc5b9cbdd4b4deb587720ec1aeb59a020cf15002b9593eef/zipp-3.5.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "617efbf3edb707c57008ec00f408972f",
"sha256": "f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"
},
"downloads": -1,
"filename": "zipp-3.5.0.tar.gz",
"has_sig": false,
"md5_digest": "617efbf3edb707c57008ec00f408972f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 13270,
"upload_time": "2021-07-02T23:51:47",
"upload_time_iso_8601": "2021-07-02T23:51:47.004396Z",
"url": "https://files.pythonhosted.org/packages/3a/9f/1d4b62cbe8d222539a84089eeab603d8e45ee1f897803a0ae0860400d6e7/zipp-3.5.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
]
},
"urls": [
{
"comment_text": "",
"digests": {
"md5": "0ec47fbf522751f6c5fa904cb33f1f59",
"sha256": "957cfda87797e389580cb8b9e3870841ca991e2125350677b2ca83a0e99390a3"
},
"downloads": -1,
"filename": "zipp-3.5.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "0ec47fbf522751f6c5fa904cb33f1f59",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.6",
"size": 5700,
"upload_time": "2021-07-02T23:51:45",
"upload_time_iso_8601": "2021-07-02T23:51:45.759726Z",
"url": "https://files.pythonhosted.org/packages/92/d9/89f433969fb8dc5b9cbdd4b4deb587720ec1aeb59a020cf15002b9593eef/zipp-3.5.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"md5": "617efbf3edb707c57008ec00f408972f",
"sha256": "f5812b1e007e48cff63449a5e9f4e7ebea716b4111f9c4f9a645f91d579bf0c4"
},
"downloads": -1,
"filename": "zipp-3.5.0.tar.gz",
"has_sig": false,
"md5_digest": "617efbf3edb707c57008ec00f408972f",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.6",
"size": 13270,
"upload_time": "2021-07-02T23:51:47",
"upload_time_iso_8601": "2021-07-02T23:51:47.004396Z",
"url": "https://files.pythonhosted.org/packages/3a/9f/1d4b62cbe8d222539a84089eeab603d8e45ee1f897803a0ae0860400d6e7/zipp-3.5.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
]
}
......@@ -16,11 +16,14 @@ from requests.exceptions import TooManyRedirects
from requests.models import Response
from poetry.factory import Factory
from poetry.repositories.exceptions import PackageNotFound
from poetry.repositories.link_sources.json import SimpleJsonPage
from poetry.repositories.pypi_repository import PyPiRepository
from poetry.utils._compat import encode
if TYPE_CHECKING:
from packaging.utils import NormalizedName
from pytest_mock import MockerFixture
......@@ -36,6 +39,14 @@ class MockRepository(PyPiRepository):
def __init__(self, fallback: bool = False) -> None:
super().__init__(url="http://foo.bar", disable_cache=True, fallback=fallback)
def get_json_page(self, name: NormalizedName) -> SimpleJsonPage:
fixture = self.JSON_FIXTURES / (name + ".json")
if not fixture.exists():
raise PackageNotFound(f"Package [{name}] not found.")
return SimpleJsonPage("", json.loads(fixture.read_text()))
def _get(
self, url: str, headers: dict[str, str] | None = None
) -> dict[str, Any] | None:
......
......@@ -73,7 +73,7 @@ isort@ git+git://github.com/timothycrosley/isort.git@e63ae06ec7d70b06df9e5283576
def test_default_hash(fixture_dir: FixtureDirGetter) -> None:
root_dir = Path(__file__).parent.parent.parent
file_path = root_dir / fixture_dir("distributions/demo-0.1.0.tar.gz")
sha_256 = "72e8531e49038c5f9c4a837b088bfcb8011f4a9f76335c8f0654df6ac539b3d6"
sha_256 = "9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad"
assert get_file_hash(file_path) == sha_256
......@@ -88,40 +88,40 @@ except ImportError:
[
(hash_name, value)
for hash_name, value in [
("sha224", "972d02f36539a98599aed0566bc8aaf3e6701f4e895dd797d8f5248e"),
("sha224", "d26bd24163fe91c16b4b0162e773514beab77b76114d9faf6a31e350"),
(
"sha3_512",
"c04ee109ae52d6440445e24dbd6d244a1d0f0289ef79cb7ba9bc3c139c0237169af9a8f61cd1cf4fc17f853ddf84f97c475ac5bb6c91a4aff0b825b884d4896c", # noqa: E501
"196f4af9099185054ed72ca1d4c57707da5d724df0af7c3dfcc0fd018b0e0533908e790a291600c7d196fe4411b4f5f6db45213fe6e5cd5512bf18b2e9eff728", # noqa: E501
),
(
"blake2s",
"c336ecbc9d867c9d860accfba4c3723c51c4b5c47a1e0a955e1c8df499e36741",
"6dd9007d36c106defcf362cc637abeca41e8e93999928c8fcfaba515ed33bc93",
),
(
"sha3_384",
"d4abb2459941369aabf8880c5287b7eeb80678e14f13c71b9ecf64c772029dc3f93939590bea9ecdb51a1d1a74fefc5a", # noqa: E501
"787264d7885a0c305d2ee4daecfff435d11818399ef96cacef7e7c6bb638ce475f630d39fdd2800ca187dcd0071dc410", # noqa: E501
),
(
"blake2b",
"48e70abac547ab38e2330e6e6743a0c0f6274dcaa6df2c98135a78a9dd5b04a072d551fc3851b34da03eb0bf50dd71c7f32a8c36956e99fd6c66491bc7844800", # noqa: E501
"077a34e8252c8f6776bddd0d34f321cc52762cb4c11a1c7aa9b6168023f1722caf53c9f029074a6eb990a8de341d415dd986293bc2a2fccddad428be5605696e", # noqa: E501
),
(
"sha256",
"72e8531e49038c5f9c4a837b088bfcb8011f4a9f76335c8f0654df6ac539b3d6",
"9fa123ad707a5c6c944743bf3e11a0e80d86cb518d3cf25320866ca3ef43e2ad",
),
(
"sha512",
"e08a00a4b86358e49a318e7e3ba7a3d2fabdd17a2fef95559a0af681ea07ab1296b0b8e11e645297da296290661dc07ae3c8f74eab66bd18a80dce0c0ccb355b", # noqa: E501
"766ecf369b6bdf801f6f7bbfe23923cc9793d633a55619472cd3d5763f9154711fbf57c8b6ca74e4a82fa9bd8380af831e7b8668e68e362669fc60b1d81d79ad", # noqa: E501
),
(
"sha384",
"aa3144e28c6700a83247e8ec8711af5d3f5f75997990d48ec41e66bd275b3d0e19ee6f2fe525a358f874aa717afd06a9", # noqa: E501
"c638f32460f318035e4600284ba64fb531630740aebd33885946e527002d742787ff09eb65fd81bc34ce5ff5ef11cfe8", # noqa: E501
),
("sha3_224", "64bfc6e4125b4c6d67fd88ad1c7d1b5c4dc11a1970e433cd576c91d4"),
("sha1", "4c057579005ac3e68e951a11ffdc4b27c6ae16af"),
("sha3_224", "72980fc7bdf8c4d34268dc469442b09e1ccd2a8ff390954fc4d55a5a"),
("sha1", "91b585bd38f72d7ceedb07d03f94911b772fdc4c"),
(
"sha3_256",
"ba3d2a964b0680b6dc9565a03952e29c294c785d5a2307d3e2d785d73b75ed7e",
"7da5c08b416e6bcb339d6bedc0fe077c6e69af00607251ef4424c356ea061fcb",
),
]
if hash_name in algorithms_guaranteed
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment